├── .gitattributes ├── .github └── workflows │ ├── codeql-analysis.yml │ ├── linux.yml │ ├── macos.yml │ └── windows.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── docs ├── dochack.js ├── globals.html ├── httpRenderer.html ├── nimdoc.out.css ├── nimview_tmp.html ├── requestMap.html ├── sharedTypes.html ├── storage.html ├── theindex.html └── webviewRenderer.html ├── examples ├── .gitignore ├── android │ ├── .gitattributes │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── app │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── cpp │ │ │ ├── CMakeLists.txt │ │ │ ├── native-lib.cpp │ │ │ └── nimbase.h │ │ │ ├── java │ │ │ └── com │ │ │ │ └── nimviewAndroid │ │ │ │ ├── CppWrapper.kt │ │ │ │ └── MainActivity.kt │ │ │ ├── nim │ │ │ ├── .gitignore │ │ │ ├── App.nimble │ │ │ ├── dist │ │ │ │ ├── favicon.png │ │ │ │ ├── global.css │ │ │ │ └── index.html │ │ │ ├── nakefile.nim │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── readme.md │ │ │ ├── rollup.config.js │ │ │ └── src │ │ │ │ ├── App.nim │ │ │ │ ├── App.svelte │ │ │ │ ├── main.js │ │ │ │ └── nim.cfg │ │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── layout │ │ │ └── activity_main.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── c_cpp │ ├── App.nimble │ ├── App.sln │ ├── App.vcxproj │ ├── dist │ │ ├── index.html │ │ ├── nimview.iife.js │ │ └── nimview.iife.js.map │ ├── nakefile.nim │ ├── readme.md │ └── src │ │ ├── c_sample.c │ │ ├── cpp_sample.cpp │ │ ├── library.nim │ │ └── nim.cfg ├── minimal │ ├── App.nim │ ├── dist │ │ ├── index.html │ │ ├── nimview.iife.js │ │ └── nimview.iife.js.map │ └── nim.cfg ├── minimal2 │ ├── App.nim │ ├── dist │ │ ├── index.html │ │ ├── nimview.iife.js │ │ └── nimview.iife.js.map │ └── nim.cfg ├── python │ ├── MANIFEST.in │ ├── README.md │ ├── bdist_wheel.py │ ├── nakefile.nim │ ├── pySample.py │ ├── setup.cfg │ ├── setup.py │ └── src │ │ ├── LICENSE │ │ ├── README.md │ │ ├── __init__.py │ │ ├── library.nim │ │ ├── nim.cfg │ │ └── pyTest.py ├── react │ ├── .env │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── robots.txt │ └── src │ │ ├── App.jsx │ │ ├── App.nim │ │ ├── components │ │ ├── Navbar.jsx │ │ └── Sample.jsx │ │ ├── index.js │ │ └── nim.cfg ├── svelte │ ├── .gitignore │ ├── dist │ │ ├── favicon.png │ │ ├── global.css │ │ └── index.html │ ├── package-lock.json │ ├── package.json │ ├── readme.md │ ├── rollup.config.js │ └── src │ │ ├── App.nim │ │ ├── App.svelte │ │ ├── components │ │ ├── Navbar.svelte │ │ └── Sample.svelte │ │ ├── main.js │ │ └── nim.cfg ├── svelte_todo │ ├── .gitignore │ ├── dist │ │ ├── favicon.png │ │ ├── global.css │ │ └── index.html │ ├── package-lock.json │ ├── package.json │ ├── readme.md │ ├── rollup.config.js │ └── src │ │ ├── App.nim │ │ ├── App.svelte │ │ ├── main.js │ │ └── nim.cfg └── vue │ ├── README.md │ ├── babel.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ └── index.html │ ├── src │ ├── App.nim │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ ├── Navbar.vue │ │ └── Sample.vue │ ├── main.js │ └── nim.cfg │ └── vue.config.js ├── nimview.nimble ├── src ├── js │ ├── LICENSE │ ├── README.md │ ├── nimview.cjs.js │ ├── nimview.cjs.js.map │ ├── nimview.esm.js │ ├── nimview.esm.js.map │ ├── nimview.iife.js │ ├── nimview.iife.js.map │ ├── nimview.js │ ├── package-lock.json │ ├── package.json │ └── rollup.config.js ├── nimview.hpp ├── nimview.nim ├── nimview.nim.cfg └── nimview │ ├── dispatchJsonRequest.nim │ ├── globalToken.nim │ ├── globals.nim │ ├── httpRenderer.nim │ ├── readme.md │ ├── requestMap.nim │ ├── sharedTypes.nim │ ├── std │ ├── readme.md │ └── sysrand.nim │ ├── storage.nim │ ├── webview │ ├── .gitattributes │ ├── .gitignore │ ├── docs │ │ └── webview.html │ ├── readme.md │ ├── tests │ │ ├── bindEx.nim │ │ ├── config.nims │ │ ├── minimal.nim │ │ └── scopeTest.nim │ ├── webview.nim │ ├── webview.nimble │ └── webview │ │ ├── LICENSE │ │ ├── README.md │ │ ├── webview.go │ │ └── webview.h │ └── webviewRenderer.nim └── tests ├── __init__.py ├── c_test.c ├── desktopSample.nim ├── httpSample.nim ├── icon.ico ├── nim.cfg ├── pyTest.py ├── pyWebViewSample.py ├── requestsFail.nim ├── requestsSuccess.nim └── test.dump /.gitattributes: -------------------------------------------------------------------------------- 1 | docs/** linguist-documentation 2 | src/js/*map linguist-generated 3 | src/js/*.cjs.js linguist-generated 4 | src/js/*.iife.js linguist-generated 5 | src/js/*.esm.js linguist-generated 6 | src/nimview/std/** linguist-vendored 7 | src/nimview/webview/** linguist-vendored 8 | src/nimview/webview2/** linguist-vendored -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '34 13 * * 6' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'javascript', 'python' ] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v2 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v1 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, you can do so here or in a config file. 46 | # By default, queries listed here will override any specified in a config file. 47 | # Prefix the list here with "+" to use these queries and those in the config file. 48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 49 | 50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 51 | # If this step fails, then you should remove it and run the build manually (see below) 52 | # - name: Autobuild 53 | # uses: github/codeql-action/autobuild@v1 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v1 68 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI linux 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | push: 9 | branches: [ main ] 10 | pull_request: 11 | branches: [ main ] 12 | 13 | 14 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 15 | jobs: 16 | # This workflow contains a single job called "build" 17 | build: 18 | # The type of runner that the job will run on 19 | runs-on: ubuntu-latest 20 | # Steps represent a sequence of tasks that will be executed as part of the job 21 | steps: 22 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 23 | - uses: actions/checkout@v2 24 | - uses: iffy/install-nim@v3.2.2 25 | - name: InstallLinuxDependencies 26 | if: runner.os == 'Linux' 27 | run: | 28 | sudo apt-get update && sudo apt install -y gcc npm libwebkit2gtk-4.0-dev curl python3 29 | 30 | - name: InstallMacOSDependencies 31 | if: runner.os == 'macOS' 32 | run: | 33 | brew install gcc python && pip install pathlib 34 | # Runs a set of commands using the runners shell 35 | - name: Test 36 | run: | 37 | nim --version 38 | nimble install -d -y --noSSLCheck --verbose 39 | nimble test -y 40 | 41 | - name: Release 42 | uses: softprops/action-gh-release@v1 43 | if: startsWith(github.ref, 'refs/tags/') 44 | with: 45 | files: | 46 | build/*.exe 47 | build/*.dll 48 | build/*.a 49 | build/*.pyd 50 | build/*.so 51 | env: 52 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 53 | 54 | # uses: actions/upload-artifact@v2 55 | # with: 56 | # name: binaries 57 | # retention-days: 5 58 | # path: | 59 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI MacOS 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | push: 9 | branches: [ main ] 10 | pull_request: 11 | branches: [ main ] 12 | 13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 14 | jobs: 15 | # This workflow contains a single job called "build" 16 | build: 17 | # The type of runner that the job will run on 18 | runs-on: macOS-latest 19 | # Steps represent a sequence of tasks that will be executed as part of the job 20 | steps: 21 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 22 | - uses: actions/checkout@v2 23 | - uses: iffy/install-nim@v3.2.2 24 | - name: InstallLinuxDependencies 25 | if: runner.os == 'Linux' 26 | run: | 27 | sudo apt-get update && sudo apt install -y gcc npm libwebkit2gtk-4.0-dev curl python3 28 | 29 | - name: InstallMacOSDependencies 30 | if: runner.os == 'macOS' 31 | run: | 32 | brew install gcc python && pip install pathlib 33 | # Runs a set of commands using the runners shell 34 | - name: Test 35 | run: | 36 | nim --version 37 | nimble install -d -y --noSSLCheck --verbose 38 | nimble test -y 39 | 40 | - name: Release 41 | uses: softprops/action-gh-release@v1 42 | if: startsWith(github.ref, 'refs/tags/') 43 | with: 44 | files: | 45 | build/*.exe 46 | build/*.dll 47 | build/*.a 48 | build/*.pyd 49 | build/*.so 50 | env: 51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 | 53 | # uses: actions/upload-artifact@v2 54 | # with: 55 | # name: binaries 56 | # retention-days: 5 57 | # path: | 58 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI Windows 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | push: 9 | branches: [ main ] 10 | pull_request: 11 | branches: [ main ] 12 | 13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 14 | jobs: 15 | # This workflow contains a single job called "build" 16 | build: 17 | # The type of runner that the job will run on 18 | runs-on: windows-latest 19 | # Steps represent a sequence of tasks that will be executed as part of the job 20 | steps: 21 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 22 | - uses: actions/checkout@v2 23 | - uses: iffy/install-nim@v3.2.2 24 | - name: InstallLinuxDependencies 25 | if: runner.os == 'Linux' 26 | run: | 27 | sudo apt-get update && sudo apt install -y gcc npm libwebkit2gtk-4.0-dev curl python3 28 | 29 | - name: InstallMacOSDependencies 30 | if: runner.os == 'macOS' 31 | run: | 32 | brew install gcc python && pip install pathlib 33 | # Runs a set of commands using the runners shell 34 | - name: Test 35 | run: | 36 | nim --version 37 | nimble install -d -y --noSSLCheck --verbose 38 | nimble test -y 39 | 40 | - name: Release 41 | uses: softprops/action-gh-release@v1 42 | if: startsWith(github.ref, 'refs/tags/') 43 | with: 44 | files: | 45 | build/*.exe 46 | build/*.dll 47 | build/*.a 48 | build/*.pyd 49 | build/*.so 50 | env: 51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 | 53 | # uses: actions/upload-artifact@v2 54 | # with: 55 | # name: binaries 56 | # retention-days: 5 57 | # path: | 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | **/dist 3 | **/node_modules 4 | tmp_c* 5 | **/x64/* 6 | debug.log 7 | 8 | nimcache/ 9 | nimblecache/ 10 | htmldocs/ 11 | 12 | # local env files 13 | .env.local 14 | .env.*.local 15 | 16 | # Log files 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | pnpm-debug.log* 21 | 22 | # Editor directories and files 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | *.pdb 30 | *.ipch 31 | *.ipdb 32 | *.iobj 33 | *.ilk 34 | *.user 35 | *.VC.db 36 | *.bak 37 | 38 | # binaries 39 | *.o 40 | *.obj 41 | *.a 42 | *.dll 43 | *.lib 44 | *.so 45 | *.pyd 46 | *.pyc 47 | 48 | # Executables 49 | *.exe 50 | *.app -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["ms-vscode.cpptools", "damiankoper.gdb-debug", "kosz78.nim", "xaver.clang-format"] 3 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | 8 | { 9 | "name": "(gdb) Anfügen", 10 | "type": "cppdbg", 11 | "request": "attach", 12 | "program": "${workspaceFolder}/out/nimview.pyd", 13 | "processId": "${command:pickProcess}", 14 | "MIMode": "gdb", 15 | // "miDebuggerPath": "/path/to/gdb", 16 | "setupCommands": [ 17 | { 18 | "description": "Automatische Strukturierung und Einrückung für \"gdb\" aktivieren", 19 | "text": "-enable-pretty-printing", 20 | "ignoreFailures": true 21 | } 22 | ] 23 | }, 24 | { 25 | "name": "nim debug (gdb) ", 26 | "type": "cppdbg", 27 | "request": "launch", 28 | "program": "${workspaceFolder}/nimview_debug.exe", 29 | "args": ["dir"], 30 | "stopAtEntry": false, 31 | "cwd": "${workspaceFolder}", 32 | "environment": [], 33 | "externalConsole": false, 34 | "MIMode": "gdb", 35 | "includePath": ["src/tmp_c"], 36 | // "miDebuggerPath": "C:/nim-1.2.6/dist/mingw64/bin/gdb.exe", 37 | "setupCommands": [ 38 | { 39 | "description": "Automatische Strukturierung und Einrückung für \"gdb\" aktivieren", 40 | "text": "-enable-pretty-printing", 41 | "ignoreFailures": true 42 | } 43 | ], 44 | "preLaunchTask": "nimble debug" 45 | } 46 | 47 | 48 | ] 49 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "iostream": "c", 4 | "xlocale": "c", 5 | "xstring": "cpp", 6 | "variant": "cpp", 7 | "nimview.h": "c", 8 | "functional": "cpp", 9 | "atomic": "cpp", 10 | "bit": "cpp", 11 | "cctype": "cpp", 12 | "clocale": "cpp", 13 | "cmath": "cpp", 14 | "compare": "cpp", 15 | "concepts": "cpp", 16 | "cstddef": "cpp", 17 | "cstdint": "cpp", 18 | "cstdio": "cpp", 19 | "cstdlib": "cpp", 20 | "cstring": "cpp", 21 | "ctime": "cpp", 22 | "cwchar": "cpp", 23 | "exception": "cpp", 24 | "initializer_list": "cpp", 25 | "ios": "cpp", 26 | "iosfwd": "cpp", 27 | "istream": "cpp", 28 | "iterator": "cpp", 29 | "limits": "cpp", 30 | "list": "cpp", 31 | "memory": "cpp", 32 | "new": "cpp", 33 | "ostream": "cpp", 34 | "stdexcept": "cpp", 35 | "streambuf": "cpp", 36 | "string": "cpp", 37 | "system_error": "cpp", 38 | "tuple": "cpp", 39 | "type_traits": "cpp", 40 | "typeinfo": "cpp", 41 | "unordered_map": "cpp", 42 | "utility": "cpp", 43 | "vector": "cpp", 44 | "xfacet": "cpp", 45 | "xhash": "cpp", 46 | "xiosbase": "cpp", 47 | "xlocinfo": "cpp", 48 | "xlocnum": "cpp", 49 | "xmemory": "cpp", 50 | "xstddef": "cpp", 51 | "xtr1common": "cpp", 52 | "xutility": "cpp", 53 | "map": "cpp", 54 | "xtree": "cpp", 55 | "sstream": "cpp", 56 | "stdio.h": "c", 57 | "*.idl": "cpp" 58 | } 59 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "2.0.0", 6 | "tasks": [ 7 | { 8 | "label": "nimble debug", 9 | "command": "nimble debug --threads:on --debugger:native", 10 | "options": { 11 | "cwd": "${workspaceRoot}" 12 | }, 13 | "type": "shell", 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 marcomq 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/dochack.js: -------------------------------------------------------------------------------- 1 | 2 | function main() { 3 | var pragmaDots = document.getElementsByClassName("pragmadots"); 4 | for (var i = 0; i < pragmaDots.length; i++) { 5 | pragmaDots[i].onclick = function(event) { 6 | // Hide tease 7 | event.target.parentNode.style.display = "none"; 8 | // Show actual 9 | event.target.parentNode.nextElementSibling.style.display = "inline"; 10 | } 11 | } 12 | 13 | const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); 14 | function switchTheme(e) { 15 | if (e.target.checked) { 16 | document.documentElement.setAttribute('data-theme', 'dark'); 17 | localStorage.setItem('theme', 'dark'); 18 | } else { 19 | document.documentElement.setAttribute('data-theme', 'light'); 20 | localStorage.setItem('theme', 'light'); 21 | } 22 | } 23 | 24 | toggleSwitch.addEventListener('change', switchTheme, false); 25 | 26 | const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; 27 | if (currentTheme) { 28 | document.documentElement.setAttribute('data-theme', currentTheme); 29 | 30 | if (currentTheme === 'dark') { 31 | toggleSwitch.checked = true; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /docs/sharedTypes.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | src/nimview/sharedTypes 21 | 22 | 23 | 24 | 25 | 67 | 68 | 69 | 70 |
71 |
72 |

src/nimview/sharedTypes

73 |
74 |
75 |
76 | 80 |     Dark Mode 81 |
82 | 89 |
90 | Search: 92 |
93 |
94 | Group by: 95 | 99 |
100 | 115 | 116 |
117 | 118 |
119 |
120 | 121 |

122 |
123 |

Types

124 |
125 |
126 |
ReqDeniedException = object of CatchableError
127 |
128 | 129 | 130 | 131 |
132 |
133 |
134 |
ReqUnknownException = object of CatchableError
135 |
136 | 137 | 138 | 139 |
140 |
141 |
142 |
ServerException = object of CatchableError
143 |
144 | 145 | 146 | 147 |
148 |
149 | 150 |
151 | 152 |
153 |
154 | 155 |
156 | 161 |
162 |
163 |
164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /docs/storage.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | src/nimview/storage 21 | 22 | 23 | 24 | 25 | 67 | 68 | 69 | 70 |
71 |
72 |

src/nimview/storage

73 |
74 |
75 |
76 | 80 |     Dark Mode 81 |
82 | 89 |
90 | Search: 92 |
93 |
94 | Group by: 95 | 99 |
100 | 124 | 125 |
126 | 127 |
128 |
129 | 130 |

Implements a simple key / value store that can be used to store preferences

131 |
132 |

Procs

133 |
134 |
135 |
proc getStoredVal(key: string): string {....raises: [], tags: [].}
136 |
137 | 138 | 139 | 140 |
141 |
142 |
143 |
proc initStorage(fileName: string = "") {....raises: [],
144 |     tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect].}
145 |
146 | 147 | 148 | 149 |
150 |
151 |
152 |
proc setStoredVal(key, value: string): string {....raises: [],
153 |     tags: [WriteIOEffect].}
154 |
155 | 156 | 157 | 158 |
159 |
160 | 161 |
162 | 163 |
164 |
165 | 166 |
167 | 172 |
173 |
174 |
175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /public/build/ 3 | /public/vendor/ 4 | 5 | .DS_Store 6 | storage.json -------------------------------------------------------------------------------- /examples/android/.gitattributes: -------------------------------------------------------------------------------- 1 | app/src/nimview/* linguist-vendored=true -------------------------------------------------------------------------------- /examples/android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /app/.cxx 13 | /captures 14 | .externalNativeBuild 15 | .cxx* 16 | .cxx 17 | *.exe 18 | app/src/main/cpp/x86* 19 | app/src/main/cpp/x86_64* 20 | app/src/main/cpp/armeabi-v7a* 21 | app/src/main/cpp/arm64-v8a* 22 | app/src/main/node_modules* 23 | */build* 24 | app/src/main/assets/* -------------------------------------------------------------------------------- /examples/android/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 marcomq 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/android/README.md: -------------------------------------------------------------------------------- 1 | # nimview_android 2 | A Nim/Webview based helper to create Android applications with Nim/C/C++ and HTML/CSS 3 | 4 | Android Studio implementation of [Nimview](https://github.com/marcomq/nimview) 5 | 6 | This project uses Android Webview as UI layer. The back-end is supposed to be written in Nim, C/C++ 7 | or - if it doesn't need to be ported to other platforms - Kotlin or Java. 8 | As Android Webview doesn't has as much debugging capabilities as Chrome or Firefox, it would be recommended to write most of the UI in a web application 9 | with nimview in debug mode + npm autoreload first and then test these changes on Android later. 10 | 11 | The Nimview folder is in android/app/src/main/nim 12 | 13 | The recommended workflow would be: 14 | ``` 15 | (installation) 16 | npx degit marcomq/nimview/examples/android myAndroid 17 | cd app/src/main/nim 18 | nimble install -d -y 19 | npm install 20 | 21 | 22 | (development) 23 | (open two terminals/shells, t1 and t2) 24 | t1: nim cpp -r -d:useServer src/App.nim 25 | t2: npm run dev 26 | open browser at http://localhost:5000 27 | (optional) modify nim / html / js code in VSCode 28 | restart nim server to apply changes on nim code 29 | press F5 if browser doesn't refresh automatically after js changes 30 | 31 | (release and testing) 32 | compile project with android studio, test, perform changes if required 33 | 34 | ``` -------------------------------------------------------------------------------- /examples/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | android { 6 | compileSdkVersion 29 7 | 8 | defaultConfig { 9 | applicationId "com.nimviewAndroid" 10 | minSdkVersion 21 11 | targetSdkVersion 29 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | externalNativeBuild { 17 | cmake { 18 | cppFlags "-std=c++17" 19 | } 20 | } 21 | } 22 | 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 27 | } 28 | 29 | debug { 30 | applicationIdSuffix ".debug" 31 | debuggable true 32 | } 33 | } 34 | externalNativeBuild { 35 | cmake { 36 | path "src/main/cpp/CMakeLists.txt" 37 | version "3.10.2" 38 | } 39 | } 40 | ndkVersion '21.0.6113669' 41 | 42 | sourceSets { 43 | main { 44 | assets.srcDirs = ['src/main/nim/dist'] 45 | } 46 | } 47 | } 48 | 49 | dependencies { 50 | implementation fileTree(dir: "libs", include: ["*.jar"]) 51 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 52 | implementation 'androidx.core:core-ktx:1.3.1' 53 | implementation 'androidx.appcompat:appcompat:1.2.0' 54 | implementation 'androidx.constraintlayout:constraintlayout:2.0.1' 55 | testImplementation 'junit:junit:4.12' 56 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 57 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 58 | } 59 | -------------------------------------------------------------------------------- /examples/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | -keepattributes SetJavaScriptEnabled 15 | -keepattributes JavascriptInterface 16 | -keepclassmembers class **.*$NativeCpp { 17 | *; 18 | } 19 | 20 | -keepclassmembers class com.nimviewAndroid.CppWrapper { 21 | public *; 22 | } 23 | 24 | # Uncomment this to preserve the line number information for 25 | # debugging stack traces. 26 | #-keepattributes SourceFile,LineNumberTable 27 | 28 | # If you keep the line number information, uncomment this to 29 | # hide the original source file name. 30 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /examples/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /examples/android/app/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html 3 | 4 | # Sets the minimum version of CMake required to build the native library. 5 | 6 | cmake_minimum_required(VERSION 3.5) 7 | 8 | include_directories(. ${ANDROID_ABI} ) 9 | 10 | add_custom_target (nimToCAndJs ALL 11 | COMMAND nake 12 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../nim/) 13 | 14 | add_definitions(-DNIMVIEW_CUSTOM_LIB -DJUST_CORE -funsigned-char) 15 | 16 | aux_source_directory(${ANDROID_ABI} ALL_NIM_C_FILES) 17 | 18 | # Creates and names a library, sets it as either STATIC 19 | # or SHARED, and provides the relative paths to its source code. 20 | # You can define multiple libraries, and CMake builds them for you. 21 | # Gradle automatically packages shared libraries with your APK. 22 | add_library( # Sets the name of the library. 23 | native-lib 24 | # Sets the library as a shared library. 25 | SHARED 26 | # Provides a relative path to your source file(s). 27 | native-lib.cpp ${ALL_NIM_C_FILES}) 28 | 29 | add_dependencies(native-lib nimToCAndJs) 30 | 31 | find_library( # Sets the name of the path variable. 32 | log-lib 33 | # Specifies the name of the NDK library that 34 | # you want CMake to locate. 35 | log ) 36 | 37 | # Specifies libraries CMake should link to your target library. You 38 | # can link multiple libraries, such as libraries you define in this 39 | # build script, prebuilt third-party libraries, or system libraries. 40 | 41 | target_link_libraries( # Specifies the target library. 42 | native-lib 43 | # Links the target library to the log library 44 | # included in the NDK. 45 | ${log-lib} ) -------------------------------------------------------------------------------- /examples/android/app/src/main/cpp/native-lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "App.h" 5 | #include "nimview.hpp" 6 | #include 7 | 8 | jint myJniVersion; 9 | JavaVM* myJvm = NULL; 10 | jobject* myCppWrapper = NULL; 11 | jmethodID myEvaljsFunc; 12 | 13 | void webviewEvalJs(char* js) { 14 | // this doesn't seem to call javascript for some reason and crashes on 2nd call 15 | JNIEnv* env = NULL; 16 | jint rs = myJvm->GetEnv((void **)&env, myJniVersion); 17 | assert (rs == JNI_OK); 18 | jstring jstr = env->NewStringUTF(js); 19 | env->CallVoidMethod(*myCppWrapper, myEvaljsFunc, jstr); 20 | env->DeleteLocalRef(jstr); 21 | }; 22 | 23 | #define THIS_PROJECT_PREFIX Java_com_nimviewAndroid 24 | extern "C" JNIEXPORT jstring JNICALL 25 | Java_com_nimviewAndroid_CppWrapper_callNim( 26 | JNIEnv* env, 27 | jobject self, 28 | jstring request, jstring value) { 29 | myCppWrapper = &self; 30 | const char* cRequest = env->GetStringUTFChars(request, nullptr); 31 | const char* cValue = env->GetStringUTFChars(value, nullptr); 32 | std::string result; 33 | try { 34 | result = nimview::dispatchRequest(std::string(cRequest), std::string(cValue)); 35 | } 36 | catch(...) { 37 | __android_log_write(ANDROID_LOG_ERROR, "Nimview", ("Exception during request " + std::string(cRequest)).c_str()); 38 | } 39 | env->ReleaseStringUTFChars(request, cRequest); 40 | env->ReleaseStringUTFChars(value, cValue); 41 | return env->NewStringUTF(result.c_str()); 42 | } 43 | 44 | extern "C" JNIEXPORT void JNICALL 45 | Java_com_nimviewAndroid_CppWrapper_initCallFrontentJs(JNIEnv* env, jobject self) { 46 | nimview::setCustomJsEval(webviewEvalJs); 47 | myCppWrapper = &self; 48 | myJniVersion = env->GetVersion(); 49 | jclass javaClass = env->FindClass("com/nimviewAndroid/CppWrapper"); 50 | myEvaljsFunc = env->GetMethodID(javaClass, "evaluateJavascript", 51 | "(Ljava/lang/String;)V"); 52 | jint rs = env->GetJavaVM(&myJvm); 53 | assert (rs == JNI_OK); 54 | } 55 | -------------------------------------------------------------------------------- /examples/android/app/src/main/java/com/nimviewAndroid/CppWrapper.kt: -------------------------------------------------------------------------------- 1 | package com.nimviewAndroid 2 | import android.webkit.WebView 3 | import androidx.appcompat.app.AppCompatActivity 4 | import java.util.concurrent.Executors 5 | import org.json.JSONObject 6 | 7 | public class CppWrapper { 8 | private 9 | var myWebview: WebView 10 | var myMainActivity: AppCompatActivity 11 | val myNimThread = Executors.newFixedThreadPool(1) // always use the same thread for nim 12 | constructor(appView: WebView, mainActivity: AppCompatActivity) { 13 | this.myWebview = appView 14 | this.myMainActivity = mainActivity 15 | this.myNimThread.execute({ 16 | this.initCallFrontentJs() 17 | }) 18 | } 19 | /** 20 | * A native method that is implemented by the 'native-lib' native library, 21 | * which is packaged with this application. 22 | */ 23 | @SuppressWarnings("unused") 24 | external fun callNim(request: String, value: String): String 25 | 26 | external fun initCallFrontentJs() 27 | 28 | @SuppressWarnings("unused") 29 | fun evaluateJavascript(command: String) { 30 | this.myMainActivity.runOnUiThread(Runnable { 31 | this.myWebview.evaluateJavascript(command, null) 32 | }) 33 | } 34 | 35 | @SuppressWarnings("unused") 36 | @android.webkit.JavascriptInterface 37 | fun call(command: String) { 38 | try { 39 | val jsonMessage = JSONObject(command) 40 | val request = jsonMessage.getString("request") 41 | var data = jsonMessage.getString("data") 42 | var requestId = jsonMessage.getInt("requestId") 43 | this.myNimThread.execute({ 44 | var result = this.callNim(request, data) 45 | this.evaluateJavascript("window.ui.applyResponse(" + requestId.toString() + ",'" 46 | + result.replace("\\", "\\\\").replace("\'", "\\'") 47 | + "');") 48 | }) 49 | } 50 | catch (e: Exception) { 51 | println(e.toString()) 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /examples/android/app/src/main/java/com/nimviewAndroid/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.nimviewAndroid 2 | 3 | import android.os.Bundle 4 | import android.view.Window 5 | import android.webkit.JsResult 6 | import android.webkit.WebChromeClient 7 | import android.webkit.WebView 8 | import androidx.appcompat.app.AppCompatActivity 9 | 10 | 11 | class MainActivity : AppCompatActivity() { 12 | 13 | private lateinit var webView: WebView 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | this.requestWindowFeature(Window.FEATURE_NO_TITLE) 16 | super.onCreate(savedInstanceState) 17 | setContentView(R.layout.activity_main) 18 | this.getSupportActionBar()?.hide() 19 | 20 | webView = findViewById(R.id.webview) 21 | val settings = webView.getSettings() 22 | settings.setJavaScriptEnabled(true) 23 | settings.setDomStorageEnabled(true) 24 | settings.setAppCacheEnabled(true) 25 | var cppWrapper = CppWrapper(webView, this) 26 | webView.addJavascriptInterface(cppWrapper, "nimview") 27 | webView.webChromeClient = object : WebChromeClient() { 28 | // WebChromeClient needs workaround for "alert" 29 | override fun onJsAlert( 30 | view: WebView, 31 | url: String, 32 | message: String, 33 | result: JsResult 34 | ): Boolean { 35 | return super.onJsAlert(view, url, message, result) 36 | } 37 | } 38 | webView.loadUrl("file:///android_asset/index.html"); 39 | 40 | } 41 | 42 | companion object { 43 | // Used to load the 'native-lib' library on application startup. 44 | init { 45 | System.loadLibrary("native-lib") 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /examples/android/app/src/main/nim/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /public/build/ 3 | /public/vendor/ 4 | /dist/ 5 | 6 | .DS_Store 7 | storage.json -------------------------------------------------------------------------------- /examples/android/app/src/main/nim/App.nimble: -------------------------------------------------------------------------------- 1 | version = "0.2.0" 2 | author = "Marco Mengelkoch" 3 | description = "Nim / C library to run webview with HTML/JS as UI" 4 | license = "MIT" 5 | srcDir = "src" 6 | 7 | # Dependencies 8 | # you may skip jester, nimpy and webview when compiling with nim c -d:just_core 9 | # alternatively, you still can just skip webkit by compiling with -d:useServer 10 | 11 | # Currently, Webview requires gcc and doesn't work with vcc or clang 12 | 13 | requires "nimview >= 0.3.0", "nake >= 1.9.0" 14 | 15 | -------------------------------------------------------------------------------- /examples/android/app/src/main/nim/dist/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/android/app/src/main/nim/dist/favicon.png -------------------------------------------------------------------------------- /examples/android/app/src/main/nim/dist/global.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/android/app/src/main/nim/dist/global.css -------------------------------------------------------------------------------- /examples/android/app/src/main/nim/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Todo app 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/android/app/src/main/nim/nakefile.nim: -------------------------------------------------------------------------------- 1 | import nake 2 | import os, strutils, system, osproc 3 | 4 | const application = "App" 5 | const uiDir = "src" 6 | const mainApp = "src" / application & ".nim" 7 | const libraryFile = mainApp 8 | 9 | 10 | let thisDir = system.currentSourcePath().parentDir() 11 | let nimbleDir = parentDir(parentDir(os.findExe("nimble"))) 12 | var nimbaseDir = parentDir(nimbleDir) & "/lib" 13 | if (not os.fileExists(nimbaseDir & "/nimbase.h")): 14 | nimbaseDir = parentDir(parentDir(os.findExe("makelink"))) & "/lib" 15 | if (not os.fileExists(nimbaseDir & "/nimbase.h")): 16 | nimbaseDir = parentDir(parentDir(parentDir(parentDir(os.findExe("gcc"))))) & "/lib" 17 | if (not os.fileExists(nimbaseDir & "/nimbase.h")): 18 | nimbaseDir = parentDir(nimbleDir) & "/.choosenim/toolchains/nim-" & system.NimVersion & "/lib" 19 | 20 | var nimviewPath = thisDir.parentDir().parentDir().parentDir().parentDir().parentDir().parentDir() / "src" # only used for nimview.hpp 21 | if not os.dirExists(nimviewPath): 22 | var nimviewPathTmp = $ osproc.execProcess "nimble path nimview" 23 | nimviewPathTmp = nimviewPathTmp.replace("\n", "").replace("\\", "/").replace("//", "/") 24 | if (nimviewPathTmp != "" and os.dirExists(nimviewPathTmp)): 25 | nimviewPath = nimviewPathTmp 26 | 27 | proc execShCmd(command: string) = 28 | echo "running: " & command 29 | doAssert 0 == os.execShellCmd(command) 30 | 31 | proc buildCForArch(cpu, path: string) = 32 | let cppPath = "../cpp" / path 33 | let headerFile = cppPath / application & ".h" 34 | if (headerfile.needsRefresh(mainApp)): 35 | os.removeDir(cppPath) 36 | var stdOptions = "--header:" & application & ".h --app:staticlib -d:just_core -d:noSignalHandler -d:release -d:androidNDK -d:noMain --os:android --threads:on --debuginfo:on " # TODO: turn off debug infos 37 | execShCmd(nimexe & " cpp -c " & stdOptions & "--cpu:" & cpu & " --nimcache:" & cppPath & " " & mainApp) 38 | 39 | proc buildC() = 40 | ## creates python and C/C++ libraries 41 | buildCForArch("arm64", "arm64-v8a") 42 | buildCForArch("arm", "armeabi-v7a") 43 | buildCForArch("i386", "x86") 44 | buildCForArch("amd64", "x86_64") 45 | 46 | proc buildJs() = 47 | var src: seq[string] = @[] 48 | for path in walkDirRec(uiDir): 49 | if path.endsWith("js") or path.endsWith("svelte") or path.endsWith("jsx") or path.endsWith("vue"): 50 | src.add(path) 51 | if ((thisDir / "dist/build/bundle.js").needsRefresh(src)): 52 | execShCmd("npm install") 53 | execShCmd("npm run build") 54 | 55 | task "serve", "Serve NPM": 56 | doAssert 0 == os.execShellCmd("npm run serve") 57 | 58 | task "clean", "cleanup files": 59 | os.removeFile(thisDir / "dist/build/bundle.js") 60 | os.removeDir("../assets") 61 | os.removeDir("../cpp/arm64-v8a") 62 | os.removeDir("../cpp/armeabi-v7a") 63 | os.removeDir("../cpp/x86") 64 | os.removeDir("../cpp/x86_64") 65 | 66 | task defaultTask, "Compiles to C": 67 | os.copyFile(nimbaseDir / "nimbase.h", thisDir / "../cpp" / "nimbase.h") 68 | os.copyFile(nimviewPath / "nimview.hpp", thisDir / "../cpp" / "nimview.hpp") 69 | buildJs() 70 | buildC() -------------------------------------------------------------------------------- /examples/android/app/src/main/nim/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-todo", 3 | "version": "1.0.1", 4 | "description": "Sample application for Nimview", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/marcomq/nimview.git" 9 | }, 10 | "scripts": { 11 | "build": "rollup -c", 12 | "devbuild": "rollup -c", 13 | "dev": "rollup -c -w", 14 | "dev-ie": "(set USE_BABEL=1 || export USE_BABEL=1) && rollup -c -w", 15 | "start": "sirv public --no-clear" 16 | }, 17 | "devDependencies": { 18 | "@babel/core": "^7.15.5", 19 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 20 | "@babel/plugin-transform-runtime": "^7.15.0", 21 | "@babel/preset-env": "^7.15.4", 22 | "@babel/runtime": "^7.15.4", 23 | "@rollup/plugin-babel": "^5.3.0", 24 | "@rollup/plugin-commonjs": "^20.0.0", 25 | "@rollup/plugin-node-resolve": "^13.0.4", 26 | "@tauri-apps/tauri-inliner": "^1.13.2", 27 | "rollup": "^2.56.3", 28 | "rollup-plugin-css-only": "^3.1.0", 29 | "rollup-plugin-livereload": "^2.0.5", 30 | "rollup-plugin-svelte": "^7.1.0", 31 | "rollup-plugin-terser": "^7.0.2", 32 | "sirv-cli": "^1.0.14", 33 | "svelte": "^3.42.4" 34 | }, 35 | "dependencies": { 36 | "bootstrap": "^4.6.0", 37 | "core-js": "^3.17.2", 38 | "jquery": "^3.6.0", 39 | "nimview": "^1.1.3" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/android/app/src/main/nim/readme.md: -------------------------------------------------------------------------------- 1 | # nimview Android Svelte Todo-list Application 2 | 3 | This is the source code for the demo app on the Nimview page. 4 | To compile your code, you need nake 5 | - npm install 6 | - npm run build 7 | - nimble install nake 8 | - nake 9 | -------------------------------------------------------------------------------- /examples/android/app/src/main/nim/rollup.config.js: -------------------------------------------------------------------------------- 1 | import svelte from 'rollup-plugin-svelte' 2 | import resolve from '@rollup/plugin-node-resolve' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | import { babel } from '@rollup/plugin-babel' 5 | import livereload from 'rollup-plugin-livereload' 6 | import { terser } from 'rollup-plugin-terser' 7 | import css from 'rollup-plugin-css-only' 8 | 9 | 10 | const production = !process.env.ROLLUP_WATCH 11 | const useBabel = production || process.env.USE_BABEL 12 | 13 | export default { 14 | input: 'src/main.js', 15 | output: { 16 | sourcemap: true, 17 | format: 'iife', 18 | name: 'app', 19 | file: 'dist/build/bundle.js' 20 | }, 21 | plugins: [ 22 | svelte({ 23 | compilerOptions: { 24 | // enable run-time checks when not in production 25 | dev: !production 26 | } 27 | }), 28 | css({ output: 'bundle.css' }), 29 | // If you have external dependencies installed from 30 | // npm, you'll most likely need these plugins. In 31 | // some cases you'll need additional configuration 32 | // consult the documentation for details: 33 | // https://github.com/rollup/rollup-plugin-commonjs 34 | resolve({ 35 | browser: true, 36 | dedupe: ['svelte'] 37 | }), 38 | commonjs(), 39 | 40 | // Watch the `dist` directory and refresh the 41 | // browser on changes when not in production 42 | !production && livereload('dist'), 43 | // added by angelo 44 | // compile to good old IE11 compatible ES5 45 | useBabel && babel({ 46 | extensions: [ '.js', '.mjs', '.html', '.svelte' ], 47 | babelHelpers: 'runtime', 48 | exclude: [ 'node_modules/@babel/**', 'node_modules/core-js/**' ], 49 | presets: [ 50 | [ 51 | '@babel/preset-env', 52 | { 53 | targets: { 54 | ie: '11' 55 | }, 56 | useBuiltIns: 'usage', 57 | corejs: 3 58 | } 59 | ] 60 | ], 61 | plugins: [ 62 | '@babel/plugin-syntax-dynamic-import', 63 | [ 64 | '@babel/plugin-transform-runtime', 65 | { 66 | useESModules: true 67 | } 68 | ] 69 | ] 70 | }), 71 | 72 | // If we're building for production (npm run build 73 | // instead of npm run dev), minify 74 | production && terser() 75 | ], 76 | watch: { 77 | clearScreen: false 78 | } 79 | } -------------------------------------------------------------------------------- /examples/android/app/src/main/nim/src/App.nim: -------------------------------------------------------------------------------- 1 | import nimview, os 2 | 3 | var storageFile = "storage.json" 4 | var sdCard = os.getenv("EXTERNAL_STORAGE", "/sdcard") 5 | 6 | enableStorage(sdCard / storageFile) # adds getStoredVal and setStoredVal 7 | 8 | proc countDown() = 9 | callFrontendJs("alert", "waiting 6 seconds") 10 | sleep(2000) 11 | callFrontendJs("alert", "4") 12 | sleep(3000) 13 | callFrontendJs("alert", "1") 14 | sleep(1000) 15 | 16 | add("countDown", countDown) 17 | 18 | when not defined(just_core): 19 | start() -------------------------------------------------------------------------------- /examples/android/app/src/main/nim/src/App.svelte: -------------------------------------------------------------------------------- 1 | 48 |
49 | 54 |
55 |
56 |
57 |
58 |
59 | 60 |
61 | 62 |
63 |
64 |
65 | 66 | {#if items.length > 0} 67 | 82 | 96 | {/if} 97 |
98 |
99 |
100 |
101 | -------------------------------------------------------------------------------- /examples/android/app/src/main/nim/src/main.js: -------------------------------------------------------------------------------- 1 | import App from './App.svelte' 2 | import 'jquery/dist/jquery.js' 3 | import 'bootstrap/dist/js/bootstrap.bundle.js' 4 | import 'bootstrap/dist/css/bootstrap.css' 5 | 6 | var app 7 | document.addEventListener("DOMContentLoaded", function() { 8 | app = new App({ 9 | target: document.body, 10 | }) 11 | }) 12 | 13 | export default app -------------------------------------------------------------------------------- /examples/android/app/src/main/nim/src/nim.cfg: -------------------------------------------------------------------------------- 1 | --path:"../../../../../../../src/" -------------------------------------------------------------------------------- /examples/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /examples/android/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /examples/android/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 17 | -------------------------------------------------------------------------------- /examples/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /examples/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /examples/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6200EE 4 | #000000 5 | #03DAC5 6 | -------------------------------------------------------------------------------- /examples/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Nimview 3 | -------------------------------------------------------------------------------- /examples/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /examples/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | ext.kotlin_version = "1.4.10" 4 | repositories { 5 | google() 6 | jcenter() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.0.2' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | jcenter() 21 | } 22 | } 23 | 24 | task clean(type: Delete) { 25 | delete rootProject.buildDir 26 | } 27 | -------------------------------------------------------------------------------- /examples/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official -------------------------------------------------------------------------------- /examples/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Sep 13 14:49:58 CEST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /examples/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | rootProject.name = "nimviewAndroid" -------------------------------------------------------------------------------- /examples/c_cpp/App.nimble: -------------------------------------------------------------------------------- 1 | version = "0.2.0" 2 | author = "Marco Mengelkoch" 3 | description = "Nim / Python / C library to run webview with HTML/JS as UI" 4 | license = "MIT" 5 | bin = @["App"] 6 | srcDir = "src" 7 | 8 | import os, strutils 9 | # Dependencies 10 | 11 | requires "nimview >= 0.2.0", "nake >= 1.9.0" 12 | 13 | task test, "Run tests": 14 | let nake = system.findExe("nake") 15 | exec nake & " test" -------------------------------------------------------------------------------- /examples/c_cpp/App.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31205.134 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "App", "App.vcxproj", "{2BC9AA2C-3B77-4AC5-B2F8-6CA4C3A2F576}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | DebuggableRelease|x64 = DebuggableRelease|x64 12 | Release|x64 = Release|x64 13 | EndGlobalSection 14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 15 | {2BC9AA2C-3B77-4AC5-B2F8-6CA4C3A2F576}.Debug|x64.ActiveCfg = Debug|x64 16 | {2BC9AA2C-3B77-4AC5-B2F8-6CA4C3A2F576}.Debug|x64.Build.0 = Debug|x64 17 | {2BC9AA2C-3B77-4AC5-B2F8-6CA4C3A2F576}.DebuggableRelease|x64.ActiveCfg = DebuggableRelease|x64 18 | {2BC9AA2C-3B77-4AC5-B2F8-6CA4C3A2F576}.DebuggableRelease|x64.Build.0 = DebuggableRelease|x64 19 | {2BC9AA2C-3B77-4AC5-B2F8-6CA4C3A2F576}.Release|x64.ActiveCfg = Release|x64 20 | {2BC9AA2C-3B77-4AC5-B2F8-6CA4C3A2F576}.Release|x64.Build.0 = Release|x64 21 | EndGlobalSection 22 | GlobalSection(SolutionProperties) = preSolution 23 | HideSolutionNode = FALSE 24 | EndGlobalSection 25 | GlobalSection(ExtensibilityGlobals) = postSolution 26 | SolutionGuid = {2D94EFB1-0CE9-4595-8DDA-400A55396AE9} 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /examples/c_cpp/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Minimal Example 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/c_cpp/nakefile.nim: -------------------------------------------------------------------------------- 1 | import nake 2 | import os, strutils, system, osproc 3 | 4 | let library = "nimview" 5 | let headerFile = library & ".h" 6 | let srcDir = "src" 7 | let mainApp = srcDir / "library.nim" 8 | var srcFiles = @[mainApp] 9 | let buildDir = "out" 10 | let thisDir = system.currentSourcePath().parentDir() 11 | let cDllExtension = when defined(windows): "dll" else: "so" 12 | 13 | os.createDir buildDir 14 | 15 | const webviewIncludes = when defined(windows): 16 | "-DWEBVIEW_WINAPI=1 -mno-ms-bitfields -DWIN32_LEAN_AND_MEAN " 17 | elif defined(macosx): 18 | "-DWEBVIEW_COCOA=1 -x objective-c" 19 | else: 20 | "-DWEBVIEW_GTK=1 " & staticExec("pkg-config --cflags gtk+-3.0 webkit2gtk-4.0") 21 | 22 | const webviewlLibs = when defined(windows): 23 | "-lole32 -lcomctl32 -loleaut32 -luuid -lgdi32" 24 | elif defined(macosx): 25 | "-framework Cocoa -framework WebKit" 26 | else: 27 | system.staticExec("pkg-config --libs gtk+-3.0 webkit2gtk-4.0") & " -ldl -pthread" 28 | 29 | proc execShCmd(command: string) = 30 | echo "running: " & command 31 | doAssert 0 == os.execShellCmd(command) 32 | 33 | var nimbleDir = parentDir(parentDir(os.findExe("nimble"))) 34 | var nimbaseDir = parentDir(nimbleDir) & "/lib" 35 | if (not os.fileExists(nimbaseDir & "/nimbase.h")): 36 | nimbaseDir = parentDir(parentDir(os.findExe("makelink"))) & "/lib" 37 | if (not os.fileExists(nimbaseDir & "/nimbase.h")): 38 | nimbaseDir = parentDir(parentDir(parentDir(parentDir(os.findExe("gcc"))))) & "/lib" 39 | if (not os.fileExists(nimbaseDir & "/nimbase.h")): 40 | nimbaseDir = parentDir(nimbleDir) & "/.choosenim/toolchains/nim-" & system.NimVersion & "/lib" 41 | var nimviewPath = thisDir / "../../src/" 42 | if not os.dirExists(nimviewPath): 43 | var nimviewPathTmp = $ osproc.execProcess "nimble path nimview" 44 | nimviewPathTmp = nimviewPathTmp.replace("\n", "").replace("\\", "/").replace("//", "/") 45 | if (nimviewPathTmp != "" and os.dirExists(nimviewPathTmp)): 46 | nimviewPath = nimviewPathTmp 47 | 48 | if os.fileExists("dist/inlined.html"): 49 | srcFiles.add("dist/inlined.html") 50 | 51 | proc execNim(command: string) = 52 | echo "running: nim " & command 53 | execShCmd nimexe & " " & command 54 | 55 | proc buildGenericObjects() = 56 | os.removeDir(buildDir / "tmp_o") 57 | os.createDir(buildDir / "tmp_o") 58 | let headerFilePath = thisDir / buildDir / "tmp_c" / headerFile 59 | if headerFilePath.needsRefresh(srcFiles): 60 | os.removeDir(buildDir / "tmp_c") 61 | execNim "c -d:release --noMain:on -d:noMain --noLinking --header: " & headerFilePath & " --nimcache=./" & buildDir & 62 | "/tmp_c --app:staticLib --out:" & buildDir / library & " " & " " & mainApp 63 | os.copyFile(nimviewPath / "nimview.hpp", thisDir / buildDir / "tmp_c/nimview.hpp") 64 | 65 | proc buildCSample() = 66 | execShCmd "gcc -c -w -o " & buildDir & "/tmp_o/c_sample.o -fmax-errors=3 -DWEBVIEW_STATIC -DWEBVIEW_IMPLEMENTATION -O3 -fno-strict-aliasing -fno-ident " & 67 | webviewIncludes & " -I" & nimbaseDir & " -I" & nimbleDir & "/pkgs/webview-0.1.0/webview -I. -I" & buildDir & "/tmp_c src/c_sample.c" 68 | execShCmd "gcc -w -o " & buildDir & "/c_sample.exe " & buildDir & "/tmp_c/*.o " & buildDir & "/tmp_o/c_sample.o " & webviewlLibs 69 | 70 | proc buildCppSample() = 71 | execShCmd "g++ -c -w -std=c++17 -o " & buildDir & "/tmp_o/cpp_sample.o -fmax-errors=3 -DWEBVIEW_STATIC -DWEBVIEW_IMPLEMENTATION -O3 -fno-strict-aliasing -fno-ident " & 72 | webviewIncludes & " -I" & nimbaseDir & " -I" & nimbleDir & "/pkgs/webview-0.1.0/webview -I. -I" & buildDir & "/tmp_c src/cpp_sample.cpp" 73 | execShCmd "g++ -w -o " & buildDir & "/cpp_sample.exe " & buildDir & "/tmp_c/*.o " & buildDir & "/tmp_o/cpp_sample.o " & webviewlLibs 74 | 75 | proc buildDll() = 76 | ## C/C++ libraries 77 | let outputLib = buildDir / library & "." & cDllExtension 78 | if (thisDir / buildDir / "tmp_dll" / headerFile).needsRefresh(srcFiles): 79 | os.removeDir(buildDir / "tmp_dll") 80 | execNim "c --threads:on --gc:orc --passC:-fpic -d:release --noMain:on -d:noMain --nimcache=./" & buildDir & "/tmp_dll" & 81 | " --app:lib --noLinking:on --header:" & library & ".h --compileOnly:off " & " " & mainApp # creates header and compiled .o files 82 | os.copyFile(nimviewPath / "nimview.hpp", thisDir / buildDir / "tmp_dll/nimview.hpp") 83 | os.copyFile(thisDir / buildDir / "tmp_c" / headerFile, thisDir / buildDir / "tmp_dll" / headerFile) 84 | os.copyFile(nimbaseDir / "nimbase.h", thisDir / buildDir / "tmp_dll" / "nimbase.h") 85 | 86 | let minGwSymbols = when defined(windows): 87 | " -Wl,--out-implib," & buildDir & "/lib" & library & 88 | ".a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive " & buildDir & "/tmp_dll/*.o -Wl,--no-whole-archive " 89 | elif defined(linux): 90 | " -Wl,--out-implib," & buildDir & "/lib" & library & ".a -Wl,--whole-archive " & buildDir & "/tmp_dll/*.o -Wl,--no-whole-archive " 91 | else: 92 | " " & buildDir & "/tmp_dll/*.o " 93 | execShCmd "gcc -shared -o " & outputLib & " -I" & buildDir & "/tmp_dll/" & " " & minGwSymbols & webviewlLibs # generate .dll and .a 94 | echo "Shared C libraries build completed. Files have been created in '" & buildDir & "' folder." 95 | 96 | proc runTests() = 97 | buildGenericObjects() 98 | buildDll() 99 | buildCSample() 100 | if not defined(macosx): 101 | buildCppSample() 102 | 103 | proc generateDocs() = 104 | execNim "doc -o:docs/" & library & ".html " & mainApp 105 | 106 | task "docs", "Generate doc": 107 | generateDocs() 108 | 109 | task "libs", "Build Libs": 110 | buildGenericObjects() 111 | buildDll() 112 | 113 | task "c", "Build C sample": 114 | buildGenericObjects() 115 | buildCSample() 116 | 117 | task "cpp", "Build CPP sample": 118 | buildGenericObjects() 119 | buildCPPSample() 120 | 121 | task "clean", "clean all files": 122 | os.removeDir(buildDir) 123 | 124 | task "test", "Run tests": 125 | runTests() 126 | echo "all C/C++ tests passed" -------------------------------------------------------------------------------- /examples/c_cpp/readme.md: -------------------------------------------------------------------------------- 1 | # nimview C / CPP example 2 | 3 | Is using a "minimal precompiled html ui" and doesn't use npm. 4 | Uses "nake" to compile library and to compile C/C++ code. 5 | 6 | Requires to have gcc available in PATH. 7 | 8 | Usage: 9 | - nake libs: This will compile Dlls / so files to use Nimview as library, for example 10 | to use it from Visual Studio 11 | - nake cpp: Compiles C++ Example 12 | - nae c: Compiles C Example 13 | 14 | Before using the VS project file, you need to run "nake libs". 15 | As Webview doesn't support clang or MSVC yet, you need to use a DLL of Nimview 16 | when using Visual Studio. -------------------------------------------------------------------------------- /examples/c_cpp/src/c_sample.c: -------------------------------------------------------------------------------- 1 | /** Nimview UI Library 2 | * Copyright (C) 2020, 2021, by Marco Mengelkoch 3 | * Licensed under MIT License, see License file for more details 4 | * git clone https://github.com/marcomq/nimview 5 | **/ 6 | // Important Notice: You should use --threads:on AND you need to avoid --gc:arc ; I had crashes on windows otherwise with NIM 1.4 when starting webview 7 | 8 | #include "nimview.h" 9 | #include 10 | #include 11 | 12 | char* echoAndModify(char* something) { 13 | const char* appendString = " modified by C"; 14 | char* result = malloc(strlen(something) + strlen(appendString) + 1); // +1 for the null-terminator, strlen is unchecked! "something" needs 0 termination 15 | if (result) { 16 | strcpy(result, something); // safe, result just created 17 | strcat(result, appendString); // safe, result just created with len 18 | } 19 | else { 20 | return ""; // "" will not be freed 21 | } 22 | return result; 23 | } 24 | int main(int argc, char* argv[]) { 25 | printf(" starting c code\n"); 26 | NimMain(); 27 | nimview_add_cstring_rstr("echoAndModify", echoAndModify, free); 28 | #ifdef _DEBUG 29 | nimview_startHttpServer("../dist/index.html", 8000, "localhost", 1); 30 | #else 31 | nimview_startDesktop("../dist/index.html", "c_sample", 640, 480, 1, 0, 1); 32 | #endif 33 | } 34 | -------------------------------------------------------------------------------- /examples/c_cpp/src/cpp_sample.cpp: -------------------------------------------------------------------------------- 1 | /** Nimview UI Library 2 | * Copyright (C) 2020, 2021, by Marco Mengelkoch 3 | * Licensed under MIT License, see License file for more details 4 | * git clone https://github.com/marcomq/nimview 5 | **/ 6 | 7 | #include 8 | #include 9 | #include "nimview.hpp" 10 | 11 | 12 | std::string echoAndModify(const std::string& something) { 13 | return (something + " appended to string"); 14 | } 15 | 16 | int main(int argc, char* argv[]) { 17 | nimview::nimMain(); 18 | nimview::add("echoAndModify", echoAndModify); 19 | nimview::start("../dist/index.html", 8000, "localhost"); 20 | } -------------------------------------------------------------------------------- /examples/c_cpp/src/library.nim: -------------------------------------------------------------------------------- 1 | import nimview 2 | when isMainModule and system.appType != "lib" and not defined noMain: 3 | start() -------------------------------------------------------------------------------- /examples/c_cpp/src/nim.cfg: -------------------------------------------------------------------------------- 1 | --path:"../../../src/" 2 | --threads:on -------------------------------------------------------------------------------- /examples/minimal/App.nim: -------------------------------------------------------------------------------- 1 | import nimview 2 | 3 | proc echoAndModify(value: string): string = 4 | result = "'" & value & "' modified by minimal" 5 | 6 | when isMainModule: 7 | add("echoAndModify", echoAndModify) 8 | start("dist/index.html") -------------------------------------------------------------------------------- /examples/minimal/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Minimal Example 5 | 6 | 7 | 8 | 9 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/minimal/nim.cfg: -------------------------------------------------------------------------------- 1 | --path: "../../src/" -------------------------------------------------------------------------------- /examples/minimal2/App.nim: -------------------------------------------------------------------------------- 1 | import nimview 2 | import os 3 | 4 | proc callJsProgress() = 5 | ## just simulating progress 6 | for i in 0..100: 7 | callJs("applyProgress", $(i) & "%") 8 | os.sleep(20) 9 | 10 | proc echoAndModify(value: string): string = 11 | result = "'" & value & "' modified by minimal" 12 | 13 | when isMainModule: 14 | add("callJsProgress", callJsProgress) 15 | add("echoAndModify", echoAndModify) 16 | startDesktop("dist/index.html", debug=true) 17 | -------------------------------------------------------------------------------- /examples/minimal2/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Minimal Example 5 | 6 | 7 | 8 | 9 | 16 |
17 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /examples/minimal2/nim.cfg: -------------------------------------------------------------------------------- 1 | --path:"../../src/" 2 | --threads:on 3 | --gc:orc -------------------------------------------------------------------------------- /examples/python/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include nakefile.nim src/library.nim nimview.pyd nimview.so -------------------------------------------------------------------------------- /examples/python/README.md: -------------------------------------------------------------------------------- 1 | ## Nimview Python sample application 2 | 3 | This is using nakefiles to build the python library 4 | - nake pyLib 5 | 6 | The pyTest.py doesn't trigger start yet - so it doesn't opens a GUI yet. 7 | Currently only used for testing 8 | 9 | building 10 | nake pyLib 11 | python bdist_wheel.py bdist_wheel 12 | python setup.py sdist 13 | python -m twine upload --repository pypi .\dist\nimview-0.3.3.* -------------------------------------------------------------------------------- /examples/python/bdist_wheel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # nake pyLib 5 | # python bdist_wheel.py bdist_wheel --plat-name win-amd64 6 | # /opt/python/cp37-cp37m/bin/python bdist_wheel.py bdist_wheel --plat-name linux-x86_64 7 | # auditwheel repair --plat manylinux2014_x86_64 dist/nimview-0.*.*-py3-none-linux_x86_64.whl 8 | from setuptools import setup, Distribution 9 | import os 10 | from shutil import copy, rmtree 11 | 12 | this_directory = os.path.abspath(os.path.dirname(__file__)) 13 | targetDir = "nimview" 14 | rmtree(targetDir, ignore_errors=True) 15 | os.makedirs(targetDir, exist_ok=True) 16 | if os.name == 'nt': 17 | fileName = "out/nimview.pyd" 18 | package = ["nimview.pyd"] 19 | else: 20 | fileName = "out/nimview.so" 21 | package = ["nimview.so"] 22 | fullFileName = os.path.join(this_directory, fileName) 23 | if os.path.isfile(fullFileName): 24 | print("copy " + fullFileName + " => " + targetDir) 25 | copy(fullFileName, targetDir) 26 | 27 | with open(targetDir + "/__init__.py", "w") as text_file: 28 | text_file.write("from nimview.nimview import *") 29 | 30 | class BinaryDistribution(Distribution): 31 | """Distribution which always forces a binary package with platform name""" 32 | def has_ext_modules(self): 33 | return False 34 | 35 | setup( 36 | distclass=BinaryDistribution, 37 | package_data={ 38 | "nimview": package 39 | } 40 | ) 41 | 42 | -------------------------------------------------------------------------------- /examples/python/nakefile.nim: -------------------------------------------------------------------------------- 1 | import nake 2 | import os, strutils, system 3 | 4 | let library = "nimview" 5 | let srcDir = "src" 6 | let mainApp = srcDir / "library.nim" 7 | let srcFiles = [mainApp] 8 | let buildDir = "out" 9 | let thisDir = system.currentSourcePath().parentDir() 10 | 11 | os.createDir buildDir 12 | 13 | proc execCmd(command: string) = 14 | echo "running: " & command 15 | doAssert 0 == os.execShellCmd(command) 16 | 17 | proc execNim(command: string) = 18 | echo "running: nim " & command 19 | execCmd nimexe & " " & command 20 | 21 | let pyDllExtension = when defined(windows): "pyd" else: "so" 22 | 23 | proc buildPyLib() = 24 | ## creates python lib 25 | let outputLib = buildDir / library & "." & pyDllExtension 26 | if outputLib.needsRefresh(srcFiles): 27 | os.removeDir(buildDir / "tmp_py") 28 | let winParams = when defined(windows): " --tlsEmulation:off --passL:-static " else: " " 29 | execNim "c -d:release -d:noMain --threads:on --gc:orc --deepCopy:on --app:lib --nimcache=./" & buildDir & "/tmp_py --out:" & outputLib & 30 | " " & winParams & mainApp & " " # creates python lib, header file not usable 31 | os.copyFile(outputLib, thisDir / "src" / library & "." & pyDllExtension) 32 | 33 | proc runTests() = 34 | buildPyLib() 35 | try: 36 | os.copyFile(buildDir / library & "." & pyDllExtension, thisDir / "../../tests" / library & "." & pyDllExtension) 37 | except: 38 | discard 39 | execCmd "python src/pyTest.py" 40 | 41 | proc cleanUp() = 42 | os.removeDir(buildDir) 43 | 44 | task "clean", "clean all files": 45 | cleanUp() 46 | 47 | task "pyLib", "Build python lib": 48 | buildPyLib() 49 | 50 | task "test", "Run tests": 51 | cleanUp() 52 | runTests() 53 | echo "all python tests passed" -------------------------------------------------------------------------------- /examples/python/pySample.py: -------------------------------------------------------------------------------- 1 | # Nimview UI Library 2 | # Copyright (C) 2020, 2021, by Marco Mengelkoch 3 | # Licensed under MIT License, see License file for more details 4 | 5 | # pylint: disable=import-error 6 | import __init__, nimview, json 7 | def echoAndModify(value): 8 | print (value) 9 | return json.dumps({"val": value + " appended by python"}) 10 | 11 | def echoAndModify2(): 12 | return json.dumps({"no val appended by python"}) 13 | 14 | nimview.setUseServer(True) 15 | nimview.addRequest("echoAndModify", echoAndModify) 16 | nimview.start("minimal_ui_sample/index.html") # current dir needs to be relative to this -------------------------------------------------------------------------------- /examples/python/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = nimview 3 | version = 0.4.2 4 | author = Marco Mengelkoch 5 | author_email = MMengelkoch@gmx.de 6 | description = A lightwight cross platform UI library for Nim, C, C++ or Python. The main purpose is to simplify creation of Desktop applications based on a HTML/CSS/JS layer that is displayed with Webview. 7 | long_description = file: src/README.md, src/LICENSE 8 | long_description_content_type = text/markdown 9 | keywords = nim, user-interface, webview, html, css, javascript, http-server 10 | url = https://github.com/marcomq/nimview 11 | license = MIT 12 | classifiers = 13 | Development Status :: 4 - Beta 14 | Natural Language :: English 15 | Operating System :: POSIX :: Linux 16 | Operating System :: Microsoft :: Windows 17 | Operating System :: MacOS :: MacOS X 18 | Environment :: Console 19 | Environment :: Other Environment 20 | Intended Audience :: Developers 21 | Programming Language :: Python 22 | Programming Language :: Python :: 3 23 | Programming Language :: Python :: 3 :: Only 24 | Programming Language :: Python :: 3.7 25 | Programming Language :: Python :: 3.8 26 | Programming Language :: Python :: 3.9 27 | Topic :: Software Development 28 | License :: OSI Approved :: MIT License 29 | 30 | [options] 31 | packages = nimview -------------------------------------------------------------------------------- /examples/python/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # python setup.py sdist 5 | from setuptools import setup, Extension 6 | from setuptools.command.build_ext import build_ext 7 | from subprocess import check_call 8 | import os 9 | from shutil import copy, rmtree 10 | 11 | this_directory = os.path.abspath(os.path.dirname(__file__)) 12 | targetDir = "nimview" 13 | 14 | # create another nimview subfolder as setup.py is much friendlier if you do so 15 | rmtree(targetDir, ignore_errors=True) 16 | os.makedirs(targetDir, exist_ok=True) 17 | os.makedirs(targetDir + "/src", exist_ok=True) 18 | srcFiles = [ "src/library.nim", "nakefile.nim", "LICENSE", "README.md"] 19 | for index, fileName in enumerate(srcFiles): 20 | fullFileName = os.path.join(this_directory, fileName) 21 | if os.path.isfile(fullFileName): 22 | copy(fullFileName, targetDir + "/" + fileName) 23 | 24 | 25 | class NimExtension(Extension): 26 | def __init__(self, name, sourcedir=''): 27 | Extension.__init__(self, name, sources=[]) 28 | self.sourcedir = os.path.abspath(sourcedir) 29 | 30 | class NimBuild(build_ext): 31 | def run(self): 32 | for ext in self.extensions: 33 | self.build_extension(ext) 34 | 35 | def build_extension(self, ext): 36 | print("=> build_extension") 37 | os.makedirs(self.build_temp, exist_ok=True) 38 | os.makedirs(self.build_temp + "/src", exist_ok=True) 39 | 40 | extdir = self.get_ext_fullpath(ext.name) 41 | os.makedirs(extdir + "/src", exist_ok=True) 42 | 43 | for fileName in srcFiles: 44 | fullFileName = os.path.join(targetDir, fileName) 45 | if os.path.isfile(fullFileName): 46 | target = self.build_temp + "/" + fileName 47 | print("copy " + fullFileName + " => " + target) 48 | copy(fullFileName, target) 49 | 50 | check_call(['nimble', 'install', 'nimview', '-dy'], cwd=self.build_temp) 51 | print("=> dependencies installed") 52 | check_call(['nake', 'pyLib'], cwd=self.build_temp, shell=True) 53 | print("=> pyLib created") 54 | libFiles = [ "out/nimview.so", "out/nimview.pyd"] 55 | install_target = os.path.abspath(os.path.dirname(extdir)) 56 | os.makedirs(install_target + "/src", exist_ok=True) 57 | 58 | for fileName in libFiles: 59 | fullFileName = os.path.join(self.build_temp, fileName) 60 | if os.path.isfile(fullFileName): 61 | print("copy " + fullFileName + " => " + install_target) 62 | copy(fullFileName, install_target) 63 | 64 | setup( 65 | ext_modules=[NimExtension('.')], 66 | cmdclass={ 67 | 'build_ext': NimBuild, 68 | }, 69 | package_data={ 70 | "nimview": srcFiles + ["nimview.so", "nimview.pyd"] 71 | }, 72 | install_requires=[ 73 | "choosenim_install" # Auto-installs Nim compiler 74 | ] 75 | ) 76 | -------------------------------------------------------------------------------- /examples/python/src/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 marcomq 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/python/src/__init__.py: -------------------------------------------------------------------------------- 1 | import sys, os, pathlib 2 | # print(pathlib.Path(os.path.abspath(__file__)).parent.parent) 3 | sys.path.append(str(pathlib.Path(os.path.abspath(__file__)).parent.parent) + "/out") 4 | import nimview -------------------------------------------------------------------------------- /examples/python/src/library.nim: -------------------------------------------------------------------------------- 1 | import nimview 2 | when isMainModule and system.appType != "lib" and not defined noMain: 3 | start() -------------------------------------------------------------------------------- /examples/python/src/nim.cfg: -------------------------------------------------------------------------------- 1 | --path:"../../../src/" 2 | --threads:on -------------------------------------------------------------------------------- /examples/python/src/pyTest.py: -------------------------------------------------------------------------------- 1 | # Nimview UI Library 2 | # Copyright (C) 2020, 2021, by Marco Mengelkoch 3 | # Licensed under MIT License, see License file for more details 4 | 5 | # pylint: disable=import-error 6 | import nimview 7 | def echoAndModify(value): 8 | print (value) 9 | return (value + " appended by python") 10 | 11 | def echoAndModify2(): 12 | print ("received nil") 13 | return (" appended by python") 14 | 15 | def echoAndModify3(value1, value2): 16 | result = value1 + " "+ value2 + " both received" 17 | print (result) 18 | return (result + " by python") 19 | 20 | def stopNimview(value): 21 | nimview.stop() 22 | return "" 23 | 24 | nimview.addRequest("echoAndModify", echoAndModify) 25 | nimview.addRequest("echoAndModify2", echoAndModify2) 26 | nimview.addRequest("echoAndModify3", echoAndModify3) 27 | nimview.addRequest("stopNimview", stopNimview) 28 | 29 | nimview.dispatchCommandLineArg("{\"request\":\"echoAndModify\",\"data\":[\"this is a test\"],\"responseId\":0}") 30 | print ("[start] Expecting an error") 31 | nimview.dispatchCommandLineArg("{\"request\":\"echoAndModify\",\"data\":[],\"responseId\":3}") # will cause an error 32 | print ("[end] Not expecting an error anymore in python test") 33 | nimview.dispatchCommandLineArg("{\"request\":\"echoAndModify2\",\"data\":[],\"responseId\":4}") 34 | nimview.dispatchCommandLineArg("{\"request\":\"echoAndModify3\",\"data\":[\"first\",\"second\"],\"responseId\":5}") 35 | nimview.dispatchCommandLineArg("{\"request\":\"stopNimview\",\"data\":\"\",\"responseId\":6}") 36 | print ("python test passed") 37 | # nimview.startDesktop("tests/minimal_ui_sample/index.html") 38 | -------------------------------------------------------------------------------- /examples/react/.env: -------------------------------------------------------------------------------- 1 | BUILD_PATH=./dist -------------------------------------------------------------------------------- /examples/react/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /dist 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /examples/react/README.md: -------------------------------------------------------------------------------- 1 | # Nimview react application 2 | To compile your code, just run 3 | - npm install 4 | - npm run build 5 | - nim c -r -d:release --app:gui src/App.nim 6 | - (or, if you want to have an async count down button: `nim c -r -d:release --gc:orc --threads:on --app:gui src/App.nim`) 7 | 8 | The code was mostly generated with `create-react-app` as described in 9 | https://reactjs.org/docs/create-a-new-react-app.html 10 | 11 | It was modified to run with Nimview. It was not optimized yet to be good for development 12 | or production. You probably need to modify this code by yourself to add auto 13 | reload capabilities etc... 14 | 15 | 16 | 17 | Here is the auto-generated readme from react: 18 | 19 | # Getting Started with Create React App 20 | 21 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 22 | 23 | ## Available Scripts 24 | 25 | In the project directory, you can run: 26 | 27 | ### `npm start` 28 | 29 | Runs the app in the development mode.\ 30 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 31 | 32 | The page will reload when you make changes.\ 33 | You may also see any lint errors in the console. 34 | 35 | ### `npm test` 36 | 37 | Launches the test runner in the interactive watch mode.\ 38 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 39 | 40 | ### `npm run build` 41 | 42 | Builds the app for production to the `build` folder.\ 43 | It correctly bundles React in production mode and optimizes the build for the best performance. 44 | 45 | The build is minified and the filenames include the hashes.\ 46 | Your app is ready to be deployed! 47 | 48 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 49 | 50 | ### `npm run eject` 51 | 52 | **Note: this is a one-way operation. Once you `eject`, you can't go back!** 53 | 54 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 55 | 56 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. 57 | 58 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 59 | 60 | ## Learn More 61 | 62 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 63 | 64 | To learn React, check out the [React documentation](https://reactjs.org/). 65 | 66 | ### Code Splitting 67 | 68 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 69 | 70 | ### Analyzing the Bundle Size 71 | 72 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 73 | 74 | ### Making a Progressive Web App 75 | 76 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 77 | 78 | ### Advanced Configuration 79 | 80 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 81 | 82 | ### Deployment 83 | 84 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 85 | 86 | ### `npm run build` fails to minify 87 | 88 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 89 | -------------------------------------------------------------------------------- /examples/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-nimview-example", 3 | "version": "0.1.0", 4 | "homepage": "./", 5 | "proxy": "http://localhost:8000", 6 | "private": true, 7 | "dependencies": { 8 | "bootstrap": "^4.6.0", 9 | "jquery": "^3.6.0", 10 | "nimview": "^1.1.3", 11 | "react": "^17.0.2", 12 | "react-app-polyfill": "^3.0.0", 13 | "react-dom": "^17.0.2", 14 | "react-scripts": "5.0.0" 15 | }, 16 | "devDependencies": { 17 | "@testing-library/jest-dom": "^5.16.1", 18 | "@testing-library/react": "^12.1.2", 19 | "@testing-library/user-event": "^13.5.0", 20 | "@tauri-apps/tauri-inliner": "^1.13.2", 21 | "web-vitals": "^2.1.2" 22 | }, 23 | "scripts": { 24 | "start": "react-scripts start", 25 | "dev": "react-scripts start", 26 | "build": "react-scripts build && inliner -m --videos --preserve-comments dist/index.html > dist/inlined.html" 27 | }, 28 | "eslintConfig": { 29 | "extends": [ 30 | "react-app", 31 | "react-app/jest" 32 | ] 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | "> 1%", 37 | "last 2 versions", 38 | "IE 11" 39 | ], 40 | "development": [ 41 | "last 1 chrome version", 42 | "last 1 firefox version", 43 | "last 1 safari version" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/react/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/react/public/favicon.ico -------------------------------------------------------------------------------- /examples/react/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/react/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /examples/react/src/App.jsx: -------------------------------------------------------------------------------- 1 | import Navbar from './components/Navbar' 2 | import Sample from './components/Sample' 3 | 4 | function App() { 5 | return ( 6 |
7 | 8 | 9 |
10 | ) 11 | } 12 | 13 | export default App 14 | -------------------------------------------------------------------------------- /examples/react/src/App.nim: -------------------------------------------------------------------------------- 1 | import nimview 2 | import os 3 | 4 | proc appendSomething(value: string): string {.noSideEffect.} = 5 | result = "'" & value & "' modified by react sample" 6 | 7 | proc countDown() = 8 | callJs("alert", "waiting 6 seconds") 9 | sleep(2000) 10 | callJs("alert", "4") 11 | sleep(3000) 12 | callJs("alert", "1") 13 | sleep(1000) 14 | 15 | proc main() = 16 | add("appendSomething", appendSomething) 17 | add("countDown", countDown) 18 | start() 19 | 20 | when isMainModule: 21 | main() -------------------------------------------------------------------------------- /examples/react/src/components/Navbar.jsx: -------------------------------------------------------------------------------- 1 | export default function Navbar() { 2 | return
3 | 23 |
24 | } -------------------------------------------------------------------------------- /examples/react/src/components/Sample.jsx: -------------------------------------------------------------------------------- 1 | import backend from "nimview" 2 | import React from "react" 3 | 4 | class Sample extends React.Component { 5 | constructor(props) { 6 | super(props) 7 | this.state = { 8 | search: '', 9 | elements: [] 10 | } 11 | } 12 | renderItems = () => { 13 | const elementItems = this.state.elements.map((item) => 14 |
{item.val}
15 | ) 16 | return elementItems 17 | } 18 | 19 | countDown = () => { 20 | backend.countDown().then(() => { 21 | alert(0) 22 | }) 23 | } 24 | 25 | runSearch = () => { 26 | const newElements = this.state.elements 27 | newElements.push({val: this.state.search}) 28 | backend.appendSomething(this.state.search).then((resp) => { 29 | this.setState({search: resp, elements: newElements}) 30 | }) // calling the backend 31 | } 32 | 33 | updateSearch = (event) => { 34 | this.setState({search: event.target.value}) 35 | } 36 | 37 | render() { 38 | return
39 |
  • 40 |
    41 | 42 | 43 | 44 |
    45 |
  • 46 |
    {this.renderItems()}
    47 |
    48 | } 49 | } 50 | export default Sample 51 | -------------------------------------------------------------------------------- /examples/react/src/index.js: -------------------------------------------------------------------------------- 1 | import 'react-app-polyfill/ie11' 2 | import React from 'react' 3 | import ReactDOM from 'react-dom' 4 | import App from './App' 5 | import 'jquery/dist/jquery.js' 6 | import 'bootstrap/dist/js/bootstrap.bundle.js' 7 | import 'bootstrap/dist/css/bootstrap.css' 8 | 9 | document.addEventListener("DOMContentLoaded", function() { 10 | ReactDOM.render( 11 | 12 | 13 | , 14 | document.getElementById('app') 15 | ) 16 | }) -------------------------------------------------------------------------------- /examples/react/src/nim.cfg: -------------------------------------------------------------------------------- 1 | --path:"../../../src/" -------------------------------------------------------------------------------- /examples/svelte/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /public/build/ 3 | /public/vendor/ 4 | /dist/ 5 | 6 | .DS_Store 7 | storage.json -------------------------------------------------------------------------------- /examples/svelte/dist/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/svelte/dist/favicon.png -------------------------------------------------------------------------------- /examples/svelte/dist/global.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/svelte/dist/global.css -------------------------------------------------------------------------------- /examples/svelte/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Svelte app 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/svelte/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-app", 3 | "version": "1.0.1", 4 | "description": "Sample application for Nimview", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/marcomq/nimview.git" 9 | }, 10 | "scripts": { 11 | "build": "rollup -c && inliner -m --videos --preserve-comments dist/index.html > dist/inlined.html", 12 | "devbuild": "rollup -c", 13 | "dev": "rollup -c -w", 14 | "dev-ie": "(set USE_BABEL=1 || export USE_BABEL=1) && rollup -c -w", 15 | "start": "sirv public --no-clear" 16 | }, 17 | "devDependencies": { 18 | "@babel/core": "^7.15.5", 19 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 20 | "@babel/plugin-transform-runtime": "^7.15.0", 21 | "@babel/preset-env": "^7.15.4", 22 | "@babel/runtime": "^7.15.4", 23 | "@rollup/plugin-babel": "^5.3.0", 24 | "@rollup/plugin-commonjs": "^20.0.0", 25 | "@rollup/plugin-node-resolve": "^13.0.4", 26 | "@tauri-apps/tauri-inliner": "^1.13.2", 27 | "rollup": "^2.56.3", 28 | "rollup-plugin-css-only": "^3.1.0", 29 | "rollup-plugin-livereload": "^2.0.5", 30 | "rollup-plugin-svelte": "^7.1.0", 31 | "rollup-plugin-terser": "^7.0.2", 32 | "sirv-cli": "^1.0.14", 33 | "svelte": "^3.42.4" 34 | }, 35 | "dependencies": { 36 | "bootstrap": "^4.6.0", 37 | "core-js": "^3.17.2", 38 | "jquery": "^3.6.0", 39 | "nimview": "^1.1.3" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/svelte/readme.md: -------------------------------------------------------------------------------- 1 | # nimview Svelte Application 2 | 3 | To compile your code, just run 4 | - npm install 5 | - npm run build 6 | - nim c -r -d:release --app:gui src/App.nim 7 | - (or, if you want to have an async count down button: `nim c -r -d:release --gc:orc --threads:on --app:gui src/App.nim`) 8 | -------------------------------------------------------------------------------- /examples/svelte/rollup.config.js: -------------------------------------------------------------------------------- 1 | import svelte from 'rollup-plugin-svelte' 2 | import resolve from '@rollup/plugin-node-resolve' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | import { babel } from '@rollup/plugin-babel' 5 | import livereload from 'rollup-plugin-livereload' 6 | import { terser } from 'rollup-plugin-terser' 7 | import css from 'rollup-plugin-css-only' 8 | 9 | 10 | const production = !process.env.ROLLUP_WATCH 11 | const useBabel = production || process.env.USE_BABEL 12 | 13 | export default { 14 | input: 'src/main.js', 15 | output: { 16 | sourcemap: true, 17 | format: 'iife', 18 | name: 'app', 19 | file: 'dist/build/bundle.js' 20 | }, 21 | plugins: [ 22 | svelte({ 23 | compilerOptions: { 24 | // enable run-time checks when not in production 25 | dev: !production 26 | } 27 | }), 28 | css({ output: 'bundle.css' }), 29 | // If you have external dependencies installed from 30 | // npm, you'll most likely need these plugins. In 31 | // some cases you'll need additional configuration 32 | // consult the documentation for details: 33 | // https://github.com/rollup/rollup-plugin-commonjs 34 | resolve({ 35 | browser: true, 36 | dedupe: ['svelte'] 37 | }), 38 | commonjs(), 39 | 40 | // Watch the `dist` directory and refresh the 41 | // browser on changes when not in production 42 | !production && livereload('dist'), 43 | // added by angelo 44 | // compile to good old IE11 compatible ES5 45 | useBabel && babel({ 46 | extensions: [ '.js', '.mjs', '.html', '.svelte' ], 47 | babelHelpers: 'runtime', 48 | exclude: [ 'node_modules/@babel/**', 'node_modules/core-js/**' ], 49 | presets: [ 50 | [ 51 | '@babel/preset-env', 52 | { 53 | targets: { 54 | ie: '11' 55 | }, 56 | useBuiltIns: 'usage', 57 | corejs: 3 58 | } 59 | ] 60 | ], 61 | plugins: [ 62 | '@babel/plugin-syntax-dynamic-import', 63 | [ 64 | '@babel/plugin-transform-runtime', 65 | { 66 | useESModules: true 67 | } 68 | ] 69 | ] 70 | }), 71 | 72 | // If we're building for production (npm run build 73 | // instead of npm run dev), minify 74 | production && terser() 75 | ], 76 | watch: { 77 | clearScreen: false 78 | } 79 | } -------------------------------------------------------------------------------- /examples/svelte/src/App.nim: -------------------------------------------------------------------------------- 1 | import nimview 2 | import os 3 | 4 | proc appendSomething(value: string): string {.noSideEffect.} = 5 | result = "'" & value & "' modified by svelte sample" 6 | 7 | proc countDown() = 8 | callJs("alert", "waiting 6 seconds") 9 | sleep(2000) 10 | callJs("alert", "4") 11 | sleep(3000) 12 | callJs("alert", "1") 13 | sleep(1000) 14 | 15 | proc main() = 16 | add("appendSomething", appendSomething) 17 | add("countDown", countDown) 18 | start() 19 | ## alternative fullscreen mode: 20 | # start(run=false) 21 | # setFullscreen(true) 22 | # run() 23 | 24 | when isMainModule: 25 | main() -------------------------------------------------------------------------------- /examples/svelte/src/App.svelte: -------------------------------------------------------------------------------- 1 | 5 |
    6 |
    7 | 8 | 9 |
    10 |
    -------------------------------------------------------------------------------- /examples/svelte/src/components/Navbar.svelte: -------------------------------------------------------------------------------- 1 |
    2 | 27 |
    -------------------------------------------------------------------------------- /examples/svelte/src/components/Sample.svelte: -------------------------------------------------------------------------------- 1 | 19 |
    20 |
  • 21 |
    22 | 23 | 24 | 25 |
    26 |
  • 27 |
    28 | {#if ((typeof elements !== "undefined") && (elements.length > 0))} 29 | {#each elements as el} 30 |
    31 | {el.text} 32 |
    33 | {/each} 34 | {:else} 35 |

    No data available yet

    36 | {/if} 37 |
    38 |
    39 | 40 | -------------------------------------------------------------------------------- /examples/svelte/src/main.js: -------------------------------------------------------------------------------- 1 | import App from './App.svelte' 2 | import 'jquery/dist/jquery.js' 3 | import 'bootstrap/dist/js/bootstrap.bundle.js' 4 | import 'bootstrap/dist/css/bootstrap.css' 5 | 6 | var app 7 | document.addEventListener("DOMContentLoaded", function() { 8 | app = new App({ 9 | target: document.body, 10 | }) 11 | }) 12 | 13 | export default app -------------------------------------------------------------------------------- /examples/svelte/src/nim.cfg: -------------------------------------------------------------------------------- 1 | --path:"../../../src/" -------------------------------------------------------------------------------- /examples/svelte_todo/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /public/build/ 3 | /public/vendor/ 4 | /dist/ 5 | 6 | .DS_Store 7 | storage.json -------------------------------------------------------------------------------- /examples/svelte_todo/dist/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/svelte_todo/dist/favicon.png -------------------------------------------------------------------------------- /examples/svelte_todo/dist/global.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/svelte_todo/dist/global.css -------------------------------------------------------------------------------- /examples/svelte_todo/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Todo app 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/svelte_todo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-todo", 3 | "version": "1.0.1", 4 | "description": "Sample application for Nimview", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/marcomq/nimview.git" 9 | }, 10 | "scripts": { 11 | "build": "rollup -c && inliner -m --videos --preserve-comments dist/index.html > dist/inlined.html", 12 | "devbuild": "rollup -c", 13 | "dev": "rollup -c -w", 14 | "dev-ie": "(set USE_BABEL=1 || export USE_BABEL=1) && rollup -c -w", 15 | "start": "sirv public --no-clear" 16 | }, 17 | "devDependencies": { 18 | "@babel/core": "^7.15.5", 19 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 20 | "@babel/plugin-transform-runtime": "^7.15.0", 21 | "@babel/preset-env": "^7.15.4", 22 | "@babel/runtime": "^7.15.4", 23 | "@rollup/plugin-babel": "^5.3.0", 24 | "@rollup/plugin-commonjs": "^20.0.0", 25 | "@rollup/plugin-node-resolve": "^13.0.4", 26 | "@tauri-apps/tauri-inliner": "^1.13.2", 27 | "rollup": "^2.56.3", 28 | "rollup-plugin-css-only": "^3.1.0", 29 | "rollup-plugin-livereload": "^2.0.5", 30 | "rollup-plugin-svelte": "^7.1.0", 31 | "rollup-plugin-terser": "^7.0.2", 32 | "sirv-cli": "^1.0.14", 33 | "svelte": "^3.42.4" 34 | }, 35 | "dependencies": { 36 | "@palsch/svelte-sortablejs": "^1.2.1", 37 | "bootstrap": "^4.6.0", 38 | "core-js": "^3.17.2", 39 | "jquery": "^3.6.0", 40 | "nimview": "^1.1.3" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/svelte_todo/readme.md: -------------------------------------------------------------------------------- 1 | # nimview Svelte Todo-list Application 2 | 3 | This is the source code for the demo app on the Nimview page. 4 | To compile your code, just run 5 | - npm install 6 | - npm run build 7 | - nim c -r --app:gui src/App.nim 8 | -------------------------------------------------------------------------------- /examples/svelte_todo/rollup.config.js: -------------------------------------------------------------------------------- 1 | import svelte from 'rollup-plugin-svelte' 2 | import resolve from '@rollup/plugin-node-resolve' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | import { babel } from '@rollup/plugin-babel' 5 | import livereload from 'rollup-plugin-livereload' 6 | import { terser } from 'rollup-plugin-terser' 7 | import css from 'rollup-plugin-css-only' 8 | 9 | 10 | const production = !process.env.ROLLUP_WATCH 11 | const useBabel = production || process.env.USE_BABEL 12 | 13 | export default { 14 | input: 'src/main.js', 15 | output: { 16 | sourcemap: true, 17 | format: 'iife', 18 | name: 'app', 19 | file: 'dist/build/bundle.js' 20 | }, 21 | plugins: [ 22 | svelte({ 23 | compilerOptions: { 24 | // enable run-time checks when not in production 25 | dev: !production 26 | } 27 | }), 28 | css({ output: 'bundle.css' }), 29 | // If you have external dependencies installed from 30 | // npm, you'll most likely need these plugins. In 31 | // some cases you'll need additional configuration 32 | // consult the documentation for details: 33 | // https://github.com/rollup/rollup-plugin-commonjs 34 | resolve({ 35 | browser: true, 36 | dedupe: ['svelte'] 37 | }), 38 | commonjs(), 39 | 40 | // Watch the `dist` directory and refresh the 41 | // browser on changes when not in production 42 | !production && livereload('dist'), 43 | // added by angelo 44 | // compile to good old IE11 compatible ES5 45 | useBabel && babel({ 46 | extensions: [ '.js', '.mjs', '.html', '.svelte' ], 47 | babelHelpers: 'runtime', 48 | exclude: [ 'node_modules/@babel/**', 'node_modules/core-js/**' ], 49 | presets: [ 50 | [ 51 | '@babel/preset-env', 52 | { 53 | targets: { 54 | ie: '11' 55 | }, 56 | useBuiltIns: 'usage', 57 | corejs: 3 58 | } 59 | ] 60 | ], 61 | plugins: [ 62 | '@babel/plugin-syntax-dynamic-import', 63 | [ 64 | '@babel/plugin-transform-runtime', 65 | { 66 | useESModules: true 67 | } 68 | ] 69 | ] 70 | }), 71 | 72 | // If we're building for production (npm run build 73 | // instead of npm run dev), minify 74 | production && terser() 75 | ], 76 | watch: { 77 | clearScreen: false 78 | } 79 | } -------------------------------------------------------------------------------- /examples/svelte_todo/src/App.nim: -------------------------------------------------------------------------------- 1 | import nimview 2 | 3 | when isMainModule: 4 | enableStorage() # adds getStoredVal and setStoredVal 5 | start() -------------------------------------------------------------------------------- /examples/svelte_todo/src/App.svelte: -------------------------------------------------------------------------------- 1 | 57 |
    58 | 63 |
    64 |
    65 |
    66 |
    67 |
    68 | 69 |
    70 | 71 |
    72 |
    73 |
    74 | {#if items.length > 0} 75 | 76 | 87 | deleteItem(item.id)} title="delete" class="delete float-right">x 88 | 89 | 103 | {/if} 104 |
    105 |
    106 |
    107 |
    108 | -------------------------------------------------------------------------------- /examples/svelte_todo/src/main.js: -------------------------------------------------------------------------------- 1 | import App from './App.svelte' 2 | import 'jquery/dist/jquery.js' 3 | import 'bootstrap/dist/js/bootstrap.bundle.js' 4 | import 'bootstrap/dist/css/bootstrap.css' 5 | 6 | var app 7 | document.addEventListener("DOMContentLoaded", function() { 8 | app = new App({ 9 | target: document.body, 10 | }) 11 | }) 12 | 13 | export default app -------------------------------------------------------------------------------- /examples/svelte_todo/src/nim.cfg: -------------------------------------------------------------------------------- 1 | --path:"../../../src/" -------------------------------------------------------------------------------- /examples/vue/README.md: -------------------------------------------------------------------------------- 1 | ## Nimview Vue sample application 2 | 3 | - npm install 4 | - npm run build 5 | - nim c -r -d:release --app:gui src/App.nim 6 | - (or, if you want to have an async count down button: `nim c -r -d:release --gc:orc --threads:on --app:gui src/App.nim`) -------------------------------------------------------------------------------- /examples/vue/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ['@vue/app', { useBuiltIns: 'usage' }] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /examples/vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nimview-bootstrapvue", 3 | "version": "0.1.1", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vue-cli-service serve --host localhost", 7 | "dev-ie": "vue-cli-service serve", 8 | "serve": "vue-cli-service serve", 9 | "build": "vue-cli-service build && inliner -m --videos --preserve-comments dist/index.html > dist/inlined.html", 10 | "lint": "vue-cli-service lint" 11 | }, 12 | "devDependencies": { 13 | "@tauri-apps/tauri-inliner": "^1.13.2", 14 | "@vue/cli-plugin-babel": "^4.5.15", 15 | "@vue/cli-service": "^4.5.15", 16 | "babel-eslint": "^10.1.0", 17 | "eslint": "^7.32.0", 18 | "eslint-plugin-vue": "^7.18.0", 19 | "vue-template-compiler": "^2.6.14" 20 | }, 21 | "dependencies": { 22 | "bootstrap": "^4.6.0", 23 | "bootstrap-vue": "^2.21.2", 24 | "core-js": "^3.18.0", 25 | "vue": "^2.6.14", 26 | "nimview": "^1.1.3" 27 | }, 28 | "eslintConfig": { 29 | "root": true, 30 | "env": { 31 | "node": true 32 | }, 33 | "extends": [ 34 | "plugin:vue/essential", 35 | "eslint:recommended" 36 | ], 37 | "parserOptions": { 38 | "parser": "babel-eslint" 39 | }, 40 | "rules": {} 41 | }, 42 | "browserslist": { 43 | "production": [ 44 | "> 1%", 45 | "last 2 versions", 46 | "IE 11" 47 | ], 48 | "development": [ 49 | "last 1 chrome version", 50 | "last 1 firefox version", 51 | "last 1 safari version" 52 | ] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /examples/vue/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/vue/public/favicon.ico -------------------------------------------------------------------------------- /examples/vue/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
    15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/vue/src/App.nim: -------------------------------------------------------------------------------- 1 | import nimview 2 | import os 3 | 4 | proc appendSomething(value: string): string {.noSideEffect.} = 5 | result = "'" & value & "' modified by Vue sample" 6 | 7 | proc countDown() = 8 | callFrontendJs("alert", "waiting 6 seconds") 9 | sleep(2000) 10 | callFrontendJs("alert", "4") 11 | sleep(3000) 12 | callFrontendJs("alert", "1") 13 | sleep(1000) 14 | 15 | proc main() = 16 | add("appendSomething", appendSomething) 17 | add("countDown", countDown) 18 | let argv = os.commandLineParams() 19 | for arg in argv: 20 | readAndParseJsonCmdFile(arg) 21 | start() 22 | 23 | when isMainModule: 24 | main() -------------------------------------------------------------------------------- /examples/vue/src/App.vue: -------------------------------------------------------------------------------- 1 | //App.vue 2 | 8 | -------------------------------------------------------------------------------- /examples/vue/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/examples/vue/src/assets/logo.png -------------------------------------------------------------------------------- /examples/vue/src/components/Navbar.vue: -------------------------------------------------------------------------------- 1 | //src/components/Navbar.vue 2 | -------------------------------------------------------------------------------- /examples/vue/src/components/Sample.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 45 | 46 | -------------------------------------------------------------------------------- /examples/vue/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import BootstrapVue from 'bootstrap-vue' 4 | 5 | import 'bootstrap/dist/css/bootstrap.css' 6 | import 'bootstrap-vue/dist/bootstrap-vue.css' 7 | 8 | document.addEventListener("DOMContentLoaded", function() { 9 | Vue.use(BootstrapVue) 10 | Vue.config.productionTip = false 11 | 12 | new Vue({ 13 | render: h => h(App) 14 | }).$mount('#app') 15 | }) 16 | -------------------------------------------------------------------------------- /examples/vue/src/nim.cfg: -------------------------------------------------------------------------------- 1 | --path:"../../../src/" -------------------------------------------------------------------------------- /examples/vue/vue.config.js: -------------------------------------------------------------------------------- 1 | // vue.config.js 2 | module.exports = { 3 | publicPath: "", 4 | filenameHashing: false, 5 | devServer: { 6 | host: "localhost", 7 | port: 5000, 8 | proxy: { 9 | "/" : { target: 'http://localhost:8000'}, 10 | "/ws" : { target: 'http://localhost:8000/ws', ws: false} 11 | } 12 | }, 13 | transpileDependencies: [ 14 | "nimview" 15 | ] 16 | } -------------------------------------------------------------------------------- /nimview.nimble: -------------------------------------------------------------------------------- 1 | version = "0.4.2" 2 | author = "Marco Mengelkoch" 3 | description = "Nim / Python / C library to run webview with HTML/JS as UI" 4 | license = "MIT" 5 | srcDir = "src" 6 | 7 | import os, strutils 8 | # Dependencies 9 | # you may skip nimpy and webview when compiling with nim c -d:just_core 10 | # Currently, Webview requires gcc and doesn't work with vcc or clang 11 | 12 | requires "nim >= 1.4.8", "nimpy >= 0.1.1", "nake >= 1.9.0", "ws >= 0.4.4" 13 | 14 | when defined(nimdistros): 15 | import distros 16 | # no foreignDep required for Windows 17 | if detectOs(Ubuntu): 18 | foreignDep "libwebkit2gtk-4.0-dev" 19 | elif detectOs(CentOS) or detectOs(RedHat) or detectOs(Fedora): 20 | foreignDep "webkitgtk4-devel" 21 | if not detectOs(Windows): 22 | echo "In case of trouble, you may need to install following dependencies:" 23 | echo "" 24 | echoForeignDeps() 25 | echo "" 26 | else: 27 | echo "no nimdistros" 28 | 29 | proc execSh(cmd: string) = 30 | if detectOs(Windows): 31 | exec "cmd /C " & cmd 32 | else: 33 | exec "bash -c '" & cmd & "'" 34 | 35 | proc builDemoBinaries() = 36 | let baseDir = thisDir() 37 | cd baseDir / "examples/svelte_todo" 38 | exec "nim c -f -d:release -d:useServer --out:" & baseDir & "/demo/httpTodo.exe src/App.nim" 39 | exec "nim c -f -d:release --app:gui --out:" & baseDir & "/demo/appTodo.exe src/App.nim" 40 | cd baseDir 41 | 42 | proc builDemoJs() = 43 | let baseDir = thisDir() 44 | cd baseDir / "examples/svelte_todo" 45 | execSh "npm install" 46 | execSh "npm run build" 47 | cd baseDir 48 | 49 | task docs, "Generate doc": 50 | exec "nim doc -o:docs/theindex.html src/nimview.nim " 51 | exec "nim doc -o:docs/webviewRenderer.html src/nimview/webviewRenderer.nim" 52 | exec "nim doc -o:docs/httpRenderer.html src/nimview/httpRenderer.nim" 53 | exec "nim doc -o:docs/storage.html src/nimview/storage.nim" 54 | exec "nim doc -o:docs/sharedTypes.html src/nimview/sharedTypes.nim" 55 | exec "nim doc -o:docs/requestMap.html src/nimview/requestMap.nim" 56 | exec "nim doc -o:docs/globals.html src/nimview/globals.nim" 57 | # let cmd = "inliner -n --preserve-comments --iesafe --inlinemin docs/nimview_tmp.html > docs/nimview.html" 58 | # execSh cmd 59 | 60 | task demo, "Generate demo files": 61 | builDemoJs() 62 | builDemoBinaries() 63 | 64 | task test, "Run tests": 65 | builDemoBinaries() 66 | let baseDir = thisDir() 67 | cd baseDir / "examples/c_cpp" 68 | let nake = system.findExe("nake") 69 | exec nake & " clean" 70 | exec nake & " test" 71 | cd baseDir / "examples/python" 72 | exec nake & " clean" 73 | exec nake & " test" 74 | cd baseDir 75 | exec "testament pattern \"tests/*.nim\"" 76 | echo "All tests passed" -------------------------------------------------------------------------------- /src/js/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 marcomq 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/js/README.md: -------------------------------------------------------------------------------- 1 | This package is part of [Nimview](https://github.com/marcomq/nimview/) and 2 | contains Javascript helpers to call server functions on nimview http server or 3 | nimview webview. 4 | 5 | You should import it as 6 | 7 | `import backend from "nimview"` 8 | 9 | The only function that might be called directly is 10 | 11 | `backend.waitInit().then(backend.someFunction)` -------------------------------------------------------------------------------- /src/js/nimview.js: -------------------------------------------------------------------------------- 1 | /** Nimview UI Library 2 | * © Copyright 2021, by Marco Mengelkoch 3 | * Licensed under MIT License, see License file for more details 4 | * git clone https://github.com/marcomq/nimview 5 | **/ 6 | 7 | import "whatwg-fetch" 8 | 9 | let ui 10 | if (typeof window.ui !== "object") { 11 | ui = {} 12 | ui.copyright = "© Copyright 2021, by Marco Mengelkoch" 13 | ui.resolveStorage = {} 14 | ui.callbackMapping = {} 15 | ui.requestCounter = 0 16 | ui.waitCounter = 0 17 | ui.initStarted = false 18 | ui.initFinished = false 19 | ui.initFailed = false 20 | window.ui = ui 21 | } 22 | else { 23 | ui = window.ui 24 | } 25 | let backend 26 | if (typeof window.backend !== "object") { 27 | backend = {} 28 | window.backend = backend 29 | } 30 | else { 31 | backend = window.backend 32 | } 33 | const defaultPostTarget = "" 34 | const host = "" // might cause "cors" errors if defined 35 | const wsHost = window.location.host 36 | 37 | ui.createRequestId = () => { 38 | if (ui.requestCounter >= Number.MAX_SAFE_INTEGER-1) { 39 | ui.requestCounter = 0 40 | } 41 | return ui.requestCounter++ 42 | } 43 | ui.alert = (str) => { 44 | if (ui.usingBrowser()) { 45 | alert(str) 46 | } 47 | else if (typeof window.nimview.alert === 'function') { 48 | window.nimview.alert(str) 49 | } 50 | } 51 | /** 52 | * This function will usually not be required as nimview registers all back-end functions 53 | * automatically to be available on front-end. You just need to call 54 | * backend.{registeredFunction}(...) 55 | * You may still use this function to explicitely call a function on server 56 | * @param {string} request 57 | * @param {*} data 58 | * @param {string} signature 59 | */ 60 | ui.callRequest = async (request, data, signature) => { 61 | if (typeof signature === "undefined") { 62 | signature = "" 63 | } 64 | if (typeof data === "undefined") { 65 | data = [] 66 | } 67 | if (ui.usingBrowser()) { 68 | // http-server 69 | const postData = JSON.stringify({request: request, data: data}) 70 | const requestOpts = { 71 | method: 'POST', // always use AJAX post for simplicity with special chars 72 | mode: 'cors', 73 | cache: 'no-cache', 74 | headers: {'Content-Type': 'application/json'}, 75 | body: postData 76 | } 77 | let url = request 78 | if (defaultPostTarget != "") { 79 | url = defaultPostTarget 80 | } 81 | if (ui.globalToken && (ui.globalToken.length > 0)) { 82 | requestOpts.headers["global-token"] = ui.globalToken 83 | } 84 | if (data.length != Math.min(signature.length, signature.split(",").length) 85 | && (signature.indexOf("array") == -1) && (signature.indexOf("vector") == -1) 86 | && (signature.indexOf("list") == -1) && (signature.indexOf("map") == -1) 87 | && console && console.log) { 88 | console.log("Request signature might not fit: '" + request + "' signature: '" 89 | + signature + "' data: '" + JSON.stringify(data) + "'") 90 | } 91 | return fetch(host + "/" + url, requestOpts).then((response) => { 92 | if (response) { 93 | var globalToken = response.headers.get("global-token") 94 | if (globalToken && (globalToken.length > 0)) { 95 | ui.globalToken = globalToken 96 | ui.lastToken = Date.now() 97 | } 98 | } 99 | return response.text() 100 | }) 101 | } 102 | else { 103 | // webview 104 | if (typeof window.nimview.call === "function") { 105 | const requestId = ui.createRequestId() 106 | const postData = JSON.stringify({request: request, data: data, 107 | requestId: requestId}) 108 | let promise = new Promise((resolve, reject) => { 109 | ui.resolveStorage[requestId] = [resolve, reject] 110 | window.nimview.call(postData) 111 | }) 112 | return promise 113 | } 114 | else { 115 | throw "window.nimview.call is not a function" 116 | } 117 | } 118 | } 119 | ui.addRequest = (requestOrArray) => { 120 | /*register global backend functions*/ 121 | if (typeof requestOrArray === "string") { 122 | requestOrArray = [requestOrArray, ""] 123 | } 124 | let request = requestOrArray[0] + "" 125 | let signature = requestOrArray[1] + "" // for debugging 126 | backend[request] = async (...data) => { 127 | return ui.callRequest(request, data, signature) 128 | } 129 | } 130 | ui.applyResponse = (requestId, data) => { 131 | if (typeof ui.resolveStorage[requestId] !== "undefined") { 132 | ui.resolveStorage[requestId][0](data) 133 | delete ui.resolveStorage[requestId] 134 | } 135 | } 136 | ui.rejectResponse = (requestId) => { 137 | if (typeof ui.resolveStorage[requestId] !== "undefined") { 138 | ui.resolveStorage[requestId][1]() 139 | delete ui.resolveStorage[requestId] 140 | } 141 | } 142 | ui.callFunction = (functionName, ...args) => { 143 | if (typeof ui.callbackMapping[functionName] === "function") { 144 | ui.callbackMapping[functionName](args) 145 | } 146 | else { 147 | window[functionName](args) 148 | } 149 | } 150 | ui.usingBrowser = () => { 151 | return (typeof window.nimview === 'undefined') 152 | } 153 | ui.init = (async () => { 154 | if (ui.waitCounter > 30) { 155 | ui.alert("API timeout") 156 | return 157 | } 158 | if ((window.location.href.indexOf("file:") == 0) 159 | || (window.location.href.indexOf("data:") == 0)) { 160 | if (typeof window.nimview === 'undefined') { 161 | // retry later when using webview and not initialized yet 162 | window.setTimeout(ui.init, 50) 163 | ui.waitCounter += 1 164 | return 165 | } 166 | } 167 | if (ui.initStarted == false) { 168 | ui.initStarted = true 169 | if (ui.usingBrowser()) { 170 | // using websocket to listen for commands 171 | let ws = "ws" 172 | if (window.location.href.indexOf("https:") == 0 173 | || host.indexOf("https:") == 0) { 174 | ws = "wss" 175 | } 176 | ui.ws = new WebSocket(ws + "://" + wsHost + "/ws") 177 | ui.ws.onmessage = (data) => { 178 | let resp 179 | try { 180 | resp = JSON.parse(data.data) 181 | } catch (err) { 182 | resp = JSON.parse(data) 183 | } 184 | ui.callFunction(resp.function, resp.args) 185 | } 186 | } 187 | await ui.callRequest("getGlobalToken", [], "").then((resp) => { 188 | let jsResp = JSON.parse(resp) 189 | if (jsResp.useGlobalToken) { 190 | window.setInterval(ui.getGlobalToken, 60 * 1000) 191 | } 192 | }).catch(() => { 193 | ui.alert("getGlobalToken failed") 194 | ui.initFailed = true 195 | }) 196 | await ui.callRequest("getRequests", [], "").then((resp) => { 197 | let jsResp = JSON.parse(resp) 198 | for (let req of jsResp) { // req is type array 199 | // bind all server requests to window.backend 200 | ui.addRequest(req) 201 | } 202 | ui.initFinished = true 203 | }).catch(() => { 204 | ui.alert("getRequests failed") 205 | ui.initFailed = true 206 | }) 207 | } 208 | }) 209 | /** 210 | * This call is optional. You need to call this function if you want to 211 | * immediately call a backend function and need to make sure that all functions 212 | * are already available. 213 | * In case you need to wait for initalization, call: 214 | * await backend.waitInit() 215 | * or 216 | * backend.waitInit().then(backend.functionThatNeedsToWait) 217 | */ 218 | backend.waitInit = () => { 219 | // everything else was complicate when using webview on windows 220 | return new Promise((resolve, reject) => { 221 | let waitLoop = () => { 222 | if (ui.initFinished) { 223 | return resolve() 224 | } 225 | else if (ui.initFailed) { 226 | return reject() 227 | } 228 | else { 229 | setTimeout(waitLoop, 5) 230 | } 231 | } 232 | setTimeout(waitLoop, 1) 233 | }) 234 | } 235 | /** 236 | * This function is used to change the mapping of a frontend function, 237 | * so it wouldn't be necessary to alter the backend, if a frontend function name 238 | * changes. You may not need it. 239 | * Use this if you need to run the same backend with multiple frontends or if 240 | * you want to call a frontend function that has a dot notation 241 | * @param {string} backendName - the function name that is called on backend 242 | * @param {string} frontendName the function name that is called on frontend 243 | */ 244 | backend.map = (backendName, frontendName) => { 245 | ui.callbackMapping[backendName] = frontendName 246 | } 247 | /** 248 | * @deprecated use backend.map instead 249 | */ 250 | backend.mapFrontendFunction = backend.map 251 | window.setTimeout(ui.init, 1) // may need to be increased on webview error 252 | export default backend -------------------------------------------------------------------------------- /src/js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nimview", 3 | "version": "1.2.1", 4 | "description": "This package is part of marcomq/nimview", 5 | "main": "nimview.js", 6 | "scripts": { 7 | "build": "rollup -c", 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "copyToSamples": "copyfiles *.iife.* ../../examples/minimal/dist && copyfiles *.iife.* ../../examples/minimal2/dist && copyfiles *.iife.* ../../examples/c_cpp/dist" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/marcomq/nimview.git" 14 | }, 15 | "keywords": [ 16 | "nim", 17 | "nimview", 18 | "webview" 19 | ], 20 | "author": "Marco Mengelkoch", 21 | "license": "MIT", 22 | "homepage": "https://github.com/marcomq/nimview#readme", 23 | "bugs": { 24 | "url": "https://github.com/marcomq/nimview/issues" 25 | }, 26 | "devDependencies": { 27 | "@babel/core": "^7.15.5", 28 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 29 | "@babel/plugin-transform-runtime": "^7.15.0", 30 | "@babel/preset-env": "^7.15.4", 31 | "@babel/runtime": "^7.15.4", 32 | "@babel/runtime-corejs2": "^7.15.4", 33 | "@rollup/plugin-babel": "^5.3.0", 34 | "@rollup/plugin-commonjs": "^20.0.0", 35 | "@rollup/plugin-node-resolve": "^13.0.4", 36 | "rollup": "^2.56.3", 37 | "rollup-plugin-terser": "^7.0.2", 38 | "copyfiles": "^2.4.1" 39 | }, 40 | "dependencies": { 41 | "whatwg-fetch": "^3.6.2" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/js/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve' 2 | import { terser } from 'rollup-plugin-terser' 3 | import { babel } from '@rollup/plugin-babel' 4 | import commonjs from '@rollup/plugin-commonjs' 5 | 6 | 7 | const production = !process.env.ROLLUP_WATCH 8 | const babelCfg = { 9 | extensions: [ '.js'], 10 | babelHelpers: 'runtime', 11 | exclude: [ 'node_modules/@babel/**', 'node_modules/core-js/**' ], 12 | presets: [ 13 | [ 14 | '@babel/preset-env', 15 | { 16 | targets: { 17 | ie: '11' 18 | }, 19 | useBuiltIns: 'usage', 20 | corejs: 2 21 | } 22 | ] 23 | ], 24 | plugins: [ 25 | '@babel/plugin-syntax-dynamic-import', 26 | [ 27 | '@babel/plugin-transform-runtime', 28 | { 29 | "absoluteRuntime": false 30 | } 31 | ] 32 | ] 33 | } 34 | export default { 35 | input: 'nimview.js', 36 | output: [{ 37 | sourcemap: true, 38 | format: 'cjs', 39 | name: 'nimview_js', 40 | file: 'nimview.cjs.js', 41 | exports: 'auto' 42 | }, { 43 | sourcemap: true, 44 | format: 'esm', 45 | name: 'nimview_js', 46 | file: 'nimview.esm.js', 47 | exports: 'auto' 48 | }, { 49 | sourcemap: true, 50 | format: 'iife', 51 | name: 'nimview_js', 52 | file: 'nimview.iife.js', 53 | exports: 'auto' 54 | }], 55 | plugins: [ 56 | resolve({ 57 | browser: true 58 | }), 59 | commonjs(), 60 | babel(babelCfg), 61 | production && terser() 62 | ], 63 | watch: { 64 | clearScreen: false 65 | } 66 | } -------------------------------------------------------------------------------- /src/nimview.nim.cfg: -------------------------------------------------------------------------------- 1 | --threads:on -------------------------------------------------------------------------------- /src/nimview/dispatchJsonRequest.nim: -------------------------------------------------------------------------------- 1 | import json 2 | import logging as log 3 | import globals 4 | import requestMap 5 | 6 | proc dispatchJsonRequest*(jsonMessage: JsonNode): string = 7 | ## Global json dispatcher that will be called from webview AND httpserver 8 | ## This will extract specific values that were prepared by nimview.js 9 | ## and forward those values to the string dispatcher. 10 | let request = jsonMessage["request"].getStr() 11 | if request == "getGlobalToken": 12 | return $ %* {"useGlobalToken": nimviewSettings.useGlobalToken} 13 | if not nimviewVars.requestLogger.isNil: 14 | nimviewVars.requestLogger.log(log.lvlInfo, $jsonMessage) 15 | let callbackFunc = requestMap.getCallbackFunc(request) 16 | result = callbackFunc(jsonMessage["data"]) 17 | -------------------------------------------------------------------------------- /src/nimview/globalToken.nim: -------------------------------------------------------------------------------- 1 | # Nimview UI Library 2 | # Copyright (C) 2021, by Marco Mengelkoch 3 | # Licensed under MIT License, see License file for more details 4 | # git clone https://github.com/marcomq/nimview 5 | 6 | import times, asynchttpserver, std/sysrand, base64 7 | 8 | type Token = object 9 | token: array[32, byte] 10 | generated: ref times.DateTime 11 | 12 | type GlobalTokens* = ref object 13 | ## 3 tokens that rotate 14 | tokens*: array[3, Token] 15 | 16 | proc init*(): GlobalTokens = 17 | new result 18 | for i in 0 ..< result.tokens.len: 19 | result.tokens[i].generated = new times.DateTime 20 | result.tokens[i].generated[] = times.now() - 5.minutes 21 | 22 | proc checkIfTokenExists(self: GlobalTokens, token: array[32, byte]): bool = 23 | # Very unlikely, but it may be necessary to also lock here 24 | for i in 0 ..< self.tokens.len: 25 | if token == self.tokens[i].token: 26 | return true 27 | return false 28 | 29 | proc byteToString*(token: array[32, byte]): string = 30 | result = base64.encode(token) 31 | 32 | func stringToByte*(token: string): array[32, byte] = 33 | let tokenString = base64.decode(token) 34 | if (tokenString.len > 31): 35 | system.copyMem(result[0].addr, tokenString[0].unsafeAddr, 32) 36 | else: 37 | raise newException(CatchableError, "token too short") 38 | 39 | proc checkToken*(self: GlobalTokens, headers: HttpHeaders): bool = 40 | var headerToken: string 41 | if headers.hasKey("global-token"): 42 | headerToken = $headers["global-token"] 43 | if headerToken.len > 31: 44 | var headerGlobalTokens = globalToken.stringToByte(headerToken) 45 | return self.checkIfTokenExists(headerGlobalTokens) 46 | return false 47 | 48 | proc getFreshToken*(self: var GlobalTokens): array[32, byte] = 49 | var currentTime = times.now() 50 | const interval = 60 51 | let frame = (currentTime.minute * 60 + currentTime.second).div(interval) mod self.tokens.len # a new token every interval seconds 52 | var currentToken = addr self.tokens[frame] 53 | var tokenPlusInterval = currentTime - interval.seconds 54 | try: 55 | if currentToken[].generated[].isInitialized(): 56 | tokenPlusInterval = currentToken[].generated[] + interval.seconds 57 | except: 58 | discard 59 | if tokenPlusInterval < currentTime: 60 | let randomValue = sysrand.urandom(32) 61 | for i in 0 ..< randomValue.len: 62 | result[i] = randomValue[i] 63 | currentToken[].generated[] = currentTime 64 | currentToken[].token = result 65 | result = currentToken[].token -------------------------------------------------------------------------------- /src/nimview/globals.nim: -------------------------------------------------------------------------------- 1 | # Nimview UI Library 2 | # Copyright (C) 2021, by Marco Mengelkoch 3 | # Licensed under MIT License, see License file for more details 4 | # git clone https://github.com/marcomq/nimview 5 | 6 | import os, macros, tables 7 | from sharedTypes import ReqFunction 8 | import logging as log 9 | 10 | type CstringFunc* = proc(jsArg: cstring) {.cdecl.} 11 | type RuntimeVars* = ref object 12 | requestLogger*: FileLogger 13 | staticDir*: string 14 | reqMapStore*: Table[string, ReqFunction] 15 | responseHttpHeader*: seq[tuple[key, val: string]] 16 | storage*: Table[string, string] 17 | storageFile*: string 18 | customJsEval*: pointer 19 | httpRenderer*: pointer 20 | webviewRenderer*: pointer 21 | 22 | type NimviewSettings* = object 23 | indexHtmlFile*: string 24 | port*: int 25 | bindAddr*: string 26 | title*: string 27 | width*: int 28 | height*: int 29 | resizable*: bool 30 | debug*: bool 31 | useHttpServer*: bool 32 | useGlobalToken*: bool 33 | useStaticIndexContent*: bool 34 | run*: bool 35 | 36 | const defaultIndex* = 37 | when not defined(release): 38 | "../dist/index.html" 39 | else: 40 | "../dist/inlined.html" 41 | 42 | const displayAvailable = 43 | when (system.hostOS == "windows"): 44 | true 45 | else: os.getEnv("DISPLAY") != "" 46 | 47 | const preferWebview = defined(useWebview) or not defined(useServer) 48 | const compileWithWebview* = not defined(just_core) and preferWebview 49 | 50 | proc initSettings*(indexHtmlFile: string = defaultIndex, port: int = 8000, 51 | bindAddr: string = "localhost", title: string = "nimview", 52 | width: int = 640, height: int = 480, resizable: bool = true): NimviewSettings = 53 | 54 | var useServer = 55 | not compileWithWebview or 56 | defined(useServer) or 57 | not defined(release) or 58 | not displayAvailable or 59 | (os.fileExists("/.dockerenv")) 60 | 61 | result.indexHtmlFile = indexHtmlFile 62 | result.port = port 63 | result.bindAddr = bindAddr 64 | result.title = title 65 | result.width = width 66 | result.height = height 67 | result.resizable = resizable 68 | result.debug = not defined release 69 | result.run = true 70 | result.useHttpServer = useServer 71 | result.useGlobalToken = defined(release) 72 | result.useStaticIndexContent = 73 | when declared(doNotLoadIndexContent): 74 | true 75 | else: 76 | false 77 | 78 | proc newRuntime*(): RuntimeVars = 79 | new result 80 | GC_ref(result) 81 | result.responseHttpHeader = @[("Access-Control-Allow-Origin", "127.0.0.1")] 82 | result.reqMapStore = initTable[string, ReqFunction]() 83 | result.storageFile = "storage.json" 84 | result.storage = initTable[string, string]() 85 | 86 | const defaultSettings* :NimviewSettings = initSettings() 87 | var nimviewSettings* {.global.} :NimviewSettings = initSettings() 88 | var nimviewVars* {.global.} :RuntimeVars = newRuntime() 89 | 90 | 91 | var indexContent* {.threadVar.}: string 92 | const indexContentStatic* = 93 | if fileExists(getProjectPath() & "/" & defaultSettings.indexHtmlFile): 94 | staticRead(getProjectPath() & "/" & defaultSettings.indexHtmlFile) 95 | else: 96 | "" -------------------------------------------------------------------------------- /src/nimview/httpRenderer.nim: -------------------------------------------------------------------------------- 1 | # Nimview UI Library 2 | # © Copyright 2021, by Marco Mengelkoch 3 | # Licensed under MIT License, see License file for more details 4 | # git clone https://github.com/marcomq/nimview 5 | 6 | import asynchttpserver, json, httpcore, asyncdispatch, os, strutils, sequtils 7 | import ws 8 | import globalToken 9 | import globals 10 | import sharedTypes 11 | import logging as log 12 | import dispatchJsonRequest 13 | 14 | type HttpRenderer* = ref object 15 | connections: seq[WebSocket] # old ws are not removed 16 | globalTokens: GlobalTokens 17 | 18 | proc getInstance: ptr HttpRenderer = 19 | {.gcsafe.}: 20 | if nimviewVars.httpRenderer.isNil: 21 | var newObj {.global.} = new HttpRenderer 22 | nimviewVars.httpRenderer = newObj.unsafeAddr 23 | GC_ref(newObj) 24 | return cast[ptr HttpRenderer](nimviewVars.httpRenderer) 25 | 26 | proc callFrontendJsEscapedHttp*(functionName: string, params: string) = 27 | ## "params" should be JS escaped values, separated by commas with surrounding quotes for string values 28 | var deletable = newSeq[int]() 29 | for idx, wsCon in getInstance().connections: 30 | if wsCon.readyState == ReadyState.Open: 31 | try: 32 | asynccheck wsCon.send("{\"function\":\"" & functionName & "\",\"args\":[" & params & "]}") 33 | except WebSocketProtocolMismatchError: 34 | log.info "Call frontend socket tried to use an unknown protocol: ", getCurrentExceptionMsg() 35 | except CatchableError: 36 | log.error "Call frontend error: ", getCurrentExceptionMsg() 37 | else: 38 | deletable.add(idx) 39 | for idx in deletable: 40 | when NimMinor >= 6 or NimMajor > 1: 41 | getInstance().connections.delete(idx..idx) 42 | else: 43 | getInstance().connections.delete(idx, idx) 44 | 45 | proc getCurrentAppDir(): string = 46 | let applicationName = os.getAppFilename().extractFilename() 47 | # debug applicationName 48 | if (applicationName.startsWith("python") or applicationName.startsWith("platform-python")): 49 | result = os.getCurrentDir() 50 | else: 51 | result = os.getAppDir() 52 | 53 | proc getAbsPath*(indexHtmlFile: string): (string, string) = 54 | let separatorFound = indexHtmlFile.rfind({'#', '?'}) 55 | if separatorFound == -1: 56 | result[0] = indexHtmlFile 57 | else: 58 | result[0] = indexHtmlFile[0 ..< separatorFound] 59 | result[1] = indexHtmlFile[separatorFound .. ^1] 60 | if (not os.isAbsolute(result[0])): 61 | result[0] = getCurrentAppDir() & "/" & indexHtmlFile 62 | 63 | proc dispatchHttpRequest*(jsonMessage: JsonNode, headers: HttpHeaders): string = 64 | ## Modify this, if you want to add some authentication, input format validation 65 | ## or if you want to process HttpHeaders. 66 | if not nimviewSettings.useGlobalToken or getInstance().globalTokens.checkToken(headers): 67 | return dispatchJsonRequest(jsonMessage) 68 | else: 69 | let request = jsonMessage["request"].getStr() 70 | if request == "getGlobalToken": 71 | return $ %* {"useGlobalToken": nimviewSettings.useGlobalToken} 72 | else: 73 | raise newException(ReqDeniedException, "403 - Token expired") 74 | 75 | proc handleRequest(request: Request): Future[void] {.async.} = 76 | ## used by HttpServer 77 | var response: string 78 | var requestPath: string = request.url.path 79 | var header = @[("Content-Type", "application/javascript")] 80 | {.gcsafe.}: 81 | let defaultHeader = nimviewVars.responseHttpHeader 82 | let separatorFound = requestPath.rfind({'#', '?'}) 83 | if separatorFound != -1: 84 | requestPath = requestPath[0 ..< separatorFound] 85 | if requestPath == "/": 86 | requestPath = "/index.html" 87 | if requestPath == "/index.html": 88 | when defined(release): 89 | if not indexContent.isEmptyOrWhitespace() and indexContent == indexContentStatic: 90 | header = @[("Content-Type", "text/html;charset=utf-8")] 91 | header.add(defaultHeader) 92 | await request.respond(Http200, indexContent, newHttpHeaders(header)) 93 | return 94 | 95 | try: 96 | {.gcsafe.}: 97 | let potentialFilename = nimviewVars.staticDir & 98 | requestPath.replace("../", "").replace("..", "") 99 | if os.fileExists(potentialFilename): 100 | debug "Sending " & potentialFilename 101 | let fileData = splitFile(potentialFilename) 102 | let contentType = case fileData.ext: 103 | of ".json": "application/json;charset=utf-8" 104 | of ".js": "text/javascript;charset=utf-8" 105 | of ".css": "text/css;charset=utf-8" 106 | of ".jpg": "image/jpeg" 107 | of ".txt": "text/plain;charset=utf-8" 108 | of ".map": "application/octet-stream" 109 | else: "text/html;charset=utf-8" 110 | header = @[("Content-Type", contentType)] 111 | header.add(defaultHeader) 112 | await request.respond(Http200, system.readFile(potentialFilename), newHttpHeaders(header)) 113 | elif requestPath == "/ws": 114 | when not defined(just_core): 115 | try: 116 | var ws = await newWebSocket(request) 117 | getInstance().connections.add(ws) 118 | while ws.readyState == ReadyState.Open: 119 | let packet = await ws.receiveStrPacket() 120 | info "Received packet: " & packet 121 | except WebSocketProtocolMismatchError: 122 | echo "Socket tried to use an unknown protocol: ", getCurrentExceptionMsg() 123 | except WebSocketError: 124 | echo "Socket error: ", getCurrentExceptionMsg() 125 | elif (request.body == ""): 126 | raise newException(ReqUnknownException, "404 - File not found") 127 | else: 128 | # if not a file, assume this is a json request 129 | var jsonMessage: JsonNode 130 | debug request.body 131 | # if unlikely(request.body == ""): 132 | # jsonMessage = parseJson(uri.decodeUrl(requestPath)) 133 | # else: 134 | jsonMessage = parseJson(request.body) 135 | {.gcsafe.}: 136 | response = dispatchHttpRequest(jsonMessage, request.headers) 137 | let byteToken = getInstance().globalTokens.getFreshToken() 138 | let currentToken = globalToken.byteToString(byteToken) 139 | let header = @{"global-token": currentToken} 140 | await request.respond(Http200, response, newHttpHeaders(header)) 141 | 142 | except ReqUnknownException: 143 | await request.respond(Http404, 144 | $ %* {"error": "404", "value": getCurrentExceptionMsg()}, 145 | newHttpHeaders(defaultHeader)) 146 | except ReqDeniedException: 147 | await request.respond(Http403, 148 | $ %* {"error": "403", "value": getCurrentExceptionMsg()}, 149 | newHttpHeaders(defaultHeader)) 150 | except ServerException: 151 | await request.respond(Http500, 152 | $ %* {"error": "500", "value": getCurrentExceptionMsg()}, 153 | newHttpHeaders(defaultHeader)) 154 | except JsonParsingError, KeyError: 155 | await request.respond(Http500, 156 | $ %* {"error": "500", "value": "request doesn't contain valid json"}, 157 | newHttpHeaders(defaultHeader)) 158 | except: 159 | await request.respond(Http500, 160 | $ %* {"error": "500", "value": "server error: " & getCurrentExceptionMsg()}, 161 | newHttpHeaders(defaultHeader)) 162 | 163 | 164 | proc serve*() {.async.} = 165 | var server = newAsyncHttpServer() 166 | getInstance().globalTokens = globalToken.init() 167 | listen(server, Port(nimviewSettings.port), nimviewSettings.bindAddr) 168 | while nimviewSettings.run: 169 | if server.shouldAcceptRequest(): 170 | await server.acceptRequest(handleRequest) 171 | else: 172 | poll() -------------------------------------------------------------------------------- /src/nimview/readme.md: -------------------------------------------------------------------------------- 1 | Git commands that were performed to checkout / update webview 2 | ``` 3 | (from nimview base dir) 4 | git subtree add --prefix src/nimview/webview https://github.com/marcomq/webview.git master --squash 5 | 6 | update to latest: 7 | git subtree pull --prefix src/nimview/webview https://github.com/marcomq/webview.git master --squash 8 | 9 | push back to webview: 10 | git subtree push --prefix src/nimview/webview https://github.com/marcomq/webview.git master 11 | 12 | ``` -------------------------------------------------------------------------------- /src/nimview/sharedTypes.nim: -------------------------------------------------------------------------------- 1 | # Nimview UI Library 2 | # Copyright (C) 2021, by Marco Mengelkoch 3 | # Licensed under MIT License, see License file for more details 4 | # git clone https://github.com/marcomq/nimview 5 | import json 6 | type ReqDeniedException* = object of CatchableError 7 | type ServerException* = object of CatchableError 8 | type ReqUnknownException* = object of CatchableError 9 | type ReqFunction* = object 10 | nimCallback*: proc (values: JsonNode): string 11 | jsSignature*: string -------------------------------------------------------------------------------- /src/nimview/std/readme.md: -------------------------------------------------------------------------------- 1 | This specific file sysrand.nim is part of Nim 1.6 source code 2 | https://github.com/nim-lang/Nim/blob/devel/lib/std/sysrand.nim 3 | 4 | It was copied here as Nim 1.6 was not available yet at time of writing. 5 | It will be removed later when Nim 1.6 is widely available and stable. -------------------------------------------------------------------------------- /src/nimview/storage.nim: -------------------------------------------------------------------------------- 1 | # Nimview UI Library 2 | # Copyright (C) 2021, by Marco Mengelkoch 3 | # Licensed under MIT License, see License file for more details 4 | # git clone https://github.com/marcomq/nimview 5 | 6 | ## Implements a simple key / value store that can be used to store preferences 7 | import strutils, os 8 | import std/[json, jsonutils] 9 | import tables 10 | import globals 11 | 12 | proc initStorage*(fileName: string = "") = 13 | try: 14 | if fileName != "": 15 | nimviewVars.storageFile = fileName 16 | if os.fileExists(nimviewVars.storageFile): 17 | var storageString = system.readFile(nimviewVars.storageFile) 18 | if not storageString.isEmptyOrWhitespace(): 19 | nimviewVars.storage = storageString.parseJson().jsonTo(typeof nimviewVars.storage) 20 | echo storageString 21 | except: 22 | echo "Couldn't read storage" 23 | 24 | proc getStoredVal*(key: string): string = 25 | try: 26 | result = nimviewVars.storage[key] 27 | except KeyError: 28 | discard 29 | 30 | proc setStoredVal*(key, value: string): string = 31 | if value == "": 32 | nimviewVars.storage.del(key) 33 | else: 34 | nimviewVars.storage[key] = value 35 | try: 36 | var jsonOutput: JsonNode 37 | jsonOutput = nimviewVars.storage.toJson() 38 | system.writeFile(nimviewVars.storageFile, $jsonOutput) 39 | except: 40 | echo "error setting storage key '" & key & "': " & getCurrentExceptionMsg() 41 | -------------------------------------------------------------------------------- /src/nimview/webview/.gitattributes: -------------------------------------------------------------------------------- 1 | *.h linguist-language=c -------------------------------------------------------------------------------- /src/nimview/webview/.gitignore: -------------------------------------------------------------------------------- 1 | nimcache/ 2 | simple 3 | window 4 | minimal 5 | *Ex* 6 | *.html 7 | *Test -------------------------------------------------------------------------------- /src/nimview/webview/readme.md: -------------------------------------------------------------------------------- 1 | # Webview for nim 2 | 3 | Nim bindings for [zserge's Webview](https://github.com/zserge/webview) which is an 4 | excellent cross-platform single header webview 5 | library for C/C++ using Gtk, Cocoa or MSHTML 6 | repectively. 7 | 8 | # Docs 9 | 10 | Documentation is [here](http://htmlpreview.github.io/?https://github.com/oskca/webview/blob/master/docs/webview.html) 11 | 12 | and [Golang's doc for webview](https://godoc.org/github.com/zserge/webview) is 13 | also very useful. 14 | 15 | When on `debian/ubuntu` `libwebkit2gtk-4.0-dev` is required as `debian/ubuntu`. 16 | -------------------------------------------------------------------------------- /src/nimview/webview/tests/bindEx.nim: -------------------------------------------------------------------------------- 1 | discard """ 2 | action: "compile" 3 | """ 4 | import webview 5 | import strutils 6 | import os 7 | 8 | const indexHTML = """ 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | """ 32 | let htmlFile = getTempDir() / "xxx.html" 33 | writeFile(htmlFile, indexHTML) 34 | var w = newWebView("Simple window demo2", "file://" & htmlFile) 35 | var fullScreen = true 36 | w.bindProcs("api"): 37 | proc open() = echo w.dialogOpen() 38 | proc save() = echo w.dialogSave() 39 | proc opendir() = echo w.dialogOpen(flag=dFlagDir) 40 | proc message() = w.msg("hello", "message") 41 | proc info() = w.info("hello", "info") 42 | proc warn() = w.warn("hello", "warn") 43 | proc error() = w.error("hello", "error") 44 | proc changeTitle(title: string) = w.setTitle(title) 45 | proc close() = w.terminate() 46 | proc changeColor() = w.setColor(210,210,210,100) 47 | proc toggleFullScreen() = fullScreen = not w.setFullscreen(fullScreen) 48 | 49 | # w.setFullscreen() 50 | w.run() 51 | w.exit() 52 | removeFile(htmlFile) 53 | -------------------------------------------------------------------------------- /src/nimview/webview/tests/config.nims: -------------------------------------------------------------------------------- 1 | --path:".." -------------------------------------------------------------------------------- /src/nimview/webview/tests/minimal.nim: -------------------------------------------------------------------------------- 1 | discard """ 2 | action: "compile" 3 | """ 4 | import webview 5 | 6 | open("Minimal webview example", "https://www.bing.com") 7 | -------------------------------------------------------------------------------- /src/nimview/webview/tests/scopeTest.nim: -------------------------------------------------------------------------------- 1 | discard """ 2 | action: "compile" 3 | """ 4 | import macros 5 | 6 | block: 7 | proc hello() = 8 | echo "nice to see youi" 9 | 10 | proc defInBlock[T, U](a:T, b:U) = echo a, b 11 | 12 | macro defInBlock(a, s: string, n: untyped): untyped = 13 | expectKind(n, nnkStmtList) 14 | let body = n 15 | body.add(newCall("hello2")) 16 | result = newBlockStmt(body) 17 | echo repr result 18 | 19 | defInBlock("waht", "api"): 20 | proc hello2() = 21 | echo "echo hello2" 22 | 23 | # hello2() -------------------------------------------------------------------------------- /src/nimview/webview/webview.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "oskca" 5 | description = "Nim bindings for zserge\'s webview" 6 | license = "MIT" 7 | skipDirs = @["tests"] 8 | 9 | # Dependencies 10 | 11 | requires "nim >= 0.17.2" 12 | 13 | task test, "a simple test case": 14 | exec "testament pattern \"tests/*.nim\"" 15 | 16 | task docs, "generate doc": 17 | exec "nim doc2 -o:docs/webview.html webview.nim" 18 | 19 | task sync, "update webview.h": 20 | exec "wget -O webview/webview.h https://raw.githubusercontent.com/wailsapp/wails/master/lib/renderer/webview/webview.h" 21 | exec "wget -O webview/webview.go https://raw.githubusercontent.com/wailsapp/wails/master/lib/renderer/webview/webview.go" 22 | exec "wget -O webview/LICENSE https://raw.githubusercontent.com/wailsapp/wails/master/lib/renderer/webview/LICENSE" 23 | 24 | task clean, "clean tmp files": 25 | exec "rm -rf nimcache" 26 | exec "rm -rf tests/nimcache" 27 | -------------------------------------------------------------------------------- /src/nimview/webview/webview/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Serge Zaitsev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/nimview/webviewRenderer.nim: -------------------------------------------------------------------------------- 1 | # Nimview UI Library 2 | # © Copyright 2021, by Marco Mengelkoch 3 | # Licensed under MIT License, see License file for more details 4 | # git clone https://github.com/marcomq/nimview 5 | 6 | 7 | const useWebviewInThread* = compileOption("threads") and not defined useWebviewSingleThreaded 8 | when useWebviewInThread: 9 | import threadpool 10 | import ../nimview/webview/webview except debug 11 | import json 12 | import logging as log 13 | import nimpy, strutils 14 | import globals 15 | import dispatchJsonRequest 16 | 17 | type WebviewRenderer = ref object 18 | webView*: Webview 19 | when useWebviewInThread: 20 | webviewQueue: Channel[string] 21 | runBarrier: Channel[bool] 22 | initBarrier: Channel[bool] 23 | 24 | 25 | proc getInstance: ptr WebviewRenderer = 26 | {.gcsafe.}: 27 | if nimviewVars.webviewRenderer.isNil: 28 | var newObj {.global.} = new WebviewRenderer 29 | when useWebviewInThread: 30 | newObj.webviewQueue.open() 31 | newObj.runBarrier.open() 32 | newObj.initBarrier.open() 33 | GC_ref(newObj) 34 | nimviewVars.webviewRenderer = newObj.unsafeAddr 35 | return cast[ptr WebviewRenderer](nimviewVars.webviewRenderer) 36 | 37 | proc computeMessageWebview(message: string) 38 | 39 | proc evalJs*(evalJsCode: string) = 40 | getInstance().webview.dispatch(proc() = 41 | discard getInstance().webview.eval(evalJsCode.cstring)) 42 | 43 | proc callFrontendJsEscapedWebview*(functionName: string, params: string) = 44 | ## "params" should be JS escaped values, separated by commas with surrounding quotes for string values 45 | {.gcsafe.}: 46 | if not getInstance().webview.isNil: 47 | let jsExec: string = "window.ui.callFunction(\"" & functionName & "\"," & params & ");" 48 | log.info jsExec 49 | evalJs(jsExec) 50 | 51 | proc stopDesktop*() {.exportpy, exportc: "nimview_$1".} = 52 | ## Will stop the Desktop app - may trigger application exit. 53 | debug "stopping ..." 54 | if not getInstance().webview.isNil(): 55 | getInstance().webview.dispatch(proc() = 56 | getInstance().webview.terminate() 57 | dealloc(getInstance().webview)) 58 | 59 | proc runDesktop*(url: string, title: string, width: int, height: int, 60 | resizable: bool, debug: bool, run: bool) = 61 | {.gcsafe.}: 62 | if getInstance().webview.isNil: 63 | getInstance().webview = webview.newWebView(title, url, width, 64 | height, resizable = resizable, debug = debug) 65 | getInstance().webview.bindProc("nimview", "alert", proc (message: string) = 66 | getInstance().webview.info("alert", message)) 67 | let dispatchCall = proc (message: string) = 68 | when useWebviewInThread: 69 | getInstance().webviewQueue.send(message) 70 | else: 71 | computeMessageWebview(message) 72 | getInstance().webview.bindProc("nimview", "call", dispatchCall) 73 | when useWebviewInThread: 74 | getInstance().initBarrier.send(true) 75 | if not run: 76 | discard getInstance().runBarrier.recv() 77 | getInstance().webview.run() 78 | getInstance().webviewQueue.send("") 79 | getInstance().runBarrier.close() 80 | getInstance().initBarrier.close() 81 | stopDesktop() 82 | 83 | 84 | proc spawnDesktopThread*(url: string, title: string, width: int, height: int, 85 | resizable: bool, debug: bool, run: bool) = 86 | when useWebviewInThread: 87 | spawn runDesktop(url, title, width, height, resizable, debug, run) 88 | discard getInstance().initBarrier.recv() 89 | 90 | proc waitForThreads*() = 91 | when useWebviewInThread: 92 | sync() 93 | 94 | proc runWebview*() = 95 | when useWebviewInThread: 96 | # actual webview is currently waiting in "runDesktop" 97 | getInstance().runBarrier.send(true) 98 | while nimviewSettings.run: 99 | var message = getInstance().webviewQueue.recv() 100 | computeMessageWebview(message) 101 | else: 102 | getInstance().webview.run() 103 | stopDesktop() 104 | 105 | 106 | proc selectFolderDialog*(title: string): string {.exportpy.} = 107 | ## Will open a "sect folder dialog" if in webview mode and return the selection. 108 | ## Will return emptys string in webserver mode 109 | if not getInstance().webview.isNil(): 110 | result = getInstance().webview.dialogOpen(title=if title != "" : title else: "Select Folder", flag=webview.dFlagDir) 111 | 112 | proc selectFileDialog*(title: string): string {.exportpy.} = 113 | ## Will open a "sect file dialog" if in webview mode and return the selection. 114 | ## Will return emptys string in webserver mode 115 | if not getInstance().webview.isNil(): 116 | result = getInstance().webview.dialogOpen(title=if title != "" : title else: "Select File", flag=webview.dFlagFile) 117 | 118 | proc setIcon*(icon: string) {.exportpy.} = 119 | getInstance().webview.dispatch(proc() = 120 | getInstance().webview.setIcon(icon.cstring)) 121 | 122 | proc setBorderless*(decorated: bool = false) {.exportc, exportpy.} = 123 | ## Use gtk mode without borders, only works on linux and only in desktop mode 124 | when defined(linux): 125 | let myWebView = getInstance().webview 126 | if not myWebView.isNil(): 127 | {.emit: "gtk_window_set_decorated(GTK_WINDOW(`myWebView`->priv.window), `decorated`);".} 128 | 129 | proc setFullscreen*(fullScreen: bool = true) {.exportc, exportpy.} = 130 | if not getInstance().webview.isNil(): 131 | getInstance().webview.dispatch(proc() = 132 | discard getInstance().webview.setFullscreen(fullScreen)) 133 | 134 | proc setColor*(r, g, b, alpha: uint8) {.exportc, exportpy.} = 135 | if not getInstance().webview.isNil(): 136 | getInstance().webview.dispatch(proc() = 137 | getInstance().webview.setColor(r, g, b, alpha)) 138 | 139 | proc setMaxSize*(width, height: int) {.exportpy.} = 140 | if not getInstance().webview.isNil(): 141 | getInstance().webview.dispatch(proc() = 142 | getInstance().webview.setMaxSize(width.cint, height.cint)) 143 | 144 | proc setMaxSize*(width, height: cint) {.exportc.} = 145 | setMaxSize(width.int, height.int) 146 | 147 | proc setMinSize*(width, height: int) {.exportpy.} = 148 | if not getInstance().webview.isNil(): 149 | getInstance().webview.dispatch(proc() = 150 | getInstance().webview.setMinSize(width.cint, height.cint)) 151 | 152 | proc setMinSize*(width, height: cint) {.exportc.} = 153 | setMinSize(width.int, height.int) 154 | 155 | proc focus*(width, height: int) {.exportpy, exportc.} = 156 | if not getInstance().webview.isNil(): 157 | getInstance().webview.dispatch(proc() = 158 | getInstance().webview.focus()) 159 | 160 | proc computeMessageWebview(message: string) {.used.} = 161 | info message 162 | if message.len == 0: 163 | nimviewSettings.run = false 164 | else: 165 | let jsonMessage = json.parseJson(message) 166 | let requestId = jsonMessage["requestId"].getInt() 167 | try: 168 | let response = dispatchJsonRequest(jsonMessage) 169 | let evalJsCode = "window.ui.applyResponse(" & $requestId & "," & 170 | response.escape("'","'") & ");" 171 | evalJs(evalJsCode) 172 | except: 173 | log.error getCurrentExceptionMsg() 174 | let evalJsCode = "window.ui.rejectResponse(" & $requestId & ");" 175 | evalJs(evalJsCode) -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | import sys, os, pathlib 2 | # print(pathlib.Path(os.path.abspath(__file__)).parent.parent) 3 | # sys.path.append(str(pathlib.Path(os.path.abspath(__file__)).parent.parent) + "/out") 4 | import nimview -------------------------------------------------------------------------------- /tests/c_test.c: -------------------------------------------------------------------------------- 1 | /** Nimview UI Library 2 | * Copyright (C) 2020, 2021, by Marco Mengelkoch 3 | * Licensed under MIT License, see License file for more details 4 | * git clone https://github.com/marcomq/nimview 5 | **/ 6 | // Important Notice: You should use --threads:on AND you need to avoid --gc:arc ; I had crashes on windows otherwise with NIM 1.4 when starting webview 7 | 8 | #include "../out/tmp_c/nimview.h" 9 | #include 10 | #include 11 | 12 | char* echoAndModify(char* something) { 13 | const char* appendString = " modified by C"; 14 | char* result = malloc(strlen(something) + strlen(appendString) + 1); // +1 for the null-terminator, strlen is unchecked! "something" needs 0 termination 15 | if (result) { 16 | strcpy(result, something); // safe, result just created 17 | strcat(result, appendString); // safe, result just created with len 18 | } 19 | else { 20 | return ""; // "" will not be freed 21 | } 22 | return result; 23 | } 24 | 25 | char* stopNimview(char* something) { 26 | nimview_stopDesktop(); 27 | return ""; 28 | } 29 | int main(int argc, char* argv[]) { 30 | printf(" starting c code\n"); 31 | NimMain(); 32 | nimview_addRequest("echoAndModify", echoAndModify, free); 33 | nimview_addRequest("stopNimview", stopNimview, free); 34 | 35 | nimview_dispatchCommandLineArg("{\"request\":\"echoAndModify\",\"value\":\"this is a test\",\"responseId\":0}"); 36 | nimview_dispatchCommandLineArg("{\"request\":\"stopNimview\",\"value\":\"\",\"responseId\":1}"); 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /tests/desktopSample.nim: -------------------------------------------------------------------------------- 1 | discard """ 2 | action: "compile" 3 | matrix: "; -d:just_core; -d:useWebviewSingleThreaded" 4 | cmd: "nim $target -f --hints:on -d:testing $file" 5 | """ 6 | import ../src/nimview 7 | import os 8 | 9 | proc main() = 10 | nimview.add("echoAndModify", proc (value: string): string = 11 | echo value 12 | ## just simulating progress 13 | for i in 0..100: 14 | callJs("applyProgress", $(i) & "%") 15 | os.sleep(20) 16 | result = "'" & value & "' modified by Webview Backend") 17 | if not defined(just_core): 18 | nimview.startDesktop("../examples/minimal2/dist/index.html", run=false) 19 | nimview.setBorderless() 20 | nimview.setFullscreen() 21 | nimview.setColor(1,2,3,50) 22 | nimview.setIcon(currentSourcePath.parentDir() / "icon.ico") # will not be visible in fullscreen 23 | nimviewSettings.width = 600 24 | nimview.enableStorage() 25 | nimview.enableRequestLogger() 26 | nimview.disableRequestLogger() 27 | nimview.add("callJsProgress", proc () = 28 | echo nimview.selectFolderDialog("") 29 | ) 30 | nimview.setUseServer(false) 31 | nimview.setUseGlobalToken(false) 32 | nimview.run() 33 | 34 | when isMainModule: 35 | main() -------------------------------------------------------------------------------- /tests/httpSample.nim: -------------------------------------------------------------------------------- 1 | discard """ 2 | matrix: "; -d:just_core; -d:useServer" 3 | action: "compile" 4 | cmd: "nim $target -f --hints:on -d:testing $file" 5 | """ 6 | import ../src/nimview 7 | 8 | add("appendSomething", proc (value: string): string = 9 | echo value 10 | result = "'" & value & "' modified by Web Backend" 11 | ) 12 | 13 | add("appendSomething2", proc (value: string) = 14 | echo value 15 | ) 16 | 17 | add("appendSomething3", proc (value: string, number: int) = 18 | echo value & $number 19 | ) 20 | 21 | add("appendSomething4", proc (value: string, number: int): string = 22 | echo value & $number 23 | result = "'" & value & $number & "' modified by Web Backend" 24 | ) 25 | 26 | add("appendSomething5", proc (value: string, number: int, number2: int): string = 27 | echo value & $number 28 | result = "'" & value & $number & $number2 & "' modified by Web Backend" 29 | ) 30 | 31 | add("appendSomething6", proc (value: string, number: int, number2: int) = 32 | echo value & $number & $number2 33 | ) 34 | 35 | add("appendSomething7", proc (number: int, number2: int): int = 36 | echo $number & $number2 37 | result = number + number2 38 | ) 39 | 40 | add("appendSomething8", proc (value: string, value2: string, number: int, number2: int): int = 41 | echo value & $number & $number2 & value2 42 | result = number + number2 43 | ) 44 | 45 | proc main() = 46 | if not defined(just_core): 47 | startHttpServer("../examples/minimal/dist/index.html") 48 | 49 | when isMainModule: 50 | main() -------------------------------------------------------------------------------- /tests/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcomq/nimview/d6bf5ce39d710f6ed80f0ca320b93ee6323ae66f/tests/icon.ico -------------------------------------------------------------------------------- /tests/nim.cfg: -------------------------------------------------------------------------------- 1 | --threads:on -------------------------------------------------------------------------------- /tests/pyTest.py: -------------------------------------------------------------------------------- 1 | # Nimview UI Library 2 | # Copyright (C) 2020, 2021, by Marco Mengelkoch 3 | # Licensed under MIT License, see License file for more details 4 | 5 | # pylint: disable=import-error 6 | import __init__, nimview 7 | def echoAndModify(value): 8 | print (value) 9 | return (value + " appended by python") 10 | 11 | def echoAndModify2(): 12 | print ("received nil") 13 | return (" appended by python") 14 | 15 | def echoAndModify3(value1, value2): 16 | result = value1 + " "+ value2 + " both received" 17 | print (result) 18 | return (result + " by python") 19 | 20 | def stopNimview(value): 21 | nimview.stopDesktop() 22 | return "" 23 | 24 | nimview.addRequest("echoAndModify", echoAndModify) 25 | nimview.addRequest("echoAndModify2", echoAndModify2) 26 | nimview.addRequest("echoAndModify3", echoAndModify3) 27 | nimview.addRequest("stopNimview", stopNimview) 28 | 29 | nimview.dispatchCommandLineArg("{\"request\":\"echoAndModify\",\"value\":\"this is a test\",\"responseId\":0}") 30 | nimview.dispatchCommandLineArg("{\"request\":\"echoAndModify\",\"value\":[],\"responseId\":3}") # will cause an error 31 | nimview.dispatchCommandLineArg("{\"request\":\"echoAndModify2\",\"value\":[],\"responseId\":4}") 32 | nimview.dispatchCommandLineArg("{\"request\":\"echoAndModify3\",\"value\":[\"first\",\"second\"],\"responseId\":5}") 33 | nimview.dispatchCommandLineArg("{\"request\":\"stopNimview\",\"value\":\"\",\"responseId\":6}") 34 | print ("python test passed") 35 | # nimview.startDesktop("tests/minimal_ui_sample/index.html") 36 | -------------------------------------------------------------------------------- /tests/pyWebViewSample.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # pylint: disable=no-member 3 | import nimview 4 | import os, time 5 | import threading 6 | 7 | def threadedWait1(): 8 | try: 9 | global thread 10 | thread = threading.Thread(target = asyncWait1, daemon=None) 11 | thread.start() 12 | except: 13 | print ("some other error") 14 | raise() 15 | return ("") 16 | 17 | def asyncWait1(): 18 | try: 19 | print ("wait 1 start") 20 | time.sleep(1) 21 | print ("wait 1 running") 22 | time.sleep(1) 23 | print ("wait 1 still running") 24 | time.sleep(1) 25 | print ("wait 1 still running") 26 | time.sleep(5) 27 | print ("wait 1 end") 28 | except: 29 | print ("some error") 30 | raise() 31 | return ("") 32 | 33 | def wait2(): 34 | print ("wait 2 start") 35 | time.sleep(2) 36 | print ("wait 2 end") 37 | return ("") 38 | 39 | def wait(): 40 | time.sleep(0.002) 41 | return ("") 42 | 43 | nimview.addRequest("asyncWait1", threadedWait1) 44 | nimview.addRequest("wait2", wait2) 45 | nimview.addRequest("wait", wait) 46 | nimview.startDesktop("asyncPy/index.html") 47 | thread.join() 48 | # threadedWait1() 49 | # asyncWait2() 50 | # time.sleep(29) -------------------------------------------------------------------------------- /tests/requestsFail.nim: -------------------------------------------------------------------------------- 1 | discard """ 2 | action: "run" 3 | cmd: "nim $target -f --hints:on -d:testing $file" 4 | output: ''' 5 | WARN Error calling function, args: {"request":"echoAndModify","data":[],"responseId":0} 6 | WARN Error calling function, args: {"request":"echoAndModify3","data":["first"],"responseId":2} 7 | ''' 8 | """ 9 | 10 | import ../src/nimview 11 | import logging 12 | 13 | proc echoAndModify(value: string): string = 14 | return (value & " appended by nim") 15 | 16 | proc echoAndModify2(): string = 17 | return (" appended by python") 18 | 19 | proc echoAndModify3(value1, value2: string): string = 20 | result = value1 & " " & value2 & " both received" 21 | 22 | proc echoAndModify4(value1: string, value2: string, value3: int): string = 23 | result = value1 & " " & value2 & " " & $value3 & " received" 24 | 25 | proc stopNimview() = 26 | nimview.stop() 27 | 28 | 29 | proc main() = 30 | logging.getHandlers()[0].levelThreshold = lvlWarn 31 | nimview.add("echoAndModify", echoAndModify) 32 | nimview.add("echoAndModify2", echoAndModify2) 33 | nimview.add("echoAndModify3", echoAndModify3) 34 | nimview.add("echoAndModify4", echoAndModify4) 35 | nimview.add("stopNimview", stopNimview) 36 | 37 | discard nimview.dispatchCommandLineArg("{\"request\":\"echoAndModify\",\"data\":[],\"responseId\":0}") # will print warning 38 | discard nimview.dispatchCommandLineArg("{\"request\":\"echoAndModify2\",\"data\":[\"unused\"],\"responseId\":1}") # will currently not print warning 39 | discard nimview.dispatchCommandLineArg("{\"request\":\"echoAndModify3\",\"data\":[\"first\"],\"responseId\":2}") # will print warning 40 | discard nimview.dispatchCommandLineArg("{\"request\":\"echoAndModify4\",\"data\":[\"first\",2,3],\"responseId\":3}") # will currently not print warning, as 2 is converted to string 41 | discard nimview.dispatchCommandLineArg("{\"request\":\"stopNimview\",\"data\":[],\"responseId\":6}") 42 | 43 | when isMainModule: 44 | main() -------------------------------------------------------------------------------- /tests/requestsSuccess.nim: -------------------------------------------------------------------------------- 1 | discard """ 2 | action: "run" 3 | cmd: "nim $target --hints:on -d:testing $file" 4 | output: "" 5 | """ 6 | 7 | import ../src/nimview 8 | import logging 9 | logging.getHandlers()[0].levelThreshold = lvlWarn 10 | 11 | proc echoAndModify(value: string): string = 12 | return (value & " appended by nim") 13 | 14 | proc echoAndModify2(): string = 15 | return (" appended by python") 16 | 17 | proc echoAndModify3(value1, value2: string): string = 18 | result = value1 & " " & value2 & " both received" 19 | 20 | proc echoAndModify4(value1: string, value2: string, value3: int): string = 21 | result = value1 & " " & value2 & " " & $value3 & " received" 22 | 23 | proc stopNimview() = 24 | nimview.stopDesktop() 25 | 26 | 27 | nimview.add("echoAndModify", echoAndModify) 28 | nimview.add("echoAndModify2", echoAndModify2) 29 | nimview.add("echoAndModify3", echoAndModify3) 30 | nimview.add("echoAndModify4", echoAndModify4) 31 | nimview.add("stopNimview", stopNimview) 32 | 33 | discard nimview.dispatchCommandLineArg("{\"request\":\"echoAndModify\",\"data\":[\"this is a test\"],\"responseId\":0}") 34 | discard nimview.dispatchCommandLineArg("{\"request\":\"echoAndModify2\",\"data\":[],\"responseId\":4}") 35 | discard nimview.dispatchCommandLineArg("{\"request\":\"echoAndModify3\",\"data\":[\"first\",\"second\"],\"responseId\":5}") 36 | discard nimview.dispatchCommandLineArg("{\"request\":\"echoAndModify4\",\"data\":[\"first\",\"second\",3],\"responseId\":7}") 37 | discard nimview.dispatchCommandLineArg("{\"request\":\"stopNimview\",\"data\":[],\"responseId\":6}") 38 | -------------------------------------------------------------------------------- /tests/test.dump: -------------------------------------------------------------------------------- 1 | {"request":"appendSomething","value":"gg","responseId":0} 2 | {"request":"appendSomething","value":"ff","responseId":1} 3 | {"request":"appendSomething","value":"vv","responseId":2} --------------------------------------------------------------------------------