{"id":17046975,"url":"https://github.com/maciejczyzewski/airtrash","last_synced_at":"2025-07-11T11:33:43.730Z","repository":{"id":36838666,"uuid":"230182061","full_name":"maciejczyzewski/airtrash","owner":"maciejczyzewski","description":"📡 100 tiny steps to build cross-platform desktop application using Electron/Node.js/C++","archived":false,"fork":false,"pushed_at":"2023-01-05T03:48:07.000Z","size":3528,"stargazers_count":94,"open_issues_count":12,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-12T15:57:38.711Z","etag":null,"topics":["electron","javascript","macos","p2p","utility"],"latest_commit_sha":null,"homepage":"","language":"C++","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/maciejczyzewski.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-12-26T02:37:43.000Z","updated_at":"2025-04-02T07:02:41.000Z","dependencies_parsed_at":"2023-01-17T05:45:27.810Z","dependency_job_id":null,"html_url":"https://github.com/maciejczyzewski/airtrash","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/maciejczyzewski/airtrash","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maciejczyzewski%2Fairtrash","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maciejczyzewski%2Fairtrash/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maciejczyzewski%2Fairtrash/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maciejczyzewski%2Fairtrash/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maciejczyzewski","download_url":"https://codeload.github.com/maciejczyzewski/airtrash/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maciejczyzewski%2Fairtrash/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264795498,"owners_count":23665233,"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":["electron","javascript","macos","p2p","utility"],"created_at":"2024-10-14T09:48:01.402Z","updated_at":"2025-07-11T11:33:43.698Z","avatar_url":"https://github.com/maciejczyzewski.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"English | [简体中文](#) | [हिन्दी](#)\n\n\u003cdiv align=\"center\"\u003e\n\t\u003cimg src=\"icon.png\" width=\"125\" height=\"125\"\u003e\n\t\u003ch1\u003eairtrash\u003c/h1\u003e\n\t\u003cp\u003e\n\t\t\u003cb\u003eClone of Apple's AirDrop - easy P2P file transfer powered by stupidity\u003c/b\u003e\n\t\u003c/p\u003e\n\t\u003cbr\u003e\n\u003c/div\u003e\n\n[![Build Status](https://travis-ci.org/maciejczyzewski/bottomline.png)](https://travis-ci.org/maciejczyzewski/bottomline) [![GitHub release](https://img.shields.io/github/release/maciejczyzewski/airtrash.svg)](https://github.com/maciejczyzewski/airtrash/releases)\n\n## 🔦 Screenshot\n\n\u003cimg src=\"screen.gif\" width=\"800\"\u003e\n\n\n## 🎯 Goal\n\n\u003e 100 tiny steps to build cross-platform desktop application using Electron/Node.js/C++\n\nIt's simple tutorial/guide for **absolute beginners** to present some tips for\ncreating desktop application. Unlike [@electron/electron-quick-start](https://github.com/electron/electron-quick-start), which presents the typical `hello world`.\nThis project aims to **focus on real-live scenario**, where we will try to implement\na complete product (**like cross-platform _Apple's AirDrop_ replacement**).\n\n## 💽 Installation\n\n**Download from [GitHub Releases](https://github.com/maciejczyzewski/airtrash/releases) and install it.**\n\n### from source\n\nTo clone and run this repository you'll need [Git](https://git-scm.com) and [Node.js](https://nodejs.org/en/download/) (and [yarn](https://github.com/yarnpkg/yarn)) installed on your computer. From your command line:\n\n```bash\n# clone this repository\ngit clone https://github.com/maciejczyzewski/airtrash\n# go into the repository\ncd airtrash\n# install dependencies\nyarn\n# run the app\nyarn start\n```\n\nNote: If you're using Linux Bash for Windows, [see this guide](https://www.howtogeek.com/261575/how-to-run-graphical-linux-desktop-applications-from-windows-10s-bash-shell/) or use `node` from the command prompt.\n\n### macOS\n\nThe macOS users can install _airtrash_ using `brew cask`.\n\n```bash\nbrew update \u0026\u0026 brew cask install airtrash\n```\n\n(nice try, you can't)\n\n## 📃 Tutorial\n\nLet's begin our journey.\n\n### 1: starting from template\n\nClone and run for a quick way to see Electron in action. From your command line:\n\n\u003e `yarn` is strongly recommended instead of `npm`.\n\n```bash\n# clone this repository\n$ git clone https://github.com/electron/electron-quick-start\n# go into the repository\n$ cd electron-quick-start\n# install dependencies\n$ yarn\n# run the app\n$ yarn start\n```\n\nYou should see:\n\n![](screen-1.png)\n\nAnd have this file structure:\n\n```bash\n.\n├── LICENSE.md        # - no one's bothered\n├── README.md         # - sometimes good to read\n├── index.html        # body: what you see\n├── main.js           # heart: electron window\n├── package-lock.json # - auto-generated\n├── package.json      # configuration/package manager\n├── preload.js        # soul: application behavior\n└── renderer.js       # - do after rendering\n\n0 directories, 8 files\n```\n\n### 2: using [@electron-userland/electron-builder](https://github.com/electron-userland/electron-builder) for packing things\n\nOur next goal will be to build `.dmg` and `.app` files with everything packed\nup.\n\n1. Run: `$ yarn add electron-builder --dev`\n\n2. Modify _package.json_:\n```diff\n+   \"name\": \"airtrash\",\n    \"scripts\": {\n        \"start\": \"electron .\",\n+       \"pack\": \"electron-builder --dir\",\n+       \"dist\": \"electron-builder\",\n+       \"postinstall\": \"electron-builder install-app-deps\"\n    },\n    ...\n+    \"build\": {\n+        \"appId\": \"maciejczyzewski.airtrash\",\n+        \"mac\": {\n+            \"category\": \"public.app-category.utilities\"\n+        }\n+    },\n```\n\n3. Run: `yarn dist`\n\nYou should see:\n\n```\n$ electron-builder\n  • electron-builder  version=21.2.0 os=17.7.0\n  • loaded configuration  file=package.json (\"build\" field)\n  • writing effective config  file=dist/builder-effective-config.yaml\n  • packaging       platform=darwin arch=x64 electron=7.1.7 appOutDir=dist/mac\n  • default Electron icon is used  reason=application icon is not set\n  • building        target=macOS zip arch=x64 file=dist/airtrash-1.0.0-mac.zip\n  • building        target=DMG arch=x64 file=dist/airtrash-1.0.0.dmg\n  • building block map  blockMapFile=dist/airtrash-1.0.0.dmg.blockmap\n  • building embedded block map  file=dist/airtrash-1.0.0-mac.zip\n✨  Done in 59.42s.\n```\n\nAnd have this additional files:\n\n![](screen-2.png)\n\n### 3: adding [@twbs/bootstrap](https://github.com/twbs/bootstrap) to project\n\nLet's add some popular package (like _bootstrap_) to understand how to do it.\n\n1. Run:\n```bash\n$ yarn add bootstrap --dev\n$ yarn add normalize.css --dev # good practise\n$ yarn add popper.js --dev # bootstrap needs this\n$ yarn add jquery --dev # and this to be complete\n```\n\n2. Enable `nodeIntegration` in _main.js_:\n```diff\n    webPreferences : {\n+      nodeIntegration : true,\n       preload : path.join(__dirname, 'app/preload.js'),\n    }\n```\n\n3. Modify _index.html_:\n```diff\n\u003chead\u003e\n  \u003chead\u003e\n-    \u003cmeta http-equiv=\"Content-Security-Policy\" content=\"default-src 'self'; script-src 'self'\"\u003e\n-    \u003cmeta http-equiv=\"X-Content-Security-Policy\" content=\"default-src 'self'; script-src 'self'\"\u003e\n+    \u003clink rel=\"stylesheet\" href=\"node_modules/normalize.css/normalize.css\" /\u003e\n+    \u003clink\n+      rel=\"stylesheet\"\n+      href=\"node_modules/bootstrap/dist/css/bootstrap.min.css\"\n+    /\u003e\n  ...\n  \u003c/head\u003e\n  \u003cbody\u003e\n  ...\n+    \u003cscript\u003e\n+      window.$ = window.jquery = require('jquery');\n+      window.popper = require('popper.js');\n+      require('bootstrap');\n+    \u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n### 4: customizing window/interface\n\nIn Electron you can modify the window interface. Let's play with it.\n\n1. Change defaults (adding icon) in _main.js_:\n\n```diff\n  mainWindow = new BrowserWindow({\n+   titleBarStyle: 'hiddenInset',\n+   width : 625,\n+   height : 400,\n+   // resizable: false, # user's don't like this option\n    webPreferences : {\n      nodeIntegration : true,\n      preload : path.join(__dirname, 'app/preload.js'),\n+     icon : __dirname + '/icon.png'\n    }\n  })\n```\n\n2. Because of `titleBarStyle: 'hiddenInset'`, it need to be defined new\n   _draggable_ element in window. It can be achieved by adding to _index.html_:\n```diff\n+ \u003cbody style=\"-webkit-app-region: drag\"\u003e\n```\n\nResult should be:\n\n![](screen-3.png)\n![](screen-4.png)\n\n### 5: adding native extension ([@nodejs/nan](https://github.com/nodejs/nan) C++ library)\n\nRefer to a [quick-start **Nan** Boilerplate](https://github.com/fcanas/node-native-boilerplate) for a ready-to-go project that utilizes basic Nan functionality (Node Native Extension).\n\n1. Run:\n```bash\n$ yarn add node-gyp --dev\n$ yarn add electron-rebuild --dev # to fix some common problems\n```\n\n2. Modify:\n```diff\n    \"scripts\": {\n        \"start\": \"electron .\",\n        \"pack\": \"electron-builder --dir\",\n        \"dist\": \"electron-builder\",\n+       \"build\": \"node-gyp build\",\n+       \"configure\": \"node-gyp configure\",\n+       \"postinstall\": \"electron-builder install-app-deps \u0026\u0026 \\\n+                       ./node_modules/.bin/electron-rebuild\"\n    },\n    ...\n    \"build\": {\n+       \"files\": [\n+           \"**/*\",\n+           \"build/Release/*\"\n+       ],\n+       \"nodeGypRebuild\": true,\n+       \"asarUnpack\": \"build/Release/*\",\n        \"appId\": \"maciejczyzewski.airtrash\",\n        \"mac\": {\n            \"icon\": \"icon.png\",\n            \"category\": \"public.app-category.utilities\"\n        }\n    },\n```\n\n3. Add _binding.gyp_:\n```diff\n{\n  \"targets\": [\n    {\n      \"target_name\": \"airtrash\",\n      \"sources\": [\n        \"airtrash.cc\",\n        \"src/api.cc\",\n      ],\n      \"include_dirs\" : [\n        \"\u003c!(node -e \\\"require('nan')\\\")\"\n      ]\n    }\n  ],\n}\n```\n\n4. Add these files:\n_airtrash.cc_:\n```c++\n#include \"src/api.h\"\n\nusing v8::FunctionTemplate;\n\n#define NAN_REGISTER(name) \\\n  Nan::Set(target, Nan::New(#name).ToLocalChecked(), \\\n           Nan::GetFunction(Nan::New\u003cFunctionTemplate\u003e(name)).ToLocalChecked());\n\n\nNAN_MODULE_INIT(InitAll) {\n  NAN_REGISTER(return_a_string);\n}\n\nNODE_MODULE(airtrash, InitAll)\n```\n\n_src/api.cc_:\n```c++\n#include \"api.h\"\n\nvoid return_a_string(const Nan::FunctionCallbackInfo\u003cv8::Value\u003e \u0026args) {\n  std::string val_example = \"haha, just a string ;-)\"\n  args.GetReturnValue().Set(\n      Nan::New\u003cv8::String\u003e(val_example).ToLocalChecked());\n}\n```\n\n_src/api.h_:\n```c++\n#ifndef NATIVE_EXTENSION_GRAB_H\n#define NATIVE_EXTENSION_GRAB_H\n\n#include \u003cnan.h\u003e\n\nNAN_METHOD(return_a_string);\n\n#endif\n```\n\n5. Test in _main.js_:\n```js\nvar NativeExtension = require(\"bindings\")(\"airtrash\");\nconsole.log(NativeExtension.return_a_string());\n// =\u003e haha, just a string ;-)\n```\n\n### 6: we don't have threads, how to handle this\n\nRecommended reading: [Node.js multithreading: What are Worker Threads and why do they matter?](https://blog.logrocket.com/node-js-multithreading-what-are-worker-threads-and-why-do-they-matter-48ab102f8b10/)\n\n1. Run: `$ yarn add worker-farm --dev`\n\n2. Create file _app/push.js_:\n```js\nvar NativeExtension = require('bindings')('airtrash');\n\nmodule.exports = (input, callback) =\u003e {\n  console.log(\"PUSH\", input.address, input.path)\n  NativeExtension.push(input.address, input.path)\n  callback(null, input)\n}\n```\n\n3. Then when we run this:\n```js\nconst workerFarm = require(\"worker-farm\");\nconst service_push = workerFarm(require.resolve(\"./push\"));\nservice_push(data,\n               function(err, output) {\n                 new Notification(\n                     \"Transmission Closed!\",\n                     {body : output.path + \" from \" + output.address})\n               });\nconsole.log(\"hello!\");\n```\n\nResult should be (random order of lines):\n```\nhello!\nPUSH 192.168.0.?:9000\n\u003ckilling service signal\u003e\nTransmission Closed!\n```\n\n### 7: idea behind simple P2P (naive but should work)\n\nIt should define 3 main functions:\n\n- **scan**: iterates (like `nmap`) through defined ranged of ports for whole\n    local network and sends message to test if they\n    are used by our application (welcome token).\n- **push**: starts a server (called here node) in new process with shared filed (if someone sends correct request, it starts new thread for this connection)\n- **pull**: connects to server and downloads the data\n\n\u003cimg src=\"screen-5.png\" width=\"500\"\u003e\n\nTo be a **real** P2P, nodes should be propagated (without user action) through network (additionally only parts of files, not whole).\n\n### 8: still writing...\n\n### 9: still writing...\n\n### 10: still writing...\n\n## 🤝 Contribute [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com)\n\nIf you are interested in participating in joint development, PR and Forks are welcome!\n\n## 📜 License\n\n[MIT](LICENSE.md) Copyright (c) Maciej A. Czyzewski\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaciejczyzewski%2Fairtrash","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaciejczyzewski%2Fairtrash","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaciejczyzewski%2Fairtrash/lists"}