{"id":13727996,"url":"https://lettier.github.io/webviewhs/","last_synced_at":"2025-05-08T00:31:00.272Z","repository":{"id":48909577,"uuid":"141025738","full_name":"lettier/webviewhs","owner":"lettier","description":"🌐 A Haskell binding to the webview library created by Serge Zaitsev.","archived":false,"fork":false,"pushed_at":"2021-07-06T03:52:03.000Z","size":114,"stargazers_count":124,"open_issues_count":6,"forks_count":8,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-10-30T06:07:26.810Z","etag":null,"topics":["bindings","cocoa","desktop","desktop-app","electron","electron-app","gtk","haskell","haskell-bindings","haskell-library","mshtml","native","native-apps","single-page-app","spa","ui","webapp","webkit","webview","windows-desktop"],"latest_commit_sha":null,"homepage":"https://lettier.github.io/webviewhs","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lettier.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-07-15T12:30:22.000Z","updated_at":"2024-10-17T16:42:10.000Z","dependencies_parsed_at":"2022-09-03T01:05:01.631Z","dependency_job_id":null,"html_url":"https://github.com/lettier/webviewhs","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lettier%2Fwebviewhs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lettier%2Fwebviewhs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lettier%2Fwebviewhs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lettier%2Fwebviewhs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lettier","download_url":"https://codeload.github.com/lettier/webviewhs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224679802,"owners_count":17351871,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bindings","cocoa","desktop","desktop-app","electron","electron-app","gtk","haskell","haskell-bindings","haskell-library","mshtml","native","native-apps","single-page-app","spa","ui","webapp","webkit","webview","windows-desktop"],"created_at":"2024-08-03T02:00:35.956Z","updated_at":"2024-11-14T19:30:32.629Z","avatar_url":"https://github.com/lettier.png","language":"Haskell","readme":"![webviewhs logo](https://i.imgur.com/yohrYgX.png)\n\n# What is webviewhs?\n\nwebviewhs is a Haskell binding to the [webview](https://github.com/zserge/webview) library created by\n[Serge Zaitsev](https://github.com/zserge).\n\nAccording to webview:\n\n\u003e [webview is] a tiny cross-platform webview library for C/C++/Golang to build modern cross-platform GUIs.\n\u003e It uses Cocoa/WebKit on macOS, gtk-webkit2 on Linux and MSHTML (IE10/11) on Windows.\n\nFor more information, see the webview\n[README](https://github.com/zserge/webview/blob/d007fc53b107f6043c2a6a3372548dbf59dfe876/README.md).\n\nwebviewhs allows you to create native desktop windows and dialogs—while at the\nsame time—rich web-based UI experiences all wrapped up in the powerful, type-safe embrace of Haskell.\n\nCoupled with [PureScript](http://www.purescript.org/) for the front-end portion,\nyou now have an end-to-end purely functional programming language solution for creating desktop apps.\n\nBe sure to explore the provided [examples](examples).\n\n## How complete is the binding?\n\n- [x] webview\n- [x] webview_init\n- [x] webview_loop\n- [x] webview_eval\n- [x] webview_inject_css\n- [x] webview_set_title\n- [x] webview_set_fullscreen\n- [x] webview_set_color\n- [x] webview_dialog\n- [x] webview_dispatch\n- [x] webview_terminate\n- [x] webview_exit\n- [x] webview_debug\n- [x] webview_print_log\n\n## How do I install webviewhs?\n\nIn your `my-project.cabal` file, list `webviewhs` under `build-depends:` like so:\n\n```yaml\n  build-depends:\n      base \u003e= 4.7 \u0026\u0026 \u003c 5\n    , webviewhs\n```\n\nIf you're using stack \u003e= 1.7.1, put the following in your `stack.yaml`:\n\n```yaml\nextra-deps:\n  - github: lettier/webviewhs\n    commit: # Insert commit SHA1 here.\n```\n\nFor older stack versions, put the following:\n\n```yaml\nextra-deps:\n  - git: https://github.com/lettier/webviewhs.git\n    commit: # Insert commit SHA1 here.\n```\n\nAnd now the run the following.\n\n```bash\nstack install --only-dependencies\n```\n\nIf you're using cabal, run the following:\n\n```bash\ngit clone https://github.com/lettier/webviewhs.git\ncd my-project\ncabal sandbox init\ncabal sandbox add-source ../webviewhs\ncabal --require-sandbox install --only-dependencies\n```\n\nDepending on your cabal version, you may be able to specify the\n[git repository and commit](https://github.com/haskell/cabal/issues/2189#issuecomment-405058663)\nmuch like stack.\n\n## How do I use webviewhs?\n\nIf you want to open up a native desktop window that loads a web page and manages itself,\ndo the following:\n\n```haskell\n{-# LANGUAGE\n    OverloadedStrings\n#-}\n\nimport qualified Graphics.UI.Webviewhs as WHS\n\nmain :: IO ()\nmain = do\n  WHS.createWindowAndBlock\n    WHS.WindowParams\n      { WHS.windowParamsTitle      = \"Test\"\n      , WHS.windowParamsUri        = \"https://lettier.github.io\"\n      , WHS.windowParamsWidth      = 800\n      , WHS.windowParamsHeight     = 600\n      , WHS.windowParamsResizable  = True\n      , WHS.windowParamsDebuggable = True\n      }\n```\n\nIf you want more control over the native desktop window, you could do something like this:\n\n```haskell\n{-# LANGUAGE\n    OverloadedStrings\n  , QuasiQuotes\n#-}\n\nimport Control.Monad\nimport Control.Concurrent\nimport Control.Concurrent.BoundedChan as CCBC\nimport Data.Maybe\nimport Data.Text\nimport qualified Data.Text.Lazy as DTL\nimport Data.Text.Format.Heavy\nimport Language.Javascript.JMacro\nimport qualified Clay\nimport qualified Graphics.UI.Webviewhs as WHS\n\nmain :: IO ()\nmain = do\n  -- Create a channel to communicate between the main thread and another thread you'll create.\n  -- This isn't necessary but it's a great way to communicate between threads.\n  channel \u003c- newBoundedChan 1\n\n  -- withWindowLoop handles the creation, iteration, and deletion of the window.\n  WHS.withWindowLoop\n\n    -- Set the window creation params.\n    WHS.WindowParams\n      { WHS.windowParamsTitle      = \"Test\"\n        -- This could be a localhost URL to your single-page application (SPA).\n      , WHS.windowParamsUri        = \"https://lettier.github.com\"\n      , WHS.windowParamsWidth      = 800\n      , WHS.windowParamsHeight     = 600\n      , WHS.windowParamsResizable  = True\n      , WHS.windowParamsDebuggable = True -- Enables the Web Inspector if using WebKit.\n      }\n\n    -- webview allows you to specify a callback function that can be\n    -- called from the JavaScript side.\n    -- The callback receives a single string parameter.\n    -- This could be unstructured text or unparsed JSON for example.\n    -- You can just print what was received for now.\n    (\\ _window stringFromJavaScript -\u003e print stringFromJavaScript)\n\n    -- This function runs before the loop.\n    (WHS.WithWindowLoopSetUp    (\\ _window -\u003e print \"Setting up.\"))\n\n    -- This function runs after the loop.\n    (WHS.WithWindowLoopTearDown (\\ _window -\u003e print \"Tearing down.\"))\n\n    -- If you don't need to set up and/or tear down anything, you can do this.\n    -- (WHS.WithWindowLoopSetUp    (void . return . const))\n    -- (WHS.WithWindowLoopTearDown (void . return . const))\n\n    -- This function is called continuously.\n    -- Return True to continue the window loop or\n    -- return False to exit the loop and destroy the window.\n    $ \\ window -\u003e do\n\n      -- webviewhs provides log and log'.\n      -- log uses text-format-heavy which provides a\n      -- \"full-featured string formatting function, similar to Python's string.format.\"\n      -- log' takes a simple Text string.\n      -- According to webview, logging will print to\n      -- \"stderr, MacOS Console or [Windows] DebugView.\"\n      let string = \"world\" :: Text\n      WHS.log \"Hello {string}!\" [(\"string\" :: DTL.Text, Variable string)]\n\n      -- webview allows you to run JS inside the window.\n      -- webviewhs comes with runJavaScript and runJavaScript'.\n      -- runJavaScript uses JMacro which is a\n      -- \"simple DSL for lightweight (untyped) programmatic generation of Javascript.\"\n      -- runJavaScript' takes a Text string which may or may not be valid JavaScript.\n      let red = \"red\" :: Text\n      _ \u003c- WHS.runJavaScript\n        window\n\n        -- This changes the web page background color to red.\n        -- Notice that you can use Haskell values inside the JavaScript and\n        -- even use Haskell like syntax.\n        [jmacro|\n          fun setBackgroundColor color { document.body.style.backgroundColor = color; }\n          setTimeout(\n            \\ -\u003e setBackgroundColor `(red)`,\n            5000\n          );\n        |]\n\n      -- webview allows you to inject CSS into the window.\n      -- webviewhs offers injectCss and injectCss'.\n      -- injectCss uses Clay \"a CSS preprocessor like LESS and Sass,\n      -- but implemented as an embedded domain specific language (EDSL) in Haskell.\"\n      -- injectCss' takes a Text string which may or may not be valid CSS.\n      _ \u003c- WHS.injectCss\n        window\n\n        -- This turns all \u003cdiv\u003e text blue.\n        $ Clay.div Clay.?\n          Clay.color \"#0000ff\"\n\n      -- Inside the window loop, create a thread.\n      _ \u003c- forkIO $ do\n        WHS.log' \"Hello from inside a thread.\"\n\n        -- When you're not in the main window UI thread, you'll need to call\n        -- dispatchToMain if you want to interact with the window.\n        -- dispatchToMain will run the given function in the main UI thread.\n        -- Note that dispatchToMain runs the function asynchronously with no guarantee\n        -- as to when it will run.\n        WHS.dispatchToMain\n          window\n          $ \\ window' -\u003e do\n            result \u003c-\n              WHS.runJavaScript\n                window'\n\n                -- This will randomly scroll the web page up and down.\n                [jmacro|\n                  if (Math.random() \u003c 0.1) {\n                    setTimeout(\n                      function() {\n                        window.scrollTo(0, Math.random() * window.innerHeight);\n                      },\n                      10000\n                    );\n                  }\n                |]\n\n            -- runJavaScript returns True if it was successful and\n            -- False if something went wrong.\n            -- Here is an attempt to write the result to the channel.\n            void $ CCBC.tryWriteChan channel result\n\n      -- Exit the loop if you read False from the channel.\n      -- Note that tryReadChan does not block which is\n      -- important when inside the window loop.\n      fromMaybe True \u003c$\u003e tryReadChan channel\n\n  -- At this point,\n  -- the loop has been exited,\n  -- the window has been destroyed,\n  -- and the program will now exit.\n```\n\nFor more ways to use webviewhs,\ntake a look at the [examples](examples) directory.\n\n## What if I don't want clay, jmacro, and text-format-heavy?\n\nwebviewhs has a `light` build flag that removes the dependencies clay, jmacro, and text-format-heavy.\nIn some cases, using the `light` build flag can reduce the final binary size by 77%.\n\nNote that the `light` build flag removes `runJavaScript`, `injectCss`, and `log` from the API.\nYou can still use `runJavaScript'`, `injectCss'`, and `log'`.\n\nIf you're using stack, you can supply the `light` flag in the `stack.yaml` file.\n\n```yaml\nflags:\n  webviewhs:\n    light: true\n```\n\nYou can also supply the `light` flag on the command line like so.\n\n```bash\nstack build --flag webviewhs:light\n```\n\nIf you're using cabal, you'll have to supply a constraint for all configure and install commands.\n\n```bash\n# For configure.\ncabal configure  --constraint=\"webviewhs +light\"\n\n# For install.\ncabal install -j --constraint=\"webviewhs +light\"\n```\n\nThere's currently no way to supply the constraint in the cabal file itself,\nhowever,\nthere is an [open issue](https://github.com/haskell/cabal/issues/2821) about it.\n\nFor more information about using the light version of webviewhs,\ntake a look at the [examples-light](examples-light) directory.\n\n## What is the license?\n\nFor the webviewhs license information, see [LICENSE](LICENSE).\nFor the webview license information, see [deps/webview/LICENSE](deps/webview/LICENSE).\n\n## Who wrote webviewhs?\n\n(C) 2018 David Lettier  \n[lettier.com](https://lettier.com)\n\n## Who wrote webview?\n\nCopyright (c) 2017 Serge Zaitsev\n","funding_links":[],"categories":["👓 Alternatives to the [Electron.js](https://electronjs.org) ⚛"],"sub_categories":["Haskell"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/lettier.github.io%2Fwebviewhs%2F","html_url":"https://awesome.ecosyste.ms/projects/lettier.github.io%2Fwebviewhs%2F","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/lettier.github.io%2Fwebviewhs%2F/lists"}