import webgui let app = newWebView() ## newWebView(dataUriHtmlHeader & "<p>Hello World</p>") app.run() ## newWebView("http://localhost/index.html") app.exit() ## newWebView("index.html") ## newWebView("Karax_Compiled_App.js") ## newWebView("Will_be_Compiled_to_JavaScript.nim")
- Design with CSS3, mockup with HTML5, Fast as Rust, Simple as Python, No-GC, powered by Nim.
- Dark-Theme and Light-Theme Built-in, Fonts, TrayIcon, Clipboard, Lazy-Loading Images, Cursors.
- Native Notifications with Sound, Config and DNS helpers, Animation Effects, few LOC, and more...
Buit-in Dark Mode
Buit-in Light Mode
Real-Life Examples
Real-Life Projects
- https://github.com/ThomasTJdev/nim_nimble_gui (~20 lines of Nim at the time of writing)
- https://github.com/juancarlospaco/ballena-itcher (~42 lines of Nim at the time of writing)
- https://github.com/juancarlospaco/nim-smnar (~32 lines of Nim at the time of writing)
- https://github.com/ThomasTJdev/choosenim_gui (~80 lines of Nim at the time of writing)
- https://github.com/juancarlospaco/borapp (~50 lines of Nim at the time of writing)
- https://github.com/ThomasTJdev/nmqttgui
Types
ExternalInvokeCb = proc (w: Webview; arg: string)
- External CallBack Proc
WebviewObj {...}{.importc: "struct webview", header: "/home/juan/code/webgui/src/webview.h", bycopy.} = object url* {...}{.importc: "url".}: cstring ## Current URL title* {...}{.importc: "title".}: cstring ## Window Title width* {...}{.importc: "width".}: cint ## Window Width height* {...}{.importc: "height".}: cint ## Window Height resizable* {...}{.importc: "resizable".}: cint ## `true` to Resize the Window, `false` for Fixed size Window debug* {...}{.importc: "debug".}: cint ## Debug is `true` when not build for Release invokeCb {...}{.importc: "external_invoke_cb".}: pointer ## Callback proc priv {...}{.importc: "priv".}: WebviewPrivObj userdata {...}{.importc: "userdata".}: pointer
- WebView Type
Webview = ptr WebviewObj
DispatchFn = proc ()
TinyDefaultButton = enum tdbCancel = 0, tdbOk = 1, tdbNo = 2
InsertAdjacent = enum beforeBegin = "beforebegin", ## Before the targetElement itself. afterBegin = "afterbegin", ## Just inside the targetElement, before its first child. beforeEnd = "beforeend", ## Just inside the targetElement, after its last child. afterEnd = "afterend" ## After the targetElement itself.
- Positions for insertAdjacentElement, insertAdjacentHTML, insertAdjacentText
CSSShake = enum shakeCrazy = "@keyframes shake-crazy{10%{transform:translate(-15px, 10px) rotate(-9deg);opacity:.86}20%{transform:translate(18px, 9px) rotate(8deg);opacity:.11}30%{transform:translate(12px, -4px) rotate(1deg);opacity:.93}40%{transform:translate(-9px, 14px) rotate(0deg);opacity:.46}50%{transform:translate(-4px, -3px) rotate(-9deg);opacity:.67}60%{transform:translate(-11px, 19px) rotate(-5deg);opacity:.59}70%{transform:translate(-19px, 11px) rotate(-5deg);opacity:.92}80%{transform:translate(-16px, 8px) rotate(-1deg);opacity:.63}90%{transform:translate(6px, 0px) rotate(-6deg);opacity:.09}0%,100%{transform:translate(0, 0) rotate(0)}} $1{animation-name:shake-crazy;animation-duration:100ms;animation-timing-function:ease-in-out;animation-iteration-count:infinite}", ## Crazy shakeSimple = "@keyframes shake{2%{transform:translate(1.5px, .5px) rotate(-.5deg)}4%{transform:translate(.5px, 2.5px) rotate(.5deg)}6%{transform:translate(2.5px, -1.5px) rotate(-.5deg)}8%{transform:translate(-1.5px, .5px) rotate(-.5deg)}10%{transform:translate(-.5px, 1.5px) rotate(.5deg)}12%{transform:translate(2.5px, .5px) rotate(.5deg)}14%{transform:translate(1.5px, -1.5px) rotate(.5deg)}16%{transform:translate(2.5px, -1.5px) rotate(1.5deg)}18%{transform:translate(.5px, -1.5px) rotate(.5deg)}20%{transform:translate(-.5px, .5px) rotate(.5deg)}22%{transform:translate(2.5px, -1.5px) rotate(-.5deg)}24%{transform:translate(2.5px, 1.5px) rotate(1.5deg)}26%{transform:translate(1.5px, 2.5px) rotate(.5deg)}28%{transform:translate(1.5px, 1.5px) rotate(.5deg)}30%{transform:translate(-.5px, 1.5px) rotate(-.5deg)}32%{transform:translate(2.5px, 2.5px) rotate(1.5deg)}34%{transform:translate(2.5px, -.5px) rotate(1.5deg)}36%{transform:translate(-1.5px, -.5px) rotate(-.5deg)}38%{transform:translate(-.5px, -.5px) rotate(.5deg)}40%{transform:translate(.5px, 2.5px) rotate(1.5deg)}42%{transform:translate(.5px, 1.5px) rotate(.5deg)}44%{transform:translate(-1.5px, -.5px) rotate(.5deg)}46%{transform:translate(1.5px, 1.5px) rotate(.5deg)}48%{transform:translate(-.5px, -.5px) rotate(1.5deg)}50%{transform:translate(2.5px, .5px) rotate(.5deg)}52%{transform:translate(2.5px, -.5px) rotate(1.5deg)}54%{transform:translate(.5px, -1.5px) rotate(-.5deg)}56%{transform:translate(-1.5px, -1.5px) rotate(-.5deg)}58%{transform:translate(.5px, -.5px) rotate(.5deg)}60%{transform:translate(-.5px, .5px) rotate(-.5deg)}62%{transform:translate(2.5px, 2.5px) rotate(.5deg)}64%{transform:translate(2.5px, 1.5px) rotate(.5deg)}66%{transform:translate(-1.5px, -.5px) rotate(.5deg)}68%{transform:translate(.5px, -.5px) rotate(1.5deg)}70%{transform:translate(.5px, -.5px) rotate(-.5deg)}72%{transform:translate(-.5px, 2.5px) rotate(-.5deg)}74%{transform:translate(1.5px, 2.5px) rotate(.5deg)}76%{transform:translate(1.5px, 2.5px) rotate(1.5deg)}78%{transform:translate(-1.5px, -.5px) rotate(-.5deg)}80%{transform:translate(2.5px, -.5px) rotate(.5deg)}82%{transform:translate(-1.5px, -.5px) rotate(-.5deg)}84%{transform:translate(.5px, -1.5px) rotate(-.5deg)}86%{transform:translate(-.5px, 2.5px) rotate(.5deg)}88%{transform:translate(2.5px, .5px) rotate(.5deg)}90%{transform:translate(2.5px, -.5px) rotate(1.5deg)}92%{transform:translate(2.5px, 1.5px) rotate(-.5deg)}94%{transform:translate(1.5px, 2.5px) rotate(-.5deg)}96%{transform:translate(2.5px, -1.5px) rotate(-.5deg)}98%{transform:translate(-.5px, .5px) rotate(.5deg)}0%,100%{transform:translate(0, 0) rotate(0)}} $1{animation-name:shake;animation-duration:100ms;animation-timing-function:ease-in-out;animation-iteration-count:infinite}", ## Simple shakeHard = "@keyframes shake-hard{2%{transform:translate(3px, 1px) rotate(3.5deg)}4%{transform:translate(3px, -2px) rotate(.5deg)}6%{transform:translate(8px, 2px) rotate(3.5deg)}8%{transform:translate(8px, -7px) rotate(-2.5deg)}10%{transform:translate(1px, 5px) rotate(2.5deg)}12%{transform:translate(8px, -8px) rotate(-.5deg)}14%{transform:translate(-5px, -3px) rotate(-1.5deg)}16%{transform:translate(-4px, -9px) rotate(-2.5deg)}18%{transform:translate(-7px, 4px) rotate(-1.5deg)}20%{transform:translate(-3px, -9px) rotate(3.5deg)}22%{transform:translate(9px, -6px) rotate(-2.5deg)}24%{transform:translate(4px, -3px) rotate(-1.5deg)}26%{transform:translate(-6px, 8px) rotate(3.5deg)}28%{transform:translate(1px, 10px) rotate(.5deg)}30%{transform:translate(0px, 5px) rotate(.5deg)}32%{transform:translate(2px, -9px) rotate(.5deg)}34%{transform:translate(-5px, -3px) rotate(2.5deg)}36%{transform:translate(-5px, -8px) rotate(-2.5deg)}38%{transform:translate(-9px, -4px) rotate(-2.5deg)}40%{transform:translate(-7px, -1px) rotate(-2.5deg)}42%{transform:translate(-5px, 1px) rotate(-.5deg)}44%{transform:translate(-5px, -3px) rotate(3.5deg)}46%{transform:translate(-8px, 5px) rotate(1.5deg)}48%{transform:translate(9px, 5px) rotate(1.5deg)}50%{transform:translate(5px, 3px) rotate(2.5deg)}52%{transform:translate(7px, 10px) rotate(-.5deg)}54%{transform:translate(-6px, 9px) rotate(3.5deg)}56%{transform:translate(-2px, 1px) rotate(-1.5deg)}58%{transform:translate(7px, 3px) rotate(-1.5deg)}60%{transform:translate(-9px, 4px) rotate(3.5deg)}62%{transform:translate(-3px, -6px) rotate(1.5deg)}64%{transform:translate(-3px, -9px) rotate(1.5deg)}66%{transform:translate(5px, 2px) rotate(-1.5deg)}68%{transform:translate(10px, 3px) rotate(-2.5deg)}70%{transform:translate(-4px, 6px) rotate(3.5deg)}72%{transform:translate(-2px, -6px) rotate(2.5deg)}74%{transform:translate(4px, -2px) rotate(-.5deg)}76%{transform:translate(-4px, -5px) rotate(3.5deg)}78%{transform:translate(9px, 4px) rotate(.5deg)}80%{transform:translate(-7px, -2px) rotate(3.5deg)}82%{transform:translate(-5px, -7px) rotate(-2.5deg)}84%{transform:translate(-3px, 1px) rotate(-2.5deg)}86%{transform:translate(-9px, 3px) rotate(2.5deg)}88%{transform:translate(-5px, -2px) rotate(2.5deg)}90%{transform:translate(7px, -2px) rotate(.5deg)}92%{transform:translate(-2px, 9px) rotate(-2.5deg)}94%{transform:translate(-8px, 8px) rotate(-.5deg)}96%{transform:translate(1px, -4px) rotate(3.5deg)}98%{transform:translate(-9px, 8px) rotate(-1.5deg)}0%,100%{transform:translate(0, 0) rotate(0)}} $1{animation-name:shake-hard;animation-duration:100ms;animation-timing-function:ease-in-out;animation-iteration-count:infinite}", ## Hard shakeHorizontal = "@keyframes shake-horizontal{2%{transform:translate(6px, 0) rotate(0)}4%{transform:translate(5px, 0) rotate(0)}6%{transform:translate(0px, 0) rotate(0)}8%{transform:translate(-5px, 0) rotate(0)}10%{transform:translate(7px, 0) rotate(0)}12%{transform:translate(9px, 0) rotate(0)}14%{transform:translate(3px, 0) rotate(0)}16%{transform:translate(-7px, 0) rotate(0)}18%{transform:translate(-3px, 0) rotate(0)}20%{transform:translate(0px, 0) rotate(0)}22%{transform:translate(9px, 0) rotate(0)}24%{transform:translate(-7px, 0) rotate(0)}26%{transform:translate(0px, 0) rotate(0)}28%{transform:translate(-6px, 0) rotate(0)}30%{transform:translate(2px, 0) rotate(0)}32%{transform:translate(3px, 0) rotate(0)}34%{transform:translate(1px, 0) rotate(0)}36%{transform:translate(-1px, 0) rotate(0)}38%{transform:translate(0px, 0) rotate(0)}40%{transform:translate(2px, 0) rotate(0)}42%{transform:translate(6px, 0) rotate(0)}44%{transform:translate(1px, 0) rotate(0)}46%{transform:translate(9px, 0) rotate(0)}48%{transform:translate(6px, 0) rotate(0)}50%{transform:translate(4px, 0) rotate(0)}52%{transform:translate(-4px, 0) rotate(0)}54%{transform:translate(10px, 0) rotate(0)}56%{transform:translate(8px, 0) rotate(0)}58%{transform:translate(5px, 0) rotate(0)}60%{transform:translate(6px, 0) rotate(0)}62%{transform:translate(3px, 0) rotate(0)}64%{transform:translate(-2px, 0) rotate(0)}66%{transform:translate(10px, 0) rotate(0)}68%{transform:translate(-5px, 0) rotate(0)}70%{transform:translate(-3px, 0) rotate(0)}72%{transform:translate(10px, 0) rotate(0)}74%{transform:translate(8px, 0) rotate(0)}76%{transform:translate(4px, 0) rotate(0)}78%{transform:translate(1px, 0) rotate(0)}80%{transform:translate(9px, 0) rotate(0)}82%{transform:translate(9px, 0) rotate(0)}84%{transform:translate(-4px, 0) rotate(0)}86%{transform:translate(-4px, 0) rotate(0)}88%{transform:translate(6px, 0) rotate(0)}90%{transform:translate(5px, 0) rotate(0)}92%{transform:translate(-7px, 0) rotate(0)}94%{transform:translate(-4px, 0) rotate(0)}96%{transform:translate(-4px, 0) rotate(0)}98%{transform:translate(4px, 0) rotate(0)}0%,100%{transform:translate(0, 0) rotate(0)}} $1{animation-name:shake-horizontal;animation-duration:100ms;animation-timing-function:ease-in-out;animation-iteration-count:infinite}", ## Horizontal shakeTiny = "@keyframes shake-little{2%{transform:translate(1px, 0px) rotate(.5deg)}4%{transform:translate(1px, 0px) rotate(.5deg)}6%{transform:translate(1px, 1px) rotate(.5deg)}8%{transform:translate(0px, 0px) rotate(.5deg)}10%{transform:translate(1px, 0px) rotate(.5deg)}12%{transform:translate(1px, 1px) rotate(.5deg)}14%{transform:translate(1px, 1px) rotate(.5deg)}16%{transform:translate(1px, 1px) rotate(.5deg)}18%{transform:translate(0px, 1px) rotate(.5deg)}20%{transform:translate(1px, 0px) rotate(.5deg)}22%{transform:translate(1px, 0px) rotate(.5deg)}24%{transform:translate(1px, 0px) rotate(.5deg)}26%{transform:translate(1px, 1px) rotate(.5deg)}28%{transform:translate(0px, 0px) rotate(.5deg)}30%{transform:translate(1px, 0px) rotate(.5deg)}32%{transform:translate(1px, 0px) rotate(.5deg)}34%{transform:translate(0px, 0px) rotate(.5deg)}36%{transform:translate(0px, 1px) rotate(.5deg)}38%{transform:translate(0px, 0px) rotate(.5deg)}40%{transform:translate(1px, 0px) rotate(.5deg)}42%{transform:translate(1px, 1px) rotate(.5deg)}44%{transform:translate(1px, 0px) rotate(.5deg)}46%{transform:translate(1px, 1px) rotate(.5deg)}48%{transform:translate(1px, 0px) rotate(.5deg)}50%{transform:translate(1px, 0px) rotate(.5deg)}52%{transform:translate(0px, 1px) rotate(.5deg)}54%{transform:translate(0px, 1px) rotate(.5deg)}56%{transform:translate(0px, 0px) rotate(.5deg)}58%{transform:translate(1px, 0px) rotate(.5deg)}60%{transform:translate(0px, 0px) rotate(.5deg)}62%{transform:translate(0px, 0px) rotate(.5deg)}64%{transform:translate(1px, 0px) rotate(.5deg)}66%{transform:translate(1px, 1px) rotate(.5deg)}68%{transform:translate(1px, 0px) rotate(.5deg)}70%{transform:translate(1px, 0px) rotate(.5deg)}72%{transform:translate(1px, 0px) rotate(.5deg)}74%{transform:translate(1px, 1px) rotate(.5deg)}76%{transform:translate(1px, 0px) rotate(.5deg)}78%{transform:translate(0px, 0px) rotate(.5deg)}80%{transform:translate(1px, 1px) rotate(.5deg)}82%{transform:translate(1px, 1px) rotate(.5deg)}84%{transform:translate(1px, 0px) rotate(.5deg)}86%{transform:translate(1px, 0px) rotate(.5deg)}88%{transform:translate(0px, 1px) rotate(.5deg)}90%{transform:translate(1px, 1px) rotate(.5deg)}92%{transform:translate(1px, 0px) rotate(.5deg)}94%{transform:translate(0px, 1px) rotate(.5deg)}96%{transform:translate(0px, 1px) rotate(.5deg)}98%{transform:translate(1px, 1px) rotate(.5deg)}0%,100%{transform:translate(0, 0) rotate(0)}}$1{animation-name:shake-little;animation-duration:100ms;animation-timing-function:ease-in-out;animation-iteration-count:infinite}", ## Tiny shakeSpin = "@keyframes shake-rotate{2%{transform:translate(0, 0) rotate(.5deg)}4%{transform:translate(0, 0) rotate(2.5deg)}6%{transform:translate(0, 0) rotate(-.5deg)}8%{transform:translate(0, 0) rotate(-4.5deg)}10%{transform:translate(0, 0) rotate(-3.5deg)}12%{transform:translate(0, 0) rotate(-2.5deg)}14%{transform:translate(0, 0) rotate(-3.5deg)}16%{transform:translate(0, 0) rotate(5.5deg)}18%{transform:translate(0, 0) rotate(-1.5deg)}20%{transform:translate(0, 0) rotate(2.5deg)}22%{transform:translate(0, 0) rotate(-2.5deg)}24%{transform:translate(0, 0) rotate(5.5deg)}26%{transform:translate(0, 0) rotate(-.5deg)}28%{transform:translate(0, 0) rotate(5.5deg)}30%{transform:translate(0, 0) rotate(3.5deg)}32%{transform:translate(0, 0) rotate(3.5deg)}34%{transform:translate(0, 0) rotate(3.5deg)}36%{transform:translate(0, 0) rotate(-3.5deg)}38%{transform:translate(0, 0) rotate(-6.5deg)}40%{transform:translate(0, 0) rotate(-2.5deg)}42%{transform:translate(0, 0) rotate(7.5deg)}44%{transform:translate(0, 0) rotate(2.5deg)}46%{transform:translate(0, 0) rotate(-6.5deg)}48%{transform:translate(0, 0) rotate(-2.5deg)}50%{transform:translate(0, 0) rotate(2.5deg)}52%{transform:translate(0, 0) rotate(3.5deg)}54%{transform:translate(0, 0) rotate(-6.5deg)}56%{transform:translate(0, 0) rotate(-5.5deg)}58%{transform:translate(0, 0) rotate(.5deg)}60%{transform:translate(0, 0) rotate(.5deg)}62%{transform:translate(0, 0) rotate(2.5deg)}64%{transform:translate(0, 0) rotate(-5.5deg)}66%{transform:translate(0, 0) rotate(3.5deg)}68%{transform:translate(0, 0) rotate(-3.5deg)}70%{transform:translate(0, 0) rotate(.5deg)}72%{transform:translate(0, 0) rotate(-.5deg)}74%{transform:translate(0, 0) rotate(6.5deg)}76%{transform:translate(0, 0) rotate(-6.5deg)}78%{transform:translate(0, 0) rotate(-1.5deg)}80%{transform:translate(0, 0) rotate(-2.5deg)}82%{transform:translate(0, 0) rotate(-6.5deg)}84%{transform:translate(0, 0) rotate(-3.5deg)}86%{transform:translate(0, 0) rotate(5.5deg)}88%{transform:translate(0, 0) rotate(-1.5deg)}90%{transform:translate(0, 0) rotate(-.5deg)}92%{transform:translate(0, 0) rotate(-1.5deg)}94%{transform:translate(0, 0) rotate(6.5deg)}96%{transform:translate(0, 0) rotate(4.5deg)}98%{transform:translate(0, 0) rotate(-3.5deg)}0%,100%{transform:translate(0, 0) rotate(0)}} $1{animation-name:shake-rotate;animation-duration:100ms;animation-timing-function:ease-in-out;animation-iteration-count:infinite}", ## Spin shakeSlow = "@keyframes shake-slow{2%{transform:translate(7px, 8px) rotate(2.5deg)}4%{transform:translate(-6px, -5px) rotate(-1.5deg)}6%{transform:translate(6px, 4px) rotate(-1.5deg)}8%{transform:translate(-8px, -5px) rotate(-1.5deg)}10%{transform:translate(0px, 7px) rotate(2.5deg)}12%{transform:translate(-9px, 6px) rotate(3.5deg)}14%{transform:translate(10px, -7px) rotate(-1.5deg)}16%{transform:translate(-8px, -9px) rotate(-2.5deg)}18%{transform:translate(-7px, -5px) rotate(.5deg)}20%{transform:translate(0px, -3px) rotate(-2.5deg)}22%{transform:translate(4px, 10px) rotate(3.5deg)}24%{transform:translate(-5px, 7px) rotate(-2.5deg)}26%{transform:translate(7px, -6px) rotate(2.5deg)}28%{transform:translate(10px, 8px) rotate(-2.5deg)}30%{transform:translate(-5px, 6px) rotate(2.5deg)}32%{transform:translate(1px, 3px) rotate(-2.5deg)}34%{transform:translate(4px, 6px) rotate(-2.5deg)}36%{transform:translate(-7px, 0px) rotate(-1.5deg)}38%{transform:translate(4px, 6px) rotate(.5deg)}40%{transform:translate(-2px, 5px) rotate(.5deg)}42%{transform:translate(6px, 2px) rotate(.5deg)}44%{transform:translate(-9px, 4px) rotate(-.5deg)}46%{transform:translate(6px, -7px) rotate(-.5deg)}48%{transform:translate(8px, 1px) rotate(1.5deg)}50%{transform:translate(-4px, -9px) rotate(1.5deg)}52%{transform:translate(7px, -5px) rotate(3.5deg)}54%{transform:translate(10px, 1px) rotate(.5deg)}56%{transform:translate(5px, 2px) rotate(3.5deg)}58%{transform:translate(4px, -4px) rotate(2.5deg)}60%{transform:translate(-2px, 6px) rotate(-2.5deg)}62%{transform:translate(5px, -4px) rotate(-2.5deg)}64%{transform:translate(8px, 0px) rotate(-2.5deg)}66%{transform:translate(7px, 7px) rotate(-1.5deg)}68%{transform:translate(7px, -2px) rotate(.5deg)}70%{transform:translate(3px, -4px) rotate(3.5deg)}72%{transform:translate(-5px, -9px) rotate(2.5deg)}74%{transform:translate(1px, 0px) rotate(-1.5deg)}76%{transform:translate(1px, -8px) rotate(-2.5deg)}78%{transform:translate(5px, 9px) rotate(-2.5deg)}80%{transform:translate(-9px, 2px) rotate(-.5deg)}82%{transform:translate(-5px, 9px) rotate(.5deg)}84%{transform:translate(-7px, -2px) rotate(-.5deg)}86%{transform:translate(-3px, 3px) rotate(1.5deg)}88%{transform:translate(8px, -7px) rotate(-1.5deg)}90%{transform:translate(-2px, 3px) rotate(2.5deg)}92%{transform:translate(10px, 10px) rotate(.5deg)}94%{transform:translate(0px, 8px) rotate(2.5deg)}96%{transform:translate(-6px, 6px) rotate(3.5deg)}98%{transform:translate(9px, -6px) rotate(2.5deg)}0%,100%{transform:translate(0, 0) rotate(0)}} $1{animation-name:shake-slow;animation-duration:5s;animation-timing-function:ease-in-out;animation-iteration-count:infinite}", ## Slow shakeVertical = "@keyframes shake-vertical{2%{transform:translate(0, -2px) rotate(0)}4%{transform:translate(0, 0px) rotate(0)}6%{transform:translate(0, 8px) rotate(0)}8%{transform:translate(0, 1px) rotate(0)}10%{transform:translate(0, -3px) rotate(0)}12%{transform:translate(0, -5px) rotate(0)}14%{transform:translate(0, 10px) rotate(0)}16%{transform:translate(0, 10px) rotate(0)}18%{transform:translate(0, 1px) rotate(0)}20%{transform:translate(0, -1px) rotate(0)}22%{transform:translate(0, -2px) rotate(0)}24%{transform:translate(0, 8px) rotate(0)}26%{transform:translate(0, -7px) rotate(0)}28%{transform:translate(0, -3px) rotate(0)}30%{transform:translate(0, -7px) rotate(0)}32%{transform:translate(0, -9px) rotate(0)}34%{transform:translate(0, -1px) rotate(0)}36%{transform:translate(0, 1px) rotate(0)}38%{transform:translate(0, 10px) rotate(0)}40%{transform:translate(0, -6px) rotate(0)}42%{transform:translate(0, 7px) rotate(0)}44%{transform:translate(0, 4px) rotate(0)}46%{transform:translate(0, 7px) rotate(0)}48%{transform:translate(0, -8px) rotate(0)}50%{transform:translate(0, -5px) rotate(0)}52%{transform:translate(0, 2px) rotate(0)}54%{transform:translate(0, -1px) rotate(0)}56%{transform:translate(0, -9px) rotate(0)}58%{transform:translate(0, -3px) rotate(0)}60%{transform:translate(0, -2px) rotate(0)}62%{transform:translate(0, -2px) rotate(0)}64%{transform:translate(0, 0px) rotate(0)}66%{transform:translate(0, -4px) rotate(0)}68%{transform:translate(0, 4px) rotate(0)}70%{transform:translate(0, -3px) rotate(0)}72%{transform:translate(0, 6px) rotate(0)}74%{transform:translate(0, -1px) rotate(0)}76%{transform:translate(0, -8px) rotate(0)}78%{transform:translate(0, -6px) rotate(0)}80%{transform:translate(0, -9px) rotate(0)}82%{transform:translate(0, 4px) rotate(0)}84%{transform:translate(0, 4px) rotate(0)}86%{transform:translate(0, -3px) rotate(0)}88%{transform:translate(0, 1px) rotate(0)}90%{transform:translate(0, -4px) rotate(0)}92%{transform:translate(0, -5px) rotate(0)}94%{transform:translate(0, 5px) rotate(0)}96%{transform:translate(0, 4px) rotate(0)}98%{transform:translate(0, 8px) rotate(0)}0%,100%{transform:translate(0, 0) rotate(0)}} $1{animation-name:shake-vertical;animation-duration:100ms;animation-timing-function:ease-in-out;animation-iteration-count:infinite}" ## Vertical
- Pure CSS Shake Effects.
Consts
fileLocalHeader = "file:///"
- Use Local File as URL.
Procs
proc dialogSave(aTitle: cstring; aDefaultPathAndFile: cstring; aNumOfFilterPatterns = 0.cint; aFilterPatterns = "*.*".cstring; aSingleFilterDescription = "".cstring; aAllowMultipleSelects: range[0 .. 1] = 0): cstring {...}{. importc: "tinyfd_saveFileDialog".}
-
- aDefaultPathAndFile is 1 default full path.
- aFilterPatterns is 1 Posix Glob pattern string. "*.*", "*.jpg", etc.
- aSingleFilterDescription is a string with descriptions for aFilterPatterns.
- aAllowMultipleSelects must be 0 (false) or 1 (true), multiple selection returns 1 string with paths divided by |, int type.
Similar to the other file dialog but with more extra options.
proc dialogOpenDir(aTitle: cstring; aDefaultPath: cstring): cstring {...}{. importc: "tinyfd_selectFolderDialog".}
-
- aDefaultPath is a Default Folder Path.
Similar to the other file dialog but with more extra options.
proc externalInvokeCB=(w: Webview; callback: ExternalInvokeCb) {...}{.inline, raises: [Exception], tags: [RootEffect].}
- Set the external invoke callback for webview, for Advanced users only
proc dispatch(w: Webview; fn: DispatchFn) {...}{.inline, raises: [Exception, KeyError], tags: [RootEffect].}
- Explicitly force dispatch a function, for advanced users only
proc run(w: Webview; quitProc: proc () {...}{.noconv.}; controlCProc: proc () {...}{.noconv.}; autoClose: static[bool] = true) {...}{.inline.}
-
run starts the main UI loop until the user closes the window. Same as run but with extras.
- quitProc is a function to run at exit, needs {.noconv.} pragma.
- controlCProc is a function to run at CTRL+C, needs {.noconv.} pragma.
- autoClose set to true to automatically run exit() at exit.
proc getOpt(key: static[string]; parseProc: proc; default: any; required: static[bool] = false; shortOpts: static[bool] = false; prefix = '-'; seps = {':', '='}): auto {...}{.inline.}
-
Fast simple parseOpt alternative, parse anything, returns value directly, copy-free.
- key is the Key to parse from commandLineParams.
- parseProc is whatever proc parses the value of key, any proc should work.
- default is 1 default value to return if key is not found, if required=true is ignored.
- required if true then key is required and mandatory.
- shortOpts if true then -key=value short format is allowed too (Slower!).
- prefix is 1 char for prefix for key, prefix='+' then ++key=value is parsed.
- seps is 1 set[char] for separator of key and value, seps={'@'} then --key@value is parsed.
echo getOpt("foo", parseInt, 0) ## --foo=42 echo getOpt("bar", parseBool, false, required = true) ## --bar:true echo getOpt("baz", parseHexStr, "f0f0", required = false) ## --baz:bebe echo getOpt("bax", readFile, "default", required = false) ## --bax:file.ext echo getOpt("bay", json.parseFile, %*{"key": "value"}) ## --bay:data.json echo getOpt("owo", parseUInt, 9, shortOpts=true, prefix='+', seps={'@'}) ## +owo@42
proc bindProc[P, R](w: Webview; scope, name: string; p: (proc (param: P): R)) {...}{.used.}
- Do NOT use directly, see bindProcs macro.
proc bindProcNoArg(w: Webview; scope, name: string; p: proc ()) {...}{.used, raises: [KeyError, Exception, ValueError], tags: [RootEffect].}
- Do NOT use directly, see bindProcs macro.
proc bindProc[P](w: Webview; scope, name: string; p: proc (arg: P)) {...}{.used.}
- Do NOT use directly, see bindProcs macro.
proc newWebView(path: static[string] = ""; title = ""; width: Positive = 640; height: Positive = 480; resizable: static[bool] = true; debug: static[bool] = not true; callback: ExternalInvokeCb = nil; skipTaskbar: static[bool] = false; windowBorders: static[bool] = true; focus: static[bool] = false; keepOnTop: static[bool] = false; minimized: static[bool] = false; cssPath: static[string] = ""; trayIcon: static[cstring] = ""; fullscreen: static[bool] = false): Webview
-
Create a new Window with given attributes, all arguments are optional.
- path is the URL or Full Path to 1 HTML file, index of the Web GUI App.
- title is the Title of the Window.
- width is the Width of the Window.
- height is the Height of the Window.
- resizable set to true to allow Resize of the Window, defaults to true.
- debug Debug mode, Debug is true when not built for Release.
- skipTaskbar if set to true the Window will not be visible on the desktop Taskbar.
- windowBorders if set to false the Window will have no Borders, no Close button, no Minimize button.
- focus if set to true the Window will force Focus.
- keepOnTop if set to true the Window will keep on top of all other windows on the desktop.
- minimized if set the true the Window will be Minimized, Iconified.
- cssPath Full Path or URL of a CSS file to use as Style, defaults to "dark.css" for Dark theme, can be "light.css" for Light theme.
- trayIcon Path to a local PNG Image Icon file.
- fullscreen if set to true the Window will be forced Fullscreen.
- If --light-theme on commandLineParams() then it will use Light Theme automatically.
- CSS is embedded, if your app is used Offline, it will display Ok.
- For templates that do CSS, remember that CSS must be injected after DOM Ready.
- Is up to the developer to guarantee access to the HTML URL or File of the GUI.
Funcs
func beep(_: Webview): void {...}{.importc: "tinyfd_beep".}
- Beep Sound to alert the user.
func notifySend(aTitle: cstring; aMessage: cstring; aDialogType = "yesno".cstring; aIconType = "info".cstring; aDefaultButton = tdbOk): cint {...}{. importc: "tinyfd_notifyPopup".}
-
This is similar to notify-send from Linux, but implemented in C. This will send 1 native notification, but will fallback from best to worse, on Linux without a full desktop or without notification system, it may use zenity or similar.
- aDialogType must be one of "ok", "okcancel", "yesno", "yesnocancel", string type.
- aIconType must be one of "info", "warning", "error", "question", string type.
- aDefaultButton must be one of 0 (for Cancel), 1 (for Ok), 2 (for No), range[0..2] type.
func dialogInput(aTitle: cstring; aMessage: cstring; aDefaultInput: cstring = nil): cstring {...}{. importc: "tinyfd_inputBox".}
-
- aDialogType must be one of "ok", "okcancel", "yesno", "yesnocancel", string type.
- aIconType must be one of "info", "warning", "error", "question", string type.
- aDefaultButton must be one of 0 (for Cancel), 1 (for Ok), 2 (for No), range[0..2] type.
- aDefaultInput must be nil (for Password entry field) or any string for plain text entry field with a default value, string or nil type.
func dialogMessage(aTitle: cstring; aMessage: cstring; aDialogType = "yesno".cstring; aIconType = "info".cstring; aDefaultButton = tdbOk): cint {...}{. importc: "tinyfd_messageBox".}
-
- aDialogType must be one of "ok", "okcancel", "yesno", "yesnocancel", string type.
- aIconType must be one of "info", "warning", "error", "question", string type.
- aDefaultButton must be one of 0 (for Cancel), 1 (for Ok), 2 (for No), range[0..2] type.
func dialogOpen(aTitle: cstring; aDefaultPathAndFile: cstring; aNumOfFilterPatterns = 0.cint; aFilterPattern = "*.*".cstring; aSingleFilterDescription = "".cstring; aAllowMultipleSelects: range[0 .. 1] = 0): cstring {...}{. importc: "tinyfd_openFileDialog".}
-
- aAllowMultipleSelects must be 0 (false) or 1 (true), multiple selection returns 1 string with paths divided by |, int type.
- aDefaultPathAndFile is 1 default full path.
- aFilterPatterns is 1 Posix Glob pattern string. "*.*", "*.jpg", etc.
- aSingleFilterDescription is a string with descriptions for aFilterPatterns.
Similar to the other file dialog but with more extra options.
func js(w: Webview; javascript: cstring): cint {...}{.importc: "webview_eval", header: "/home/juan/code/webgui/src/webview.h", discardable.}
- Evaluate a JavaScript cstring, runs the javascript string on the window
func css(w: Webview; css: cstring): cint {...}{.importc: "webview_inject_css", header: "/home/juan/code/webgui/src/webview.h", discardable.}
- Set a CSS cstring, inject the CSS on the Window
func setTitle(w: Webview; title: cstring) {...}{.importc: "webview_set_title", header: "/home/juan/code/webgui/src/webview.h".}
- Set Title of window
func setColor(w: Webview; red, green, blue, alpha: uint8) {...}{. importc: "webview_set_color", header: "/home/juan/code/webgui/src/webview.h".}
- Set background color of the Window
func setFullscreen(w: Webview; fullscreen: bool) {...}{.importc: "webview_set_fullscreen", header: "/home/juan/code/webgui/src/webview.h".}
- Set fullscreen
func jsDebug(format: cstring) {...}{.varargs, importc: "webview_debug", header: "/home/juan/code/webgui/src/webview.h".}
- console.debug() directly inside the JavaScript context.
func jsLog(s: cstring) {...}{.importc: "webview_print_log", header: "/home/juan/code/webgui/src/webview.h".}
- console.log() directly inside the JavaScript context.
func setUrl(w: Webview; url: cstring) {...}{.importc: "webview_launch_external_URL", header: "/home/juan/code/webgui/src/webview.h".}
- Set the current URL
func setIconify(w: Webview; mustBeIconified: bool) {...}{.importc: "webview_set_iconify", header: "/home/juan/code/webgui/src/webview.h".}
- Set window to be Minimized Iconified
func setBorderless(w: Webview; decorated: bool) {...}{.inline, raises: [], tags: [].}
- Use a window without borders, no close nor minimize buttons.
func setSkipTaskbar(w: Webview; hint: bool) {...}{.inline, raises: [], tags: [].}
- Do not show the window on the Taskbar
func setSize(w: Webview; width: Positive; height: Positive) {...}{.inline, raises: [], tags: [].}
- Resize the window to given size
func setFocus(w: Webview) {...}{.inline, raises: [], tags: [].}
- Force focus on the window
func setOnTop(w: Webview; mustBeOnTop: bool) {...}{.inline, raises: [], tags: [].}
- Force window to be on top of all other windows
func setClipboard(w: Webview; text: cstring) {...}{.inline, raises: [], tags: [].}
- Set a text cstring on the Clipboard, text must not be empty string
func setTrayIcon(w: Webview; path, tooltip: cstring; visible = true) {...}{.inline, raises: [], tags: [].}
- Set a TrayIcon on the corner of the desktop. path is full path to a PNG image icon. Only shows an icon.
func run(w: Webview) {...}{.inline, raises: [], tags: [].}
- run starts the main UI loop until the user closes the window or exit() is called.
func exit(w: Webview) {...}{.inline, raises: [], tags: [].}
- Explicitly Terminate, close, exit, quit.
func currentHtmlPath(filename: static[string] = "index.html"): string {...}{.inline, deprecated.}
- currentSourcePath().splitPath.head / "index.html" for URL of index.html Alias for
Macros
macro bindProcs(w: Webview; scope: string; n: untyped): untyped
-
- Functions must be proc or func; No template nor macro.
- Functions must NOT have return Type, must NOT return anything, use the API.
- To pass return data to the Frontend use the JavaScript API and WebGui API.
- Functions do NOT need the * Star to work. Functions must NOT have Pragmas.
You can bind functions with the signature like:
proc functionName[T, U](argumentString: T): U proc functionName[T](argumentString: T) proc functionName()
Then you can call the function in JavaScript side, like this:
scope.functionName(argumentString)
Example:
let app = newWebView() app.bindProcs("api"): proc changeTitle(title: string) = app.setTitle(title) ## You can call code on the right-side, proc changeCss(stylesh: string) = app.css(stylesh) ## from JavaScript Web Frontend GUI, proc injectJs(jsScript: string) = app.js(jsScript) ## by the function name on the left-side. ## (JS) JavaScript Frontend <-- = --> Nim Backend (Native Code, C Speed)
The only limitation is 1 string argument only, but you can just use JSON.
Templates
template msg(w: Webview; title, msg: string)
- Show one message box
template info(w: Webview; title, msg: string)
- Show one alert box
template warn(w: Webview; title, msg: string)
- Show one warn box
template error(w: Webview; title, msg: string)
- Show one error box
template dialogOpen(w: Webview; title = ""): string
- Opens a dialog that requests filenames from the user. Returns "" if the user closed the dialog without selecting a file.
template dialogSave(w: Webview; title = ""): string
- Opens a dialog that requests a filename to save to from the user. Returns "" if the user closed the dialog without selecting a file.
template dialogOpenDir(w: Webview; title = ""): string
- Opens a dialog that requests a Directory from the user.
template dataUriHtmlHeader(s: string): string
- Data URI for HTML UTF-8 header string. For Mac uses Base64, import base64 to use.
template setTheme(w: Webview; dark: bool)
-
Set Dark Theme or Light Theme on-the-fly, dark = true for Dark, dark = false for Light.
- If --light-theme on commandLineParams() then it will use Light Theme automatically.
template imgLazyLoad(_: Webview; src, id: string; width = ""; heigth = ""; class = ""; alt = ""): string
- HTML Image LazyLoad (Must have an ID!).
template sanitizer(_: Webview; s: string): string
- Sanitize all non-printable and weird characters from a string. import re to use it.
template getLang(_: Webview): string
- Detect the Language of the user, returns a string like "en-US", JavaScript side.
template duckDns(_: Webview; domains: string; token: string; ipv4 = ""; ipv6 = ""; verbose: static[bool] = false; clear: static[bool] = false; noParameters: static[bool] = false; ssl: static[bool] = true): string
- Duck DNS, Free Dynamic DNS Service, use your PC or RaspberryPi as $0 Web Hosting
template setAttribute(_: Webview; id, key, val: string): string
- Sets an attribute value.
template toggleAttribute(_: Webview; id, key: string): string
- Toggles an attribute value. E.g. use it on a readonly attribute.
template removeAttribute(_: Webview; id, key: string): string
- Remove an attribute.
template setText(_: Webview; id, text: string): string
- Sets the Elements innerHtml.
template addText(_: Webview; id, text: string; position = beforeEnd): string
- Appends Plain-Text to an Element by id at position, uses insertAdjacentText(), JavaScript side.
template addHtml(_: Webview; id, html: string; position = beforeEnd): string
- Appends HTML to an Element by id at position, uses insertAdjacentHTML(), JavaScript side.
template removeHtml(_: Webview; id: string): string
- Removes an object by id.
template addElement(_: Webview; id, htmlTag: string; position = beforeEnd): string
- Appends 1 New HTML Element to an Element by id at position, uses insertAdjacentElement(), JavaScript side.
template setBlink(_: Webview; id: string; iterations = 3.byte; duration = 1.byte): string
- <blink> is back!, use with app.css() https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blink#Example
template setCursor(_: Webview; id: string; url: string): string
- Set the mouse Cursor, use with app.css(), PNG, SVG, GIF, JPG, BMP, CUR, Data URI.
template setShake(_: Webview; id: string; effect: CSSShake): string
- Shake Effects, use with app.css(), import strutils to use.
template textareaScroll(_: Webview; id: string; scrollIntoView: static[bool] = false; selectAll: static[bool] = false; copyToClipboard: static[bool] = false): string
-
Scroll a textarea to the bottom, alias for textarea.scrollTop = textarea.scrollHeight;.
- scrollIntoView if true runs textarea.scrollIntoView();.
- selectAll if true runs textarea.select();.
- copyToClipboard if true runs document.execCommand('copy');, requires selectAll = true.
template jsWithDisable(w: Webview; id: string; body: untyped)
-
Disable 1 element, run some code, Enable same element back again. Disables at the start, Enables at the end.
app.jsWithDisable("#myButton"): ## "#myButton" becomes Disabled. slowFunction() ## Code block that takes a while to finish. ## "#myButton" becomes Enabled.
template jsWithHide(w: Webview; id: string; body: untyped)
-
Hide 1 element, run some code, Show same element back again. Hides at the start, Visible at the end.
app.jsWithHide("#myButton"): ## "#myButton" becomes Hidden. slowFunction() ## Code block that takes a while to finish. ## "#myButton" becomes Visible.
template jsWithOpacity(w: Webview; id: string; body: untyped)
-
Opacity 25% on 1 element, run some code, Opacity 100% same element back again. 25% Transparent at the start, Opaque at the end.
app.jsWithOpacity("#myButton"): ## "#myButton" becomes transparent. slowFunction() ## Code block that takes a while to finish. ## "#myButton" becomes Opaque.
template getConfig(filename: string; configObject; compileTime: static[bool] = false): auto
- Config Helper, JSON to Type. Read from config.json, serialize to configObject, return configObject, if compileTime is true all is done compile-time, import json to use it. You must provide 1 configObject that match the config.json structure. Works with ARC.
template setFont(_: Webview; fontName: string): string
- Use a Font from Google Fonts, returns string for app.css(), import uri to use.
template setFont(_: Webview; fontName, element: string): string
- Use a Font from Google Fonts and set it directly on HTML element, returns string for app.css(), import uri to use.
template datePicker(_: Webview; yearID, monthID, dayID: string; year, month, day: Positive): string
- Date Picker improvised using <input> and <datalist> HTML elements, returns string for app.addHtml().
template datetimePicker(_: Webview; yearID, monthID, dayID, hourID, minuteID, secondID: string; year, month, day: Positive; hour = 0.Natural; minute = 0.Natural): string
- Date and Time Picker improvised using <input> and <datalist> HTML elements, returns string for app.addHtml().
template animatedGauge(value: range[-90 .. 90]; width = 255.byte; height = 255.byte; needleColor = "red"; gaugeColor = "grey"; id = ""; class = ""): string
- Animated Gauge, Speedmeter-like, value is Degrees, animates on load and then shows value.