{"id":13728294,"url":"https://github.com/Niminem/Neel","last_synced_at":"2025-05-08T00:31:30.903Z","repository":{"id":43602320,"uuid":"299354262","full_name":"Niminem/Neel","owner":"Niminem","description":"A Nim library for making Electron-like HTML/JS GUI apps, with full access to Nim capabilities.","archived":false,"fork":false,"pushed_at":"2023-11-30T06:29:28.000Z","size":482,"stargazers_count":227,"open_issues_count":1,"forks_count":8,"subscribers_count":13,"default_branch":"master","last_synced_at":"2024-08-04T02:07:59.920Z","etag":null,"topics":["desktop-app","desktop-application","electron","electron-app","gui","gui-application","nim","nim-gui","nim-lang","nim-language","nim-library"],"latest_commit_sha":null,"homepage":"","language":"Nim","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/Niminem.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}},"created_at":"2020-09-28T15:36:21.000Z","updated_at":"2024-07-23T10:53:04.000Z","dependencies_parsed_at":"2023-02-08T01:47:38.767Z","dependency_job_id":"8f1cfa8b-5bca-4ac1-a69d-b224a99c123b","html_url":"https://github.com/Niminem/Neel","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Niminem%2FNeel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Niminem%2FNeel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Niminem%2FNeel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Niminem%2FNeel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Niminem","download_url":"https://codeload.github.com/Niminem/Neel/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224679879,"owners_count":17351883,"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-app","desktop-application","electron","electron-app","gui","gui-application","nim","nim-gui","nim-lang","nim-language","nim-library"],"created_at":"2024-08-03T02:00:40.049Z","updated_at":"2024-11-14T19:30:54.460Z","avatar_url":"https://github.com/Niminem.png","language":"Nim","funding_links":[],"categories":["👓 Alternatives to the [Electron.js](https://electronjs.org) ⚛","User Interface"],"sub_categories":["Nim","GUI"],"readme":"# Neel | HTML/JS GUI Library for Nim\n\nNeel is a Nim library for making lightweight Electron-like HTML/JS GUI apps, with full access to Nim capabilities and targets any of the C, C++, or Objective-C backends.\n\n\u003e By default, Neel opens a new Chrome session in app mode and allows the Nim backend and HTML/JS frontend to communicate via JSON and websockets. Alternate mode available, simply opening a new tab in your system's default browser. Webview capabilities coming soon.\n\nNeel is designed to take all the hassle out of writing GUI applications. Current Features:\n\n* Eliminate 99% of boilerplate code\n* Automatic routes\n* Embeds frontend assets at compiletime (on `release` builds)\n* Automatic type conversions (from JSON to each procedure's parameter types)\n* Simple interface for backend/frontend communication\n* Cross-platform (tested on Mac, Windows, and Linux)\n\nNeel is inspired by [Eel](https://github.com/samuelhwilliams/Eel), its Python cousin.\n\n----------------------\n\n## Introduction\n\nCurrently, Nim’s options for writing GUI applications are quite limited and if you wanted to use HTML/JS instead you can expect a lot of boilerplate code and headaches.\n\nNeel is still in its infancy, so as of right now I don’t think it’s suitable for making full-blown commercial applications like Slack or Twitch. It is, however, very suitable for making all kinds of other projects and tools.\n\nThe best visualization libraries that exist are in Javascript and the most powerful capabilities of software can be harnessed with Nim. The goal of Neel is to combine the two languages and assist you in creating some killer applications.\n\n## Installation\n\nInstall from nimble:\n`nimble install neel`\n\n## Usage\n\n### Directory Structure\n\nCurrently, Neel applications consist of various static web assets (HTML,CSS,JS, etc.) and Nim modules.\n\nAll of the frontend files need to be placed within a single folder (they can be further divided into more folders within it if necessary). The starting URL must be named `index.html` and placed within the root of the web folder.\n\n```\nmain.nim            \u003c----\\\ndatabase.nim        \u003c-------- Nim files\nother.nim           \u003c----/\n/assets/             \u003c------- Web folder containing frontend files (can be named whatever you want)\n  index.html        \u003c-------- The starting URL for the application (**must** be named 'index.html' and located within the root of the web folder)\n  /css/\n    style.css\n  /js/\n    main.js\n```\n\n### Developing the Application\n\n#### Nim / Backend\n\nWe begin with a very simple example, from there I'll explain the process and each part in detail.\n\n(main.nim)\n```nim\nimport neel #1\n\nexposeProcs: #2\n    proc echoThis(jsMsg: string) =\n        echo \"got this from frontend: \" \u0026 jsMsg\n        callJs(\"logThis\", \"Hello from Nim!\") #3\n\nstartApp(webDirPath=\"path-to-web-assets-folder\") #4\n```\n\n##### #1 import neel\n\nWhen you `import neel`, several modules are exported into the calling module. `exposedProcs` and `startApp` are macros that require these modules in order to work properly. The list below are all of the exported modules. It's not necessary to remember them, and even if you accidently imported the module twice Nim disregards it. This is just for your reference really.\n* `std/os`\n* `std/osproc`\n* `std/strutils`\n* `std/json`\n* `std/threadpool`\n* `std/browsers`\n* `std/jsonutils`\n* `pkg/mummy`\n* `pkg/routers`\n\n##### #2 exposeProcs\n\n`exposeProcs` is a macro that *exposes* specific procedures for javascript to be able to call from the frontend. When the macro is expanded, it creates a procedure `callNim` which contains **all exposed procedures** and will call a specified procedure based on frontend data, passing in the appropriate parameters (should there be any).\n\nThe data being received is initially **JSON** and needs to be converted into the appropriate types for each parameter in a procedure. This is also handled by the macro.\n\nAs of Neel 1.1.0, you can use virtually any Nim type for parameters in *exposed procedures*. Neel uses `std/jsonutils` to programmatically handle the conversions. Some caveats:\n* Does not support default values for parameters.\n* Does not support generics for parameters.\n\nThis above macro produces this result:\n\n```nim\nproc callNim(procName: string; params: seq[JsonNode]) =\n    proc echoThis(jsMsg: string) =\n        echo \"got this from frontend: \" \u0026 jsMsg\n        callJs(\"logThis\", \"Hello from Nim!\")\n    case procName\n    of \"echoThis\": echoThis(params[0].getStr)\n    ...\n```\n\nNOTE: You *can* pass complex data in a single parameter now if you'd like to. Use Javascript objects or dictionaries and simply create a custom object type to accept it from the Nim side.\n\nI'm sure this is obvious, but it's much cleaner to have your exposed procedures call procedures from other modules.\n\nExample:\n```nim\n# (main.nim)\nimport neel, othermodule\nexposeProcs:\n    proc proc1(param: seq[JsonNode]) =\n        doStuff(param[0].getInt)\n...\n\n# (othermodule.nim)\nfrom neel import callJs # you only need to import this macro from Neel :)\n\nproc doStuff*(param: int) =\n    var dataForFrontEnd = param + 100\n    callJs(\"myJavascriptFunc\", dataForFrontEnd)\n...\n```\n\n##### #3 callJs\n\n`callJs` is a macro that takes in at least one value, a `string`, and it's *the name of the javascript function you want to call*. Any other value will be passed into that javascript function call on the frontend. You may pass in any amount to satisfy your function parameters needs like so:\n\n```nim\ncallJs(\"myJavascriptFunc\",1,3.14,[\"some stuff\",1,9000])\n```\n\nThe above code gets converted into stringified JSON and sent to the frontend via websocket.\n\n##### #4 startApp\n\n`startApp` is a macro that handles server logic, routing, and Chrome web browser. `startApp` currently takes 6 parameters.\nexample:\n\n```nim\nstartApp(webDirPath= \"path_to_web_assets_folder\", portNo=5000,\n            position= [500,150], size= [600,600], chromeFlags= @[\"--force-dark-mode\"], appMode= true)\n            # left, top          # width, height\n```\n\n* `webDirPath` : sets the path to the web directory containing all frontend assets **needs to be set**\n* `portNo` : specifies the port for serving your application (default is 5000)\n* `position` : positions the *top* and *left* side of your application window (default is 500 x 150)\n* `size` : sets the size of your application window by *width* and *height*(default is 600 x 600)\n* `chromeFlags` : passes any additional flags to chrome (default is none)\n* `appMode` : if \"true\" (default) Chrome will open a new session/window in App mode, if \"false\" a new tab will be opened in your *current default browser* - which can be very useful for debugging.\n\n#### Javascript / Frontend\n\nThe Javascript aspect of a Neel app isn't nearly as complex. Let's build the frontend for the example above:\n\n(index.html)\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n    \u003cmeta charset=\"UTF-8\"\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\u003e\n    \u003ctitle\u003eNeel App Example\u003c/title\u003e\n    \u003cscript src=\"/neel.js\"\u003e\u003c/script\u003e \u003c!-- always include /neel.js in your \u003chead\u003e --\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n    \u003ch1\u003eMy First Neel App\u003c/h1\u003e\n    \u003cscript src=\"/main.js\"\u003e\u003c/script\u003e \u003c!-- always use absolute paths with web assets --\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n(main.js)\n```javascript\nneel.callNim(\"echoThis\",\"Hello from Javascript!\")\n\nfunction logThis(param){\n    console.log(param)\n}\n```\nThe first thing you'll notice is we've included a script tag containing `/neel.js` in the \u003chead\u003e section of our HTML page. This allows Neel to handle all of the logic on the frontend for websocket connections and function/procedure calls.\n\n`neel.callNim` is a function that takes in at least one value, a `string`, and it's *the name of the Nim procedure you want to call*. Any other value will be passed into that Nim procedure call on the backend asa parameter. **You must pass in the correct number of params for that proc, in order, and of the correct types for it to be called properly.** Example: \n\nfrontend call to backend:\n```javascript\nneel.callNim(\"myNimProc\",1,3.14,[\"some stuff\",1,9000])\n```\nmust match the result of the `exposeProcs` macro:\n```nim\n...\nof \"myNimProc\": return myNimProc(params[0].getInt,params[1].getFloat,params[2].getElems)\n...\n```\n\nAs of Neel 1.1.0, there is exception handling in place (with a verbose logging in debug builds) for unknown procedure calls and parameter type mismatches.\n\nGoing back to our first example, when `index.html` is served, Javascript will call the `echoThis` procedure and pass \"Hello from Javascript!\" as the param. This will print the string in the terminal. Then, Nim will call the `logThis` function and pass \"Hello from Nim!\". Neel handles the JSON conversion, calls the function and passes in the param.\n\nNow open the console in Chrome developer tools and you should see \"Hello from Nim!\".\n\n**Keep In Mind: absolute paths must be used within your HTML files** ex: `\u003cscript src=\"/this_js_module.js\u003e\u003c/script\u003e`\n\n#### Compilation Step\n\nIf using nim 1.6.X branch, compile your Neel application with `--threads:on` and `--mm:orc`. *Nim \u003e= 2.0.0 does this by default.*\nexample:\n```nim\nnim c -r --threads:on --mm:orc main.nim\n```\nWhen compiling for Windows, also pass the `--app:gui` flag on your `release` builds if you want to prevent the app opening up with a terminal.\nexample:\n```nim\nnim c -r --threads:on --mm:orc --app:gui -d:release main.nim\n```\n\n#### Final Thoughts Before Developing With Neel\nKeep the following in mind when developing you Neel App:\n* All of your frontend assets are embedded into the binary when compiling a `release` build. Stick to debug builds when needing to modify/change static frontend assets, as you can simply refresh a page and see the updates in real-time.\n* To prevent crashes when users spam refresh or constantly switch between different pages, we implemented a sort of countdown timer for shutting down. For debug builds, approximately 3 seconds after closing the app window the server and program is shutdown if a websocket hasn't reconnected within that time period. For release builds that time delay is approximately 10 seconds for some extra cusion.\n\n## Examples\n\nA simple Neel app that picks a random filename out of a given folder (something impossible from a browser):\n\n[Random File Picker](https://github.com/Niminem/Neel/tree/master/examples/FilePicker)\n\n## Neel version 1.0.0 Newly Released 10/28/23\n\nNeel now leverages the power of [Mummy](https://github.com/guzba/mummy) for websever / websocket needs. Mummy returns to the ancient way of threads, removing the need for async entirely. Historically Neel applications ran into async problems in certain situations. For example, try/except blocks were necessary in some exposed procedures yet using async procedures within an except block is a no go.\n\n## Future Work\n\nThe vision for this library is to eventually have this as full-fledged as Electron for Nim. I believe it has the potential for developing commercial applications and maybe one day even rival Electron as a framework.\n\nA BIG teaser for what's to come within the next few releases:\n\n- [X] Arbitrary JavaScript-To-Nim Type Conversions (JSON Parsing)\n\n      As long as the parameter types from the Javascript side match to the Nim side, use what you want!\n\n- [ ] Webview Capabilities\n\n      [Webview](https://github.com/webview/webview) is an MIT licensed cross-platform webview library for C/C++. Uses WebKit (GTK/Cocoa) and Edge WebView2 (Windows). Having a webview target will get us closer to solid framework for shipping commercial builds.\n\n- [ ] Distributable Applications\n\n      Build your Neel app and have it packaged and ready to be shipped.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNiminem%2FNeel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FNiminem%2FNeel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNiminem%2FNeel/lists"}