├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── RELEASE.md ├── Setup.hs ├── client ├── .gitignore ├── DEPENDENCIES ├── config │ ├── dev │ │ └── Try.Config.purs │ └── prod │ │ └── Try.Config.purs ├── dependencies.sh ├── examples │ ├── ADTs.purs │ ├── DoNotation.purs │ ├── Generic.purs │ ├── Loops.purs │ ├── Main.purs │ ├── Operators.purs │ ├── QuickCheck.purs │ ├── Records.purs │ ├── Recursion.purs │ └── TypeClasses.purs ├── package.json ├── packages.dhall ├── public │ ├── css │ │ ├── flare.css │ │ ├── index.css │ │ ├── mathbox.css │ │ ├── slides.css │ │ └── style.css │ ├── frame.html │ ├── img │ │ ├── favicon-black.ico │ │ ├── favicon-black.svg │ │ ├── favicon-white.svg │ │ └── loading.gif │ ├── index.html │ └── js │ │ └── frame.js ├── spago.dhall ├── src │ ├── Main.purs │ └── Try │ │ ├── API.purs │ │ ├── Container.js │ │ ├── Container.purs │ │ ├── Editor.purs │ │ ├── Gist.js │ │ ├── Gist.purs │ │ ├── GitHub.purs │ │ ├── QueryString.js │ │ ├── QueryString.purs │ │ ├── SharedConfig.purs │ │ └── Types.purs ├── test │ ├── Fixture.purs │ ├── Fixture │ │ ├── compile-failure.json │ │ ├── compile-other-error.json │ │ └── compile-success.json │ └── Main.purs └── updateSharedConfigVersions.mjs ├── deploy ├── nginx.conf ├── remote.sh ├── run.sh ├── start └── trypurescript.service ├── prep-release.sh ├── server └── Main.hs ├── stack.yaml ├── stack.yaml.lock ├── staging ├── .gitignore ├── packages.dhall ├── spago.dhall └── src │ ├── TryPureScript.js │ └── TryPureScript.purs └── trypurescript.cabal /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Description of the change** 2 | 3 | Clearly and concisely describe the purpose of the pull request. If this PR relates to an existing issue or change proposal, please link to it. Include any other background context that would help reviewers understand the motivation for this PR. 4 | 5 | --- 6 | 7 | **Checklist:** 8 | 9 | - [ ] Added the change to the changelog's "Unreleased" section with a reference to this PR (e.g. "- Made a change (#0 by @)") 10 | - [ ] Linked any existing issues or proposals that this pull request should close 11 | - [ ] Updated or added relevant documentation 12 | - [ ] Added a test for the contribution (if applicable) 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: [master] 7 | release: 8 | types: [published] 9 | 10 | 11 | env: 12 | SERVER_ASSET: trypurescript-server 13 | CLIENT_ASSET: trypurescript-client 14 | 15 | jobs: 16 | build_server: 17 | name: Build server 18 | # Note that this must be kept in sync with the version of Ubuntu which the 19 | # Try PureScript server is running, otherwise the server binary may fail to 20 | # run. 21 | runs-on: ubuntu-20.04 22 | steps: 23 | - uses: actions/checkout@v2 24 | 25 | - uses: haskell/actions/setup@v1 26 | with: 27 | enable-stack: true 28 | stack-version: "2.5.1" 29 | stack-no-global: true 30 | 31 | - name: Cache dependencies 32 | uses: actions/cache@v2 33 | with: 34 | path: ~/.stack 35 | key: ${{ runner.os }}-stack-${{ hashFiles('stack.yaml.lock') }}-${{ hashFiles('trypurescript.cabal') }} 36 | 37 | - name: Build server code 38 | run: stack --no-terminal -j1 build 39 | 40 | - name: Build server assets 41 | if: github.event_name == 'release' 42 | run: | 43 | mkdir ${{ env.SERVER_ASSET }} 44 | cp $(stack path --dist-dir)/build/trypurescript/trypurescript ${{ env.SERVER_ASSET }}/ 45 | cp LICENSE ${{ env.SERVER_ASSET }}/ 46 | cp -r deploy/ ${{ env.SERVER_ASSET }}/ 47 | cp -r staging/ ${{ env.SERVER_ASSET}}/ 48 | tar czf ${{ env.SERVER_ASSET }}.tar.gz -C ${{ env.SERVER_ASSET }}/ . 49 | 50 | - name: Persist server assets 51 | uses: actions/upload-artifact@v2 52 | if: github.event_name == 'release' 53 | with: 54 | name: ${{ env.SERVER_ASSET }}.tar.gz 55 | path: ${{ env.SERVER_ASSET }}.tar.gz 56 | retention-days: 1 57 | 58 | build_client: 59 | name: Build client 60 | # Note that this must be kept in sync with the version of Ubuntu which the 61 | # Try PureScript server is running, otherwise the server binary may fail to 62 | # run. 63 | runs-on: ubuntu-20.04 64 | steps: 65 | - uses: actions/checkout@v2 66 | - uses: actions/setup-node@v2 67 | 68 | - name: Build client code 69 | run: | 70 | cd client 71 | npm install 72 | npm run build 73 | npm run test 74 | npm run build:production 75 | npm run bundle 76 | 77 | - name: Check SharedConfig.purs versions 78 | run: | 79 | cd client 80 | cp src/Try/SharedConfig.purs sharedConfig.out 81 | node updateSharedConfigVersions.mjs sharedConfig.out 82 | diff src/Try/SharedConfig.purs sharedConfig.out || { 83 | echo 'PureScript and/or package set versions in "client/src/Try/SharedConfig.purs"' 84 | echo 'do not match the versions extracted from "stack.yaml" and "staging/packages.dhall".' 85 | echo 'Please run "cd client && npm run updateConfigVersions". CI will fail until then.' 86 | exit 1 87 | } 88 | 89 | - name: Build client assets 90 | if: github.event_name == 'release' 91 | run: | 92 | mkdir ${{ env.CLIENT_ASSET }} 93 | cp LICENSE ${{ env.CLIENT_ASSET }}/ 94 | cp -r client/public/ ${{ env.CLIENT_ASSET }}/ 95 | tar czf ${{ env.CLIENT_ASSET }}.tar.gz -C ${{ env.CLIENT_ASSET }}/ . 96 | 97 | - name: Persist client assets 98 | uses: actions/upload-artifact@v2 99 | if: github.event_name == 'release' 100 | with: 101 | name: ${{ env.CLIENT_ASSET }}.tar.gz 102 | path: ${{ env.CLIENT_ASSET }}.tar.gz 103 | retention-days: 1 104 | 105 | release: 106 | name: Release 107 | # Note that this must be kept in sync with the version of Ubuntu which the 108 | # Try PureScript server is running, otherwise the server binary may fail to 109 | # run. 110 | runs-on: ubuntu-20.04 111 | if: github.event_name == 'release' 112 | env: 113 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 114 | needs: 115 | - build_server 116 | - build_client 117 | steps: 118 | - name: Retrieve server assets 119 | uses: actions/download-artifact@v2 120 | with: 121 | name: ${{ env.SERVER_ASSET }}.tar.gz 122 | 123 | - name: Retrieve client assets 124 | uses: actions/download-artifact@v2 125 | with: 126 | name: ${{ env.CLIENT_ASSET }}.tar.gz 127 | 128 | - name: Upload server and client assets 129 | uses: softprops/action-gh-release@v1 130 | with: 131 | files: | 132 | ${{ env.SERVER_ASSET }}.tar.gz 133 | ${{ env.CLIENT_ASSET }}.tar.gz 134 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .psc-ide-port 2 | .pulp-cache/ 3 | .psci_modules/ 4 | .stack-work/ 5 | output/ 6 | .psc-package/ 7 | dist/ 8 | .cabal-sandbox/ 9 | 10 | cabal.sandbox.config 11 | *.o 12 | *.hi 13 | *.chi 14 | *.chs.h 15 | *.lksh* 16 | bundle/ 17 | client/public/js/output 18 | client/client.js 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | Notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 4 | 5 | ## [Unreleased] 6 | 7 | Breaking changes: 8 | 9 | New features: 10 | 11 | Bugfixes: 12 | 13 | Other improvements: 14 | 15 | ## [v2023-12-22.1](https://github.com/purescript/trypurescript/releases/tag/v2023-12-22.1) 16 | 17 | Other improvements: 18 | - Bump PureScript to `0.15.13` (#306 by @JordanMartinez) 19 | - Update to latest package set (#306 by @JordanMartinez) 20 | 21 | ## [v2023-07-18.1](https://github.com/purescript/trypurescript/releases/tag/v2023-07-18.1) 22 | 23 | Other improvements: 24 | - Bump PureScript to `0.15.10` (#310 by @JordanMartinez) 25 | - Update to latest package set (#310 by @JordanMartinez) 26 | 27 | ## [v2023-03-06.1](https://github.com/purescript/trypurescript/releases/tag/v2023-03-06.1) 28 | 29 | Other improvements: 30 | - Bump PureScript to `0.15.8` (#305 by @JordanMartinez) 31 | - Update to latest package set (#305 by @JordanMartinez) 32 | 33 | ## [v2022-12-12.1](https://github.com/purescript/trypurescript/releases/tag/v2022-12-12.1) 34 | 35 | Other improvements: 36 | - Update main example to note that editor state is persisted in the URL (#300 by @thomashoneyman) 37 | - Update PureScript to `0.15.7` (#302 by @JordanMartinez) 38 | - Update to latest package set (#302 by @JordanMartinez) 39 | 40 | ## [v2022-09-10.1](https://github.com/purescript/trypurescript/releases/tag/v2022-09-10.1) 41 | 42 | New features: 43 | - Remove `localStorage` for session storage, persist editor state in URL query param (#299 by @ptrfrncsmrph) 44 | 45 | ## [v2022-08-16.1](https://github.com/purescript/trypurescript/releases/tag/v2022-08-16.1) 46 | 47 | Other improvements: 48 | - Update `es-module-shims` to 1.5.12 (#298 by @andys8) 49 | 50 | ## [v2022-08-12.1](https://github.com/purescript/trypurescript/releases/tag/v2022-08-12.1) 51 | 52 | Bugfixes: 53 | - Add missing `react-dom/client` shim (#294 by @andys8) 54 | - Fix double `main` invocation (#295 by @JordanMartinez) 55 | - Stop loading hang when using query params to show compiled JS output (#296 by @JordanMartinez) 56 | 57 | Other improvements: 58 | - Update package set to latest `0.15.4` one (#294) by @andys8) 59 | 60 | ## [v2022-07-21.1](https://github.com/purescript/trypurescript/releases/tag/v2022-07-21.1) 61 | 62 | Bugfixes: 63 | - Fix `Reference Error: main is not defined` bug: (#288 by @JordanMartinez) 64 | - Fix `Unknown type Effect` bug in examples (#292 by @ptrfrncsmrph) 65 | - Fix NPM dependency shims on Firefox 100+ (#289 by @JordanMartinez) 66 | - Fix import maps for React deps by bumping version to 17.0.2 (#289 by @JordanMartinez) 67 | 68 | Other improvements: 69 | - Update `es-module-shims` to 1.5.9 (#289 by @JordanMartinez) 70 | 71 | ## [v2022-07-15.1](https://github.com/purescript/trypurescript/releases/tag/v2022-07-15.1) 72 | 73 | Other improvements: 74 | - Drop requirement that module name be `Main` (#285 by @JordanMartinez) 75 | - Fixes compiler warnings in examples (#286 by @JordanMartinez) 76 | - Support cookbook repo UI recipes by adding element to `frame.html` (#286 by @JordanMartinez) 77 | - Update to latest package set (#287 by @JordanMartinez) 78 | 79 | ## [v2022-07-12.1](https://github.com/purescript/trypurescript/releases/tag/v2022-07-12.1) 80 | 81 | Other improvements: 82 | - Update to PureScript 0.15.4 (#281 by @JordanMartinez) 83 | 84 | ## [v2022-06-24.1](https://github.com/purescript/trypurescript/releases/tag/v2022-06-24.1) 85 | 86 | Other improvements: 87 | - Update to PureScript 0.15.3 (#281 by @JordanMartinez) 88 | - Update codebase to GHC 9.2.3 (#281 by @JordanMartinez) 89 | 90 | ## [v2022-06-18.1](https://github.com/purescript/trypurescript/releases/tag/v2022-06-18.1) 91 | 92 | New features: 93 | - Clearly indicate PureScript and package set version (#280 by @JordanMartinez) 94 | 95 | Bugfixes: 96 | - Stop double `main` invocation by updating `es-module-shims` to 1.5.6 (#279 by @JordanMartinez) 97 | 98 | ## [v2022-06-10.2](https://github.com/purescript/trypurescript/releases/tag/v2022-06-10.2) 99 | 100 | Other improvements: 101 | - Update client to 0.15.2; bundle via esbuild (#278 by @JordanMartinez) 102 | 103 | ## [v2022-06-10.1](https://github.com/purescript/trypurescript/releases/tag/v2022-06-10.1) 104 | 105 | Bugfixes: 106 | - Fix the URL used for getting module dependencies of `Main` (#277 by @JordanMartinez) 107 | - Update alias to refer to correct location on server (#277 by @JordanMartinez) 108 | 109 | ## [v2022-06-08.1](https://github.com/purescript/trypurescript/releases/tag/v2022-06-08.1) 110 | 111 | Breaking changes: 112 | - update compiler to v0.15.2 (#275 by @JordanMartinez) 113 | - Update package set to latest `0.15.2` one (#275 by @JordanMartinez) 114 | 115 | ## [v2022-02-25.1](https://github.com/purescript/trypurescript/releases/tag/v2022-02-25.1) 116 | 117 | Breaking changes: 118 | - Update compiler to v0.14.7 (#271 by @JordanMartinez) 119 | 120 | New features: 121 | 122 | Bugfixes: 123 | 124 | Other improvements: 125 | - Update package set to latest 0.14.5 one (#271 by @JordanMartinez) 126 | 127 | ## [v2022-02-05.1](https://github.com/purescript/trypurescript/releases/tag/v2022-02-05.1) 128 | 129 | Breaking changes: 130 | 131 | New features: 132 | 133 | Bugfixes: 134 | - Use `replaceState` for setting query params (#266 by @ptrfrncsmrph) 135 | - Display missing FFI dependency error message to user (#268 by @ptrfrncsmrph) 136 | 137 | Other improvements: 138 | 139 | ## [v2021-11-30.1](https://github.com/purescript/trypurescript/releases/tag/v2021-11-11.1) 140 | 141 | Breaking changes: 142 | 143 | New features: 144 | 145 | Bugfixes: 146 | 147 | Other improvements: 148 | - Fix double-encoding of quotation marks (#261 by @rhendric) 149 | 150 | ## [v2021-11-11.1](https://github.com/purescript/trypurescript/releases/tag/v2021-11-11.1) 151 | 152 | Breaking changes: 153 | 154 | New features: 155 | 156 | Bugfixes: 157 | - Fixed `encode` not replacing all instances of special characters (#254 by @jy14898) 158 | - Fixed up-to-five-second hangs between the loading icon disappearing and the content rendering (#256 @mikesol) 159 | 160 | Other improvements: 161 | - Update `purescript` dependency to `0.14.5` (#257 by @JordanMartinez) 162 | - Update package set to `psc-0.14.5-20211111` (#260 by @JordanMartinez) 163 | 164 | ## [v2021-08-25.1](https://github.com/purescript/trypurescript/releases/tag/v2021-08-25.1) - 2021-08-25 165 | 166 | Other improvements: 167 | - Update `purescript` dependency to `0.14.4` (#253 by @JordanMartinez) 168 | 169 | ## [v2021-08-23.1](https://github.com/purescript/trypurescript/releases/tag/v2021-08-23.1) - 2021-08-23 170 | 171 | Other improvements: 172 | - Update to the August 23, 2021 package set (#252 by @thomashoneyman) 173 | 174 | ## [v2021-07-07.1](https://github.com/purescript/trypurescript/releases/tag/v2021-07-07.1) - 2021-07-07 175 | 176 | Other improvements: 177 | - Update to build against PureScript 0.14.3 (#238 by @thomashoneyman) 178 | 179 | ## [v2021-07-04.1](https://github.com/purescript/trypurescript/releases/tag/v2021-07-04.1) - 2021-07-04 180 | 181 | Bugfixes: 182 | - Support use of JS `const` keyword in `requireRegex` (#237 by @ptrfrncsmrph) 183 | 184 | ## [v2021-06-18.1](https://github.com/purescript/trypurescript/releases/tag/v2021-06-18.1) - 2021-06-18 185 | 186 | Other improvements: 187 | 188 | - Migrated CI to GitHub Actions (#232 by @thomashoneyman) 189 | - Fixed mangled 'Compiled ModuleName' output (#228 by @JordanMartinez) 190 | - Updated dev instructions: create valid symbolic link across OSes (#226 by @JordanMartinez) 191 | - Added a changelog (#229 by @JordanMartinez) 192 | - Updated PureScript dependency to v0.14.2 (#230 by @JordanMartinez) 193 | - Sped up server slightly by using `rebuildModule'` (#230 by @JordanMartinez) 194 | 195 | ## [v2021-05-29.1](https://github.com/purescript/trypurescript/releases/tag/v2021-05-29.1) - 2021-05-29 196 | 197 | This release officially adds support for PureScript 0.14 to Try PureScript, among many other (largely internal) improvements and updates. 198 | 199 | - Updated the compiler and package set for PureScript 0.14 (#209, #213, #224 by @thomashoneyman, #223 by @JordanMartinez) 200 | - Updated local development instructions (#221 by @JordanMartinez, #225 by @thomashoneyman) 201 | - Added support for loading files from GitHub repositories and migrated examples into the Try PureScript repository (#218 by @thomashoneyman) 202 | - Migrated the client to Halogen from JQuery (#215 by @thomashoneyman) 203 | - Migrated the client to `argonaut-codecs` from `foreign-generic` (#212, #217 by @thomashoneyman) 204 | - Migrated the client to `Aff` from `ContT` (#208 by @thomashoneyman) 205 | - Added fixtures to test API responses (#211 by @thomashoneyman) 206 | - Removed unused pragmas, imports, and definitions from server code (#206 by @thomashoneyman) 207 | - Allowed form submission in the Try PureScript iframe (#203 by @mikesol) 208 | - Switch to use a svg favicon with ico fallback (#191 by @milesfrain) 209 | - Implement `encode` in PureScript instead of in FFI (#186 by @maxdeviant) 210 | 211 | ## [v2020-07-11.1](https://github.com/purescript/trypurescript/releases/tag/v2020-07-11.1) - 2020-07-11 212 | 213 | - Enable automated SSL certificate renewal (#184, @hdgarrood) 214 | - Remove unused `parsec` dependency (#178, @hdgarrood) 215 | - Only listen on 127.0.0.1 (#177, @hdgarrood) 216 | 217 | ## [v2020-05-26.1](https://github.com/purescript/trypurescript/releases/tag/v2020-05-26.1) - 2020-05-26 218 | 219 | - Update to PureScript v0.13.8 (#175, @hdgarrood 220 | - Make the whole package set available (#173, @hdgarrood, @thomashoneyman) 221 | - Do away with version numbers (#176, @hdgarrood) 222 | 223 | ## [v0.13.7](https://github.com/purescript/trypurescript/releases/tag/v0.13.7) - 2020-05-03 224 | 225 | - Fixed help link (#165, @hdgarrood) 226 | - Update API documentation in readme (#168, @hdgarrood) 227 | 228 | ## [v0.13.6](https://github.com/purescript/trypurescript/releases/tag/v0.13.6) - 2020-05-03 229 | 230 | - Updated to v0.13.6 of the PureScript compiler 231 | - Made minor tweaks to get deploys working 232 | 233 | ## [v0.13.5](https://github.com/purescript/trypurescript/releases/tag/v0.13.5) - 2020-05-02 234 | 235 | - Updated to v0.13.5 of the compiler (@natefaubion) 236 | - Removed backends, now serve individual modules on demand (@natefaubion, @gabejohnson, #128, #136) 237 | - Hosted the client ourselves rather than using GH pages 238 | - Put all source files in one branch (@gabejohnson, #135) 239 | - Fixed gist navigation within the iframe (@natefaubion, #140) 240 | - Used different sourceURL syntax per warnings (@natefaubion, #141) 241 | - Switched to spago for managing PS dependencies (@hdgarrood, #147, #150) 242 | - Built the frontend in CI (@hdgarrood, #152) 243 | - Mostly automated deployments (@hdgarrood) 244 | 245 | ## [v0.11.7](https://github.com/purescript/trypurescript/releases/tag/v0.11.7) - 2017-11-30 246 | 247 | Update to 0.11.7 (@Thimoteus) 248 | 249 | ## [v0.11.6.1](https://github.com/purescript/trypurescript/releases/tag/v0.11.6.1) - 2017-09-01 250 | 251 | Return warnings from API 252 | 253 | ## [v0.11.6](https://github.com/purescript/trypurescript/releases/tag/v0.11.6) - 2017-07-11 254 | 255 | Updates for the v0.11.6 compiler 256 | 257 | ## [v0.11.2](https://github.com/purescript/trypurescript/releases/tag/v0.11.2) - 2017-04-02 258 | 259 | Update to 0.11.2 compiler 260 | 261 | ## [v0.11.1](https://github.com/purescript/trypurescript/releases/tag/v0.11.1) - 2017-04-01 262 | 263 | Updates for 0.11.1 compiler. 264 | 265 | ## [v0.10.5](https://github.com/purescript/trypurescript/releases/tag/v0.10.5) - 2017-01-16 266 | 267 | Update compiler and add basic type search 268 | 269 | ## [v0.10.4](https://github.com/purescript/trypurescript/releases/tag/v0.10.4) - 2017-01-02 270 | 271 | Update to compiler v0.10.4 272 | 273 | ## [v0.10.3](https://github.com/purescript/trypurescript/releases/tag/v0.10.3) - 2016-12-18 274 | 275 | Update to 0.10.3, use JSON errors. 276 | 277 | ## [v0.10.2](https://github.com/purescript/trypurescript/releases/tag/v0.10.2) - 2016-11-11 278 | 279 | New version using compiler v0.10.2 280 | 281 | ## [v0.9.1.1](https://github.com/purescript/trypurescript/releases/tag/v0.9.1.1) - 2016-06-17 282 | 283 | ## [v0.9.1](https://github.com/purescript/trypurescript/releases/tag/v0.9.1) - 2016-06-17 284 | 285 | ## [v0.8.2.0](https://github.com/purescript/trypurescript/releases/tag/v0.8.2.0) - 2016-03-11 286 | 287 | Updates for v0.8.2 compiler 288 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-20 PureScript 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Try PureScript 2 | 3 | [![Build Status](https://github.com/purescript/trypurescript/workflows/CI/badge.svg?branch=master)](https://github.com/purescript/trypurescript/actions?query=workflow%3ACI+branch%3Amaster) 4 | 5 | [Try PureScript](https://try.purescript.org) is an online PureScript code editor for quickly experimenting with PureScript code snippets and ideas. It consists of a client and a server component, both of which live within this repository. 6 | 7 | ## Features: 8 | 9 | - Writing code using the [Ace Editor](http://ace.c9.io) 10 | - Automatic compilation 11 | - PureScript syntax highlighting 12 | - Run and print output or show resulting JavaScript 13 | - Multiple view modes: code, output or both 14 | - Shareable code and editor state via URL 15 | - Load PureScript code from GitHub Gists or repository files 16 | 17 | ### Control Features via the Query String 18 | 19 | Most of these features can be controlled not only from the toolbar, but also using the [query parameters](https://en.wikipedia.org/wiki/Query_string): 20 | 21 | - **Load From GitHub Repo**: Load a PureScript file from a GitHub repository using the `github` parameter 22 | - Format: `github=////.purs` 23 | - Example: `github=/purescript/trypurescript/master/client/examples/Main.purs`. 24 | - Notes: the file should be a single PureScript module with the module name `Main`. 25 | 26 | - **Load From Gist**: Load PureScript code from a gist using the `gist` parameter 27 | - Format: `gist=` 28 | - Example: `gist=37c3c97f47a43f20c548` 29 | - Notes: the file should be named `Main.purs` with the module name `Main`. 30 | 31 | - **Load From URL**: Load compressed PureScript code using the `code` parameter 32 | - Managed by Try PureScript and updated on editor state change to create shareable URLs 33 | - Format: `code=` 34 | - Example: `code=LYewJgrgNgpgBAWQIYEsB2cDuALGAnGIA` will set the editor state to the single line `module Main where` 35 | 36 | - **View Mode**: Control the view mode using the `view` parameter 37 | - Options are: `code`, `output`, `both` (default) 38 | - Example: `view=output` will only display the output 39 | 40 | - **Auto Compile**: Automatic compilation can be turned off using the `compile` parameter 41 | - Options are: `true` (default), `false` 42 | - Example: `compile=false` will turn auto compilation off 43 | 44 | - **JavaScript Code Generation**: Print the resulting JavaScript code in the output window instead of the output of the program using the `js` parameter 45 | - Options are: `true`, `false` (default) 46 | - Example: `js=true` will print JavaScript code instead of the program's output 47 | 48 | ### Which Libraries Are Available? 49 | 50 | Try PureScript aims to provide a complete, recent package set from . The available libraries are those listed in [`staging/spago.dhall`](./staging/spago.dhall), at the versions in the package set mentioned in [`staging/packages.dhall`](./staging/packages.dhall). 51 | 52 | ## Development 53 | 54 | ### 1. Shared setup 55 | 56 | These steps should be performed whether you are working on the server, the client, or both. 57 | 58 | ```sh 59 | # Clone into the repository 60 | git clone git@github.com:purescript/trypurescript.git 61 | cd trypurescript 62 | ``` 63 | 64 | ### 2. Local compile server setup 65 | 66 | This step sets up a local server for Try PureScript. You can skip this step if you just want to use the client with the production server. 67 | 68 | ```sh 69 | # Build the trypurescript executable 70 | stack build 71 | 72 | # Set up the PureScript environment for the server 73 | cd staging 74 | spago build 75 | 76 | # Ensure the compiled JavaScript is available to the client via symbolic link. 77 | ln -s "$PWD/output" "$PWD/../client/public/js/output" 78 | 79 | # Then, start the server. 80 | # 81 | # Below, we disable glob expansion via `set -o noglob` to ensure that globs are 82 | # passed to `purs` unchanged. 83 | # 84 | # We run this in a subshell so that setting noglob only lasts for the duration 85 | # of the command and no longer. 86 | (set -o noglob && stack exec trypurescript 8081 $(spago sources)) 87 | 88 | # Should output that it is compiling the sources (first time) 89 | # Then: Setting phasers to stun... (port 8081) (ctrl-c to quit) 90 | ``` 91 | 92 | ### 3. Client setup 93 | 94 | ```sh 95 | # Install development dependencies 96 | cd client 97 | npm install 98 | 99 | # Use `serve:dev` if you are using a local Try PureScript server, 100 | # e.g. you followed the instructions in step 1. 101 | # 102 | # Use `serve:production` if you would like 103 | # to test the client against the production Try PureScript server. 104 | # Note: the production server may not match the package set you have locally. 105 | npm run serve:(dev|production) 106 | 107 | # Try PureScript is now available on localhost:8080 108 | ``` 109 | 110 | ### 4. Choosing a Tag 111 | 112 | The built-in examples for Try PureScript are loaded from this GitHub repository. To change the tag that the examples are loaded from, you'll need to touch three files: 113 | 114 | * `client/config/dev/Try.Config.purs` 115 | * `client/config/prod/Try.Config.purs` 116 | * `client/examples/Main.purs`, in the `fromExample` function. 117 | 118 | If you are preparing a release or if you need to adjust examples in development, you should change the tag in these three places (and ensure you're using the same tag in each place!). 119 | 120 | ## Server API 121 | 122 | The server is a very basic web service which wraps the PureScript compiler, allowing clients to send PureScript code to be compiled and receiving either compiled JS or error messages in response. 123 | It is hosted at . 124 | 125 | ### Compile PureScript code 126 | 127 | #### POST /compile 128 | 129 | - Request body: PureScript code defining a module whose name must be Main 130 | - Status code: 200 (success) 131 | 132 | Response body on compilation success: 133 | 134 | ```javascript 135 | { 136 | "js": "...", // a string containing JavaScript code 137 | "warnings": [ ... ] // an array of warnings, using the same format as the 138 | // compiler's --json-errors flag 139 | } 140 | ``` 141 | 142 | Response body on compilation failure: 143 | 144 | ```javascript 145 | { 146 | "error": { 147 | "tag": "CompilerErrors", 148 | "contents": [ ... ] // an array of errors, using the same format as the 149 | // compiler's --json-errors flag 150 | } 151 | } 152 | ``` 153 | 154 | Response body on other errors (eg, the name of the module in request body was not Main, or the request body was too large) 155 | 156 | ```javascript 157 | { 158 | "error": { 159 | "tag": "OtherError", 160 | "contents": "..." // a string containing an error message 161 | } 162 | } 163 | ``` 164 | 165 | Note that the API returns a 200 response in all of the above cases; in particular, if the code in the request body fails to compile and the API returns errors, this is still considered a success. 166 | Among other things, this makes it easier to use the API from another domain using CORS. 167 | 168 | The output code will contain references to any imported modules using `require` calls. 169 | To run these files in the browser, it is necessary to either use a `require` shim (such as require1k), or replace these calls and deploy a bundle of precompiled modules. 170 | The Try PureScript client uses the first approach. 171 | 172 | #### GET /output/:module/(index.js|foreign.js) 173 | 174 | The server exposes the compiled JS for all of the modules it has access to. 175 | If the compiled JavaScript code in the response includes a `require` call such as `require(../Web.HTML/index.js)`, then the client is expected to arrange things so that this `require` call provides access to the JavaScript code available at the URL path `/output/Web.HTML/index.js`, via a shim or otherwise. 176 | 177 | ### Configuration 178 | 179 | The server application takes the following arguments on the command line: 180 | 181 | - port number 182 | - a list of input source files 183 | 184 | #### Example 185 | 186 | trypurescript 8081 'bower_components/purescript-*/src/**/*.purs' 187 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release 2 | 3 | ## Instructions for Redeploying Try PureScript 4 | 5 | After making a new compiler release, do the following to redeploy Try PureScript using the new compiler. 6 | 7 | 1. Submit a PR with the following changes: 8 | - In `stack.yaml`, 9 | - update the `resolver` to match the same one used in the PureScript repo 10 | - update `purescript` to use its new version. 11 | - Update the package set (see next section's instructions). 12 | - Update the shared config by running `cd client && npm run updateConfigVersions`. 13 | - Update the changelog to include the next release's date. 14 | 2. Once the PR is merged, create a new GitHub tagged release using `vYYYY-MM-DD.X` (where `X` is usually `1` or the release attempt) as the version schema. The release will trigger a GitHub Actions build. 15 | 3. Wait for the GitHub Actions build to finish (it builds the assets) 16 | 4. Run `./deploy/run.sh vX-X-X.1`, replacing `vX-X-X.1` with the version you created. 17 | - Note: if you're updating the compiler, be sure to push the corresponding tag to [`purescript-metadata`](https://github.com/purescript/purescript-metadata). Otherwise, `spago install` will fail on the server and prevent the server from starting. 18 | 19 | ## Updating the Package Set 20 | 21 | The try.purescript.org server only has a limited amount of memory. If the package set we use in deployment is too large, the server will run out of memory. 22 | 23 | Before deploying an updated package set, someone (your reviewer) should check that the memory required to hold the package set's externs files does not exceed that of the try.purescript.org server. 24 | 25 | Update the package set by doing the following. Each step is explained below: 26 | 27 | ### Summary 28 | 29 | ```sh 30 | pushd staging 31 | spago upgrade-set 32 | cat > spago.dhall << EOF 33 | { name = "try-purescript-server" 34 | , dependencies = [] : List Text 35 | , packages = ./packages.dhall 36 | , sources = [ "src/**/*.purs" ] 37 | } 38 | EOF 39 | spago ls packages | cut -f 1 -d ' ' | xargs spago install 40 | popd 41 | pushd client 42 | npm run updateConfigVersions 43 | popd 44 | # add any new shims 45 | # update ES Module Shims (if needed) 46 | ``` 47 | 48 | ### Step-by-Step Explanation 49 | 50 | 1. Update the `upstream` package set in `staging/packages.dhall`: 51 | 52 | ``` 53 | $ pushd staging && spago upgrade-set && popd 54 | ``` 55 | 56 | 2. Set the `dependencies` key in the `spago.dhall` file to be an empty list. This will require a type annotation of `List Text`: 57 | 58 | ```dhall 59 | { name = "try-purescript-server" 60 | , dependencies = [] : List Text 61 | , packages = ./packages.dhall 62 | , sources = [ "src/**/*.purs" ] 63 | } 64 | ``` 65 | 66 | 3. For `staging/spago.dhall`, install all packages in the package set by running this command: 67 | 68 | ``` 69 | $ spago ls packages | cut -f 1 -d ' ' | xargs spago install 70 | ``` 71 | 72 | 4. Update the `client/src/Try/SharedConfig.purs` file by running this command in `client`: 73 | 74 | ```console 75 | $ npm run updateConfigVersions 76 | ``` 77 | 78 | 5. If any packages need NPM dependencies, you can try adding their shims to the import map in `client/public/frame.html` 79 | - Open up the `generator.jspm.io` URL in the comment 80 | - Use the 'Add Dependency' search bar to find the NPM dependency 81 | - If it exists but doesn't exist in that CDN, you can try another one or [open an issue on `jspm/project`](https://github.com/jspm/project#issue-queue-for-the-jspm-cdn) 82 | - Update the version to the one you need once added 83 | - If needed, include other files from that dependency 84 | - Copy and paste the content into the `client/public/frame.html` file 85 | - Ensure `es-module-shims` has version `1.5.9` or greater. 86 | 87 | 6. If `es-module-shims` releases a new version, you can calculate its SHA-384 via 88 | 89 | ```console 90 | $ ESM_VERSION=1.5.5 91 | $ curl -L -o es-module-shims.js "https://ga.jspm.io/npm:es-module-shims@$ESM_VERSION/dist/es-module-shims.js" 92 | $ echo "sha384-$(openssl dgst -sha384 -binary es-module-shims.js | openssl base64 -A)" 93 | $ rm es-module-shims.js 94 | ``` 95 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /package-lock.json 3 | /output/ 4 | /generated-docs/ 5 | /.psc* 6 | /.purs* 7 | /.psa* 8 | /.stack* 9 | /public/js/index.js 10 | .spago/ 11 | -------------------------------------------------------------------------------- /client/config/dev/Try.Config.purs: -------------------------------------------------------------------------------- 1 | module Try.Config where 2 | 3 | import Prelude 4 | 5 | loaderUrl :: String 6 | loaderUrl = "/js/output" 7 | 8 | compileUrl :: String 9 | compileUrl = "http://localhost:8081" 10 | 11 | tag :: String 12 | tag = "master" 13 | 14 | mainGitHubExample :: String 15 | mainGitHubExample = "/purescript/trypurescript/" <> tag <> "/client/examples/Main.purs" 16 | -------------------------------------------------------------------------------- /client/config/prod/Try.Config.purs: -------------------------------------------------------------------------------- 1 | module Try.Config where 2 | 3 | import Prelude 4 | 5 | loaderUrl :: String 6 | loaderUrl = "https://compile.purescript.org/output" 7 | 8 | compileUrl :: String 9 | compileUrl = "https://compile.purescript.org" 10 | 11 | tag :: String 12 | tag = "master" 13 | 14 | mainGitHubExample :: String 15 | mainGitHubExample = "/purescript/trypurescript/" <> tag <> "/client/examples/Main.purs" 16 | -------------------------------------------------------------------------------- /client/dependencies.sh: -------------------------------------------------------------------------------- 1 | echo Try PureScript uses the following dependencies. 2 | echo Their licenses are reproduced here: 3 | 4 | for dir in bower_components/* 5 | do 6 | echo 7 | basename $dir 8 | echo 9 | cat $dir/LICENSE* || (echo 'No license file found. bower.json says:' && cat $dir/bower.json | jq '.license') 10 | done 11 | -------------------------------------------------------------------------------- /client/examples/ADTs.purs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Prelude 4 | 5 | import Effect (Effect) 6 | import Effect.Console (logShow) 7 | import Data.Map (Map, lookup, singleton) 8 | import TryPureScript (render, withConsole) 9 | 10 | -- | A Name consists of a first name and a last name 11 | data Name = Name String String 12 | 13 | -- | With compiler versions >= 0.8.2, we can derive 14 | -- | instances for Eq and Ord, making names comparable. 15 | derive instance eqName :: Eq Name 16 | derive instance ordName :: Ord Name 17 | 18 | -- | The Ord instance allows us to use Names as the 19 | -- | keys in a Map. 20 | phoneBook :: Map Name String 21 | phoneBook = singleton (Name "John" "Smith") "555-555-1234" 22 | 23 | main :: Effect Unit 24 | main = render =<< withConsole do 25 | logShow (lookup (Name "John" "Smith") phoneBook) 26 | -------------------------------------------------------------------------------- /client/examples/DoNotation.purs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Prelude 4 | import Control.MonadPlus (guard) 5 | import Effect (Effect) 6 | import Effect.Console (logShow) 7 | import Data.Array ((..)) 8 | import Data.Foldable (for_) 9 | import TryPureScript (render, withConsole) 10 | 11 | -- Find Pythagorean triples using an array comprehension. 12 | triples :: Int -> Array (Array Int) 13 | triples n = do 14 | z <- 1 .. n 15 | y <- 1 .. z 16 | x <- 1 .. y 17 | guard $ x * x + y * y == z * z 18 | pure [x, y, z] 19 | 20 | main :: Effect Unit 21 | main = render =<< withConsole do 22 | for_ (triples 20) logShow 23 | -------------------------------------------------------------------------------- /client/examples/Generic.purs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Prelude 4 | import Effect (Effect) 5 | import Effect.Console (logShow) 6 | import Data.Generic.Rep (class Generic) 7 | import Data.Eq.Generic (genericEq) 8 | import Data.Ord.Generic (genericCompare) 9 | import Data.Show.Generic (genericShow) 10 | import TryPureScript (render, withConsole) 11 | 12 | data Address = Address 13 | { city :: String 14 | , state :: String 15 | } 16 | 17 | data Person = Person 18 | { first :: String 19 | , last :: String 20 | , address :: Address 21 | } 22 | 23 | -- Generic instances can be derived by the compiler, 24 | -- using the derive keyword: 25 | derive instance genericAddress :: Generic Address _ 26 | 27 | derive instance genericPerson :: Generic Person _ 28 | 29 | -- Now we can write instances for standard type classes 30 | -- (Show, Eq, Ord) by using standard definitions 31 | instance showAddress :: Show Address where 32 | show = genericShow 33 | 34 | instance eqAddress :: Eq Address where 35 | eq = genericEq 36 | 37 | instance ordAddress :: Ord Address where 38 | compare = genericCompare 39 | 40 | instance showPerson :: Show Person where 41 | show = genericShow 42 | 43 | instance eqPerson :: Eq Person where 44 | eq = genericEq 45 | 46 | instance ordPerson :: Ord Person where 47 | compare = genericCompare 48 | 49 | main :: Effect Unit 50 | main = render =<< withConsole do 51 | logShow $ Person 52 | { first: "John" 53 | , last: "Smith" 54 | , address: Address 55 | { city: "Faketown" 56 | , state: "CA" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /client/examples/Loops.purs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Prelude 4 | 5 | import Effect (Effect) 6 | import Effect.Console (log) 7 | import Data.Array ((..)) 8 | import Data.Foldable (for_) 9 | import TryPureScript (render, withConsole) 10 | 11 | main :: Effect Unit 12 | main = render =<< withConsole do 13 | for_ (10 .. 1) \n -> log (show n <> "...") 14 | log "Lift off!" 15 | -------------------------------------------------------------------------------- /client/examples/Main.purs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Prelude 4 | 5 | import Data.Foldable (fold) 6 | import Effect (Effect) 7 | import TryPureScript (h1, h2, p, text, list, indent, link, render, code) 8 | 9 | main :: Effect Unit 10 | main = 11 | render $ fold 12 | [ h1 (text "Try PureScript!") 13 | , p (text "Try out the examples below, or create your own!") 14 | , h2 (text "Examples") 15 | , list (map fromExample examples) 16 | , h2 (text "Share Your Code") 17 | , p (text "The contents of the text editor are stored in the URL. To share your code you can simply copy the URL.") 18 | , p (text "Additionally, a PureScript file can be loaded from GitHub from a gist or a repository. To share code using a gist, include the gist ID in the URL as follows:") 19 | , indent (p (code (text " try.purescript.org?gist="))) 20 | , p (fold 21 | [ text "The gist should contain PureScript module named " 22 | , code (text "Main") 23 | , text " in a file named " 24 | , code (text "Main.purs") 25 | , text " containing your PureScript code." 26 | ]) 27 | , p (text "To share code from a repository, include the path to the source file as follows:") 28 | , indent (p (code (text " try.purescript.org?github=////.purs"))) 29 | , p (fold 30 | [ text "The file should be a PureScript module named " 31 | , code (text "Main") 32 | , text " containing your PureScript code." 33 | ]) 34 | ] 35 | where 36 | fromExample { title, source } = 37 | link ("https://github.com/purescript/trypurescript/master/client/examples/" <> source) (text title) 38 | 39 | examples = 40 | [ { title: "Algebraic Data Types" 41 | , source: "ADTs.purs" 42 | } 43 | , { title: "Loops" 44 | , source: "Loops.purs" 45 | } 46 | , { title: "Operators" 47 | , source: "Operators.purs" 48 | } 49 | , { title: "Records" 50 | , source: "Records.purs" 51 | } 52 | , { title: "Recursion" 53 | , source: "Recursion.purs" 54 | } 55 | , { title: "Do Notation" 56 | , source: "DoNotation.purs" 57 | } 58 | , { title: "Type Classes" 59 | , source: "TypeClasses.purs" 60 | } 61 | , { title: "Generic Programming" 62 | , source: "Generic.purs" 63 | } 64 | , { title: "QuickCheck" 65 | , source: "QuickCheck.purs" 66 | } 67 | ] 68 | -------------------------------------------------------------------------------- /client/examples/Operators.purs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Prelude 4 | import Effect (Effect) 5 | import Effect.Console (log) 6 | import TryPureScript (render, withConsole) 7 | 8 | type FilePath = String 9 | 10 | subdirectory :: FilePath -> FilePath -> FilePath 11 | subdirectory p1 p2 = p1 <> "/" <> p2 12 | 13 | -- Functions can be given an infix alias 14 | -- The generated code will still use the original function name 15 | infixl 5 subdirectory as 16 | 17 | filepath :: FilePath 18 | filepath = "usr" "local" "bin" 19 | 20 | main :: Effect Unit 21 | main = render =<< withConsole do 22 | log filepath 23 | -------------------------------------------------------------------------------- /client/examples/QuickCheck.purs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Prelude 4 | import Data.Array (sort) 5 | import Effect (Effect) 6 | import Test.QuickCheck (quickCheck, (===)) 7 | import TryPureScript (render, withConsole, h1, h2, p, text) 8 | 9 | main :: Effect Unit 10 | main = do 11 | render $ h1 $ text "QuickCheck" 12 | render $ p $ text """QuickCheck is a Haskell library which allows us to assert properties 13 | hold for our functions. QuickCheck uses type classes to generate 14 | random test cases to verify those properties. 15 | purescript-quickcheck is a port of parts of the QuickCheck library to 16 | PureScript.""" 17 | render $ h2 $ text "Sort function is idempotent" 18 | render =<< withConsole do 19 | quickCheck \(xs :: Array Int) -> sort (sort xs) === sort xs 20 | render $ h2 $ text "Every array is sorted" 21 | render $ p $ text "This test should fail on some array which is not sorted" 22 | render =<< withConsole do 23 | quickCheck \(xs :: Array Int) -> sort xs === xs 24 | -------------------------------------------------------------------------------- /client/examples/Records.purs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Prelude 4 | import Effect (Effect) 5 | import Effect.Console (log) 6 | import TryPureScript (render, withConsole) 7 | 8 | -- We can write functions which require certain record labels... 9 | showPerson :: forall r. { firstName :: String, lastName :: String | r } -> String 10 | showPerson o = o.lastName <> ", " <> o.firstName 11 | 12 | -- ... but we are free to call those functions with any 13 | -- additional arguments, such as "age" here. 14 | main :: Effect Unit 15 | main = render =<< withConsole do 16 | log $ showPerson 17 | { firstName: "John" 18 | , lastName: "Smith" 19 | , age: 30 20 | } 21 | -------------------------------------------------------------------------------- /client/examples/Recursion.purs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Prelude 4 | import Effect (Effect) 5 | import Effect.Console (logShow) 6 | import TryPureScript (render, withConsole) 7 | 8 | isOdd :: Int -> Boolean 9 | isOdd 0 = false 10 | isOdd n = isEven (n - 1) 11 | 12 | isEven :: Int -> Boolean 13 | isEven 0 = true 14 | isEven n = isOdd (n - 1) 15 | 16 | main :: Effect Unit 17 | main = render =<< withConsole do 18 | logShow $ isEven 1000 19 | logShow $ isEven 1001 20 | -------------------------------------------------------------------------------- /client/examples/TypeClasses.purs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Prelude 4 | import Effect (Effect) 5 | import Effect.Console (log) 6 | import TryPureScript (render, withConsole) 7 | 8 | -- A type class for types which can be used with 9 | -- string interpolation. 10 | class Interpolate a where 11 | interpolate :: a -> String 12 | 13 | instance interpolateString :: Interpolate String where 14 | interpolate = identity 15 | 16 | instance interpolateInt :: Interpolate Int where 17 | interpolate = show 18 | 19 | -- A type class for printf functions 20 | -- (each list of argument types will define a type class instance) 21 | class Printf r where 22 | printfWith :: String -> r 23 | 24 | -- An instance for no function arguments 25 | -- (just return the accumulated string) 26 | instance printfString :: Printf String where 27 | printfWith = identity 28 | 29 | -- An instance for adding another argument whose 30 | -- type is an instance of Interpolate 31 | instance printfShow :: (Interpolate a, Printf r) => Printf (a -> r) where 32 | printfWith s a = printfWith (s <> interpolate a) 33 | 34 | -- Our generic printf function 35 | printf :: forall r. (Printf r) => r 36 | printf = printfWith "" 37 | 38 | -- Now we can create custom formatters using different argument 39 | -- types 40 | debug :: String -> Int -> String -> String 41 | debug uri status msg = printf "[" uri "] " status ": " msg 42 | 43 | main :: Effect Unit 44 | main = render =<< withConsole do 45 | log $ debug "http://www.purescript.org" 200 "OK" 46 | log $ debug "http://bad.purescript.org" 404 "Not found" 47 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "trypurescript-client", 3 | "private": true, 4 | "scripts": { 5 | "clean": "rimraf output", 6 | "test": "spago test --path config/dev/Try.Config.purs", 7 | "build": "spago build --path config/dev/Try.Config.purs", 8 | "build:dev": "spago bundle-app --path config/dev/Try.Config.purs --to client.js", 9 | "build:production": "spago bundle-app --path config/prod/Try.Config.purs --purs-args '--censor-lib --strict' --to client.js", 10 | "bundle": "esbuild --outfile=public/js/index.js --bundle --minify --platform=browser --format=iife --tree-shaking=true client.js", 11 | "serve": "http-server public/ -o / --cors=\"Access-Control-Allow-Origin: *\" -c-1", 12 | "serve:dev": "npm run build:dev && npm run bundle && npm run serve", 13 | "serve:production": "npm run build:production && npm run bundle && npm run serve", 14 | "updateConfigVersions": "node updateSharedConfigVersions.mjs src/Try/SharedConfig.purs" 15 | }, 16 | "devDependencies": { 17 | "esbuild": "^0.14.43", 18 | "http-server": "^14.1.0", 19 | "purescript": "^0.15.2", 20 | "purescript-psa": "^0.8.2", 21 | "rimraf": "^2.5.4", 22 | "spago": "^0.20.9" 23 | }, 24 | "dependencies": { 25 | "ace-builds": "^1.5.0", 26 | "jquery": "^1.12.4", 27 | "lz-string": "^1.4.4" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /client/packages.dhall: -------------------------------------------------------------------------------- 1 | let upstream = 2 | https://github.com/purescript/package-sets/releases/download/psc-0.15.4-20220808/packages.dhall 3 | sha256:60eee64b04ca0013fae3e02a69fc3b176105c6baa2f31865c67cd5f881a412fd 4 | 5 | in upstream 6 | -------------------------------------------------------------------------------- /client/public/css/flare.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0} 2 | 3 | @import url(https://fonts.googleapis.com/css?family=Source+Code+Pro:400,700|Roboto:400,700); 4 | 5 | body { 6 | margin: 20px; 7 | } 8 | 9 | body, p, input, label, legend, h1, h2, h3, h4 { 10 | font-family: 'Roboto', sans-serif; 11 | } 12 | 13 | a, a:visited, a:active, a:hover { 14 | color: #cc0000; 15 | } 16 | 17 | .flare-input > label { 18 | display: inline-block; 19 | width: 150px; 20 | } 21 | 22 | .flare-input { 23 | margin-bottom: 10px; 24 | } 25 | 26 | .flare-input-number { 27 | width: 85px; 28 | } 29 | 30 | input[type="range"] { 31 | width: 150px; 32 | border: 1px solid transparent; 33 | } 34 | 35 | input:invalid { 36 | background-color: #DEAFB4; 37 | } 38 | 39 | label { 40 | font-weight: bold; 41 | } 42 | 43 | fieldset.flare-input-radioGroup { 44 | margin: 0px; 45 | margin-top: 15px; 46 | border: none; 47 | padding: 0px; 48 | } 49 | 50 | .flare-input-radioGroup legend { 51 | padding: 0px; 52 | width: 150px; 53 | float: left; 54 | font-weight: bold; 55 | } 56 | 57 | .flare-input-radioGroup label { 58 | margin-left: 3px; 59 | margin-right: 15px; 60 | cursor: pointer; 61 | font-weight: normal; 62 | } 63 | 64 | code { 65 | background-color: #f0f0f0; 66 | padding-left: 3px; 67 | padding-right: 3px; 68 | } 69 | 70 | .sparkle-test pre, 71 | .sparkle-test code, 72 | .sparkle-test input, 73 | .sparkle-test label, 74 | .sparkle-test legend, 75 | .sparkle-test select, 76 | .sparkle-test textarea { 77 | font-family: 'Source Code Pro', monospace; 78 | } 79 | 80 | fieldset.sparkle-test { 81 | margin-top: 15px; 82 | margin-bottom: 40px; 83 | max-width: 800px; 84 | border: 1px solid #666; 85 | border-radius: 3px; 86 | padding: .35em .625em .75em; 87 | } 88 | 89 | fieldset.sparkle-test > legend { 90 | background-color: #f0f0f0; 91 | border: 1px solid #666; 92 | border-radius: 3px; 93 | float: initial; 94 | width: initial; 95 | } 96 | 97 | .sparkle-test pre { 98 | background-color: #f0f0f0; 99 | padding: 10px; 100 | max-width: 780px; 101 | 102 | /* http://stackoverflow.com/a/248013/704831 */ 103 | white-space: pre-wrap; /* CSS 3 */ 104 | white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ 105 | white-space: -pre-wrap; /* Opera 4-6 */ 106 | white-space: -o-pre-wrap; /* Opera 7 */ 107 | word-wrap: break-word; /* Internet Explorer 5.5+ */ 108 | } 109 | 110 | .sparkle-test .flare-input { 111 | margin-bottom: 5px; 112 | } 113 | 114 | .sparkle-test input:not([type="range"]) { 115 | padding: 3px; 116 | border-radius: 3px; 117 | border: 1px solid #888; 118 | } 119 | 120 | .sparkle-test input:invalid { 121 | background-color: #DEAFB4; 122 | } 123 | 124 | .sparkle-test .flare-input-number, 125 | .sparkle-test .flare-input-int-number { 126 | width: 80px; 127 | } 128 | 129 | .sparkle-test fieldset { 130 | border-radius: 3px; 131 | margin-top: 5px; 132 | margin-bottom: 20px; 133 | } 134 | 135 | .sparkle-test label { 136 | width: 160px; 137 | display: inline-block; 138 | } 139 | 140 | .sparkle-test legend { 141 | padding: 2px 6px; 142 | font-weight: 700; 143 | } 144 | 145 | .sparkle-test .flare-input-list button { 146 | margin-left: 5px; 147 | margin-right: 5px; 148 | } 149 | 150 | .sparkle-test .sparkle-okay { 151 | background-color: #B4DEAF; 152 | } 153 | 154 | .sparkle-test .sparkle-warn { 155 | background-color: #DEAFB4; 156 | } 157 | 158 | .sparkle-string { 159 | color: #E91E63; 160 | } 161 | 162 | .sparkle-number { 163 | color: #1339E8; 164 | } 165 | 166 | .sparkle-boolean { 167 | font-weight: bold; 168 | } 169 | 170 | .sparkle-constructor { 171 | font-weight: bold; 172 | } 173 | 174 | .sparkle-record-field { 175 | font-style: italic; 176 | } 177 | 178 | .sparkle-color { 179 | width: 1em; 180 | height: 1em; 181 | margin-right: 0.5em; 182 | display: inline-block; 183 | } 184 | 185 | .sparkle-tooltip { 186 | cursor: default; 187 | } 188 | 189 | .sparkle-tooltip:hover { 190 | background-color: #f2e7a6; 191 | } 192 | 193 | .sparkle-tooltip > .sparkle-tooltip:hover { 194 | background-color: #e9d563; 195 | } 196 | 197 | .sparkle-tooltip > .sparkle-tooltip > .sparkle-tooltip:hover { 198 | background-color: #dfc220; 199 | } 200 | 201 | .sparkle-tooltip > .sparkle-tooltip > .sparkle-tooltip > .sparkle-tooltip:hover { 202 | background-color: #938015; 203 | } 204 | 205 | .sparkle-tooltip > .sparkle-tooltip > .sparkle-tooltip > .sparkle-tooltip > .sparkle-tooltip:hover { 206 | background-color: #62560e; 207 | } 208 | -------------------------------------------------------------------------------- /client/public/css/index.css: -------------------------------------------------------------------------------- 1 | /* Page layout */ 2 | 3 | html, body { 4 | height: 100%; 5 | } 6 | 7 | body { 8 | font-family: 'Roboto', sans-serif; 9 | margin: 0; 10 | color: rgb(29, 34, 45); 11 | } 12 | 13 | #wrapper { 14 | display: flex; 15 | flex-direction: column; 16 | height: 100vh; 17 | } 18 | 19 | #body { 20 | display: flex; 21 | flex-direction: column; 22 | flex: 1; 23 | } 24 | 25 | #menu { 26 | background-color: #1d222d; 27 | color: white; 28 | vertical-align: middle; 29 | margin: 0; 30 | padding: 0; 31 | } 32 | 33 | #menu input { 34 | vertical-align: middle; 35 | margin-bottom: 5px; 36 | cursor: pointer; 37 | } 38 | 39 | #home_link { 40 | padding: 0px 10px; 41 | text-decoration: none; 42 | text-align: center; 43 | font-weight: bold; 44 | } 45 | 46 | #home_link > img { 47 | vertical-align: middle; 48 | } 49 | 50 | .menu-item { 51 | list-style: none; 52 | display: inline-block; 53 | border-left: 1px solid #454648; 54 | min-width: 90px; 55 | } 56 | 57 | .menu-item a { 58 | color: white; 59 | text-decoration: none; 60 | } 61 | 62 | .menu-item > label, .menu-item > a > label { 63 | text-align: center; 64 | line-height: 40px; 65 | padding: 0px 10px; 66 | display: block; 67 | } 68 | 69 | .menu-item label { 70 | cursor: pointer; 71 | } 72 | 73 | .menu-item:hover { 74 | background-color: black; 75 | } 76 | 77 | .menu-item input[type="checkbox"] { 78 | display: none; 79 | } 80 | 81 | .menu-item input[type="checkbox"]:checked + label { 82 | background-color: #8490a9; 83 | } 84 | 85 | .menu-item input[type="checkbox"]:not(:checked) + label { 86 | text-decoration: line-through; 87 | color: #ababab; 88 | } 89 | 90 | .menu-dropdown { 91 | position: relative; 92 | } 93 | 94 | .menu-dropdown > label { 95 | display: block; 96 | } 97 | 98 | .menu-dropdown > label:after { 99 | content: ' ▾'; 100 | } 101 | 102 | .menu-dropdown > ul { 103 | position:absolute; 104 | top: 40px; 105 | left: 0px; 106 | min-width: 100%; 107 | display: none; 108 | margin: 0px; 109 | padding: 0px; 110 | background-color: #1d222d; 111 | list-style: none; 112 | z-index: 100000; 113 | } 114 | 115 | .menu-dropdown > ul > li { 116 | margin: 0; 117 | padding: 0; 118 | } 119 | 120 | .menu-dropdown:hover > ul { 121 | display: inherit; 122 | } 123 | 124 | .menu-dropdown li label { 125 | display: block; 126 | padding: 10px 8px; 127 | margin: 0; 128 | } 129 | 130 | .menu-dropdown label:hover { 131 | background-color: black; 132 | } 133 | 134 | .menu-dropdown input[type="radio"] { 135 | display:none; 136 | } 137 | 138 | .menu-dropdown input[type="radio"]:checked + label { 139 | color: #c4953a; 140 | } 141 | 142 | .menu-dropdown a { 143 | display: block; 144 | color: white; 145 | } 146 | 147 | .menu-dropdown a:visited { 148 | color: white; 149 | } 150 | 151 | #editor_view { 152 | display: flex; 153 | flex-direction: row; 154 | flex: 1; 155 | margin-top: 0px; 156 | position: relative; 157 | } 158 | 159 | #editor_view[data-view-mode="output"] #column1 { 160 | display: none; 161 | } 162 | 163 | #editor_view[data-view-mode="code"] #column2_wrapper { 164 | display: none; 165 | } 166 | 167 | #column1, #column2_wrapper { 168 | position: relative; 169 | flex: 1; 170 | } 171 | 172 | #column2_wrapper { 173 | position: relative; 174 | -webkit-overflow-scrolling: touch; 175 | overflow: auto; 176 | } 177 | 178 | #code { 179 | position: absolute; 180 | top: 0; 181 | left: 0; 182 | right: 0; 183 | bottom: 0; 184 | font-size: 13px; 185 | } 186 | 187 | .separator { 188 | flex-basis: 5px; 189 | flex-grow: 0; 190 | flex-shrink: 0; 191 | background-color: rgb(250, 250, 250); 192 | } 193 | 194 | .error { 195 | position: absolute; 196 | z-index: 20; 197 | border-bottom: 2px dotted red; 198 | } 199 | 200 | .warning { 201 | position: absolute; 202 | z-index: 20; 203 | border-bottom: 2px dotted #c4953a; 204 | } 205 | 206 | .ace_gutter-tooltip { 207 | white-space: pre-wrap; 208 | } 209 | 210 | .error-banner { 211 | font-family: 'Roboto Slab', serif; 212 | padding: 10px 20px; 213 | } 214 | 215 | pre { 216 | padding: 20px; 217 | font-family: 'Monaco', monospace; 218 | } 219 | 220 | pre code { 221 | background: none; 222 | border: 0; 223 | margin: 0; 224 | overflow-x: auto; 225 | white-space: pre-wrap; 226 | white-space: -moz-pre-wrap; 227 | white-space: -pre-wrap; 228 | white-space: -o-pre-wrap; 229 | word-wrap: break-word; 230 | } 231 | 232 | iframe { 233 | position: absolute; 234 | left: 0; 235 | right: 0; 236 | width: 100%; 237 | height: 100%; 238 | border: 0; 239 | } 240 | 241 | #loading { 242 | position: absolute; 243 | top: 0; 244 | left: 0; 245 | width: 100%; 246 | height: 100%; 247 | background: white url(../img/loading.gif); 248 | background-position: center center; 249 | background-repeat: no-repeat; 250 | opacity: 0.85; 251 | z-index: 10000; 252 | } 253 | 254 | #code { 255 | overflow: visible; 256 | } 257 | 258 | .mobile-banner { 259 | background: #dabf8b; 260 | padding: 5px; 261 | border-bottom: 1px solid #1d222d; 262 | font-size: 14px; 263 | margin-bottom: 10px; 264 | } 265 | 266 | 267 | footer { 268 | background-color: #1d222d; 269 | display: flex; 270 | flex-direction: row; 271 | justify-content: center; 272 | gap: 5px; 273 | } 274 | 275 | footer .footer-separator { 276 | color: white; 277 | } 278 | 279 | footer .footer-link { 280 | display: flex; 281 | min-width: 300px; 282 | gap: 2px; 283 | color: white; 284 | } 285 | 286 | footer .footer-link a { 287 | color: white; 288 | } 289 | 290 | footer .footer-link a:visited { 291 | color: white; 292 | } 293 | 294 | @media all and (max-width: 720px) { 295 | .no-mobile { 296 | display: none; 297 | } 298 | 299 | footer { 300 | flex-direction: column; 301 | align-items: center; 302 | } 303 | 304 | footer .footer-link { 305 | min-width: initial; 306 | } 307 | } 308 | 309 | @media all and (min-width: 720px) { 310 | .mobile-only { 311 | display: none; 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /client/public/css/mathbox.css: -------------------------------------------------------------------------------- 1 | .shadergraph-graph { 2 | font: 12px sans-serif; 3 | line-height: 25px; 4 | position: relative; 5 | } 6 | .shadergraph-graph:after { 7 | content: ' '; 8 | display: block; 9 | height: 0; 10 | font-size: 0; 11 | clear: both; 12 | } 13 | .shadergraph-graph svg { 14 | pointer-events: none; 15 | } 16 | .shadergraph-clear { 17 | clear: both; 18 | } 19 | .shadergraph-graph svg { 20 | position: absolute; 21 | left: 0; 22 | right: 0; 23 | top: 0; 24 | bottom: 0; 25 | width: auto; 26 | height: auto; 27 | } 28 | .shadergraph-column { 29 | float: left; 30 | } 31 | .shadergraph-node .shadergraph-graph { 32 | float: left; 33 | clear: both; 34 | overflow: visible; 35 | } 36 | .shadergraph-node .shadergraph-graph .shadergraph-node { 37 | margin: 5px 15px 15px; 38 | } 39 | .shadergraph-node { 40 | margin: 5px 15px 25px; 41 | background: rgba(0, 0, 0, .1); 42 | border-radius: 5px; 43 | box-shadow: 0 1px 2px rgba(0, 0, 0, .2), 44 | 0 1px 10px rgba(0, 0, 0, .2); 45 | min-height: 35px; 46 | float: left; 47 | clear: left; 48 | position: relative; 49 | } 50 | .shadergraph-type { 51 | font-weight: bold; 52 | } 53 | .shadergraph-header { 54 | font-weight: bold; 55 | text-align: center; 56 | height: 25px; 57 | background: rgba(0, 0, 0, .3); 58 | text-shadow: 0 1px 2px rgba(0, 0, 0, .25); 59 | color: #fff; 60 | border-top-left-radius: 5px; 61 | border-top-right-radius: 5px; 62 | margin-bottom: 5px; 63 | padding: 0 10px; 64 | } 65 | .shadergraph-outlet div { 66 | } 67 | .shadergraph-outlet-in .shadergraph-name { 68 | margin-right: 7px; 69 | } 70 | .shadergraph-outlet-out .shadergraph-name { 71 | margin-left: 7px; 72 | } 73 | 74 | .shadergraph-name { 75 | margin: 0 4px; 76 | } 77 | .shadergraph-point { 78 | margin: 6px; 79 | width: 11px; 80 | height: 11px; 81 | border-radius: 7.5px; 82 | background: rgba(255, 255, 255, 1); 83 | } 84 | .shadergraph-outlet-in { 85 | float: left; 86 | clear: left; 87 | } 88 | .shadergraph-outlet-in div { 89 | float: left; 90 | } 91 | .shadergraph-outlet-out { 92 | float: right; 93 | clear: right; 94 | } 95 | .shadergraph-outlet-out div { 96 | float: right; 97 | } 98 | 99 | .shadergraph-node-callback { 100 | background: rgba(205, 209, 221, .5); 101 | box-shadow: 0 1px 2px rgba(0, 10, 40, .2), 102 | 0 1px 10px rgba(0, 10, 40, .2); 103 | } 104 | .shadergraph-node-callback > .shadergraph-header { 105 | background: rgba(0, 20, 80, .3); 106 | } 107 | .shadergraph-graph .shadergraph-graph .shadergraph-node-callback { 108 | background: rgba(0, 20, 80, .1); 109 | } 110 | 111 | .shadergraph-node-call { 112 | background: rgba(209, 221, 205, .5); 113 | box-shadow: 0 1px 2px rgba(10, 40, 0, .2), 114 | 0 1px 10px rgba(10, 40, 0, .2); 115 | } 116 | .shadergraph-node-call > .shadergraph-header { 117 | background: rgba(20, 80, 0, .3); 118 | } 119 | .shadergraph-graph .shadergraph-graph .shadergraph-node-call { 120 | background: rgba(20, 80, 0, .1); 121 | } 122 | 123 | .shadergraph-node-isolate { 124 | background: rgba(221, 205, 209, .5); 125 | box-shadow: 0 1px 2px rgba(40, 0, 10, .2), 126 | 0 1px 10px rgba(40, 0, 10, .2); 127 | } 128 | .shadergraph-node-isolate > .shadergraph-header { 129 | background: rgba(80, 0, 20, .3); 130 | } 131 | .shadergraph-graph .shadergraph-graph .shadergraph-node-isolate { 132 | background: rgba(80, 0, 20, .1); 133 | } 134 | 135 | .shadergraph-node.shadergraph-has-code { 136 | cursor: pointer; 137 | } 138 | .shadergraph-node.shadergraph-has-code::before { 139 | position: absolute; 140 | content: ' '; 141 | top: 0; 142 | left: 0; 143 | right: 0; 144 | bottom: 0; 145 | display: none; 146 | border: 2px solid rgba(0, 0, 0, .25); 147 | border-radius: 5px; 148 | } 149 | .shadergraph-node.shadergraph-has-code:hover::before { 150 | display: block; 151 | } 152 | .shadergraph-code { 153 | z-index: 10000; 154 | display: none; 155 | position: absolute; 156 | background: #fff; 157 | color: #000; 158 | white-space: pre; 159 | padding: 10px; 160 | border-radius: 5px; 161 | box-shadow: 0 1px 2px rgba(0, 0, 0, .2), 162 | 0 1px 10px rgba(0, 0, 0, .2); 163 | font-family: monospace; 164 | font-size: 10px; 165 | line-height: 12px; 166 | } 167 | 168 | .shadergraph-overlay { 169 | position: fixed; 170 | top: 50%; 171 | left: 0; 172 | right: 0; 173 | bottom: 0; 174 | background: #fff; 175 | border-top: 1px solid #CCC; 176 | } 177 | .shadergraph-overlay .shadergraph-view { 178 | position: absolute; 179 | left: 0; 180 | top: 0; 181 | right: 0; 182 | bottom: 0; 183 | overflow: auto; 184 | } 185 | .shadergraph-overlay .shadergraph-inside { 186 | width: 4000px; 187 | min-height: 100%; 188 | box-sizing: border-box; 189 | } 190 | .shadergraph-overlay .shadergraph-close { 191 | position: absolute; 192 | top: 5px; 193 | right: 5px; 194 | padding: 4px; 195 | border-radius: 16px; 196 | background: rgba(255,255,255,.3); 197 | color: rgba(0, 0, 0, .3); 198 | cursor: pointer; 199 | font-size: 24px; 200 | line-height: 24px; 201 | width: 24px; 202 | text-align: center; 203 | vertical-align: middle; 204 | } 205 | .shadergraph-overlay .shadergraph-close:hover { 206 | background: rgba(255,255,255,1); 207 | color: rgba(0, 0, 0, 1); 208 | } 209 | .shadergraph-overlay .shadergraph-graph { 210 | padding-top: 10px; 211 | overflow: visible; 212 | min-height: 100%; 213 | } 214 | .shadergraph-overlay span { 215 | display: block; 216 | padding: 5px 15px; 217 | margin: 0; 218 | background: rgba(0, 0, 0, .1); 219 | font-weight: bold; 220 | font-family: sans-serif; 221 | } 222 | .mathbox-loader { 223 | position: absolute; 224 | top: 50%; 225 | left: 50%; 226 | -webkit-transform: translate(-50%, -50%); 227 | transform: translate(-50%, -50%); 228 | padding: 10px; 229 | border-radius: 50%; 230 | background: #fff; 231 | } 232 | 233 | .mathbox-loader.mathbox-exit { 234 | opacity: 0; 235 | -webkit-transition: 236 | opacity .15s ease-in-out; 237 | transition: 238 | opacity .15s ease-in-out; 239 | } 240 | 241 | .mathbox-progress { 242 | height: 10px; 243 | border-radius: 5px; 244 | width: 80px; 245 | margin: 0 auto 20px; 246 | box-shadow: 247 | 1px 1px 1px rgba(255, 255, 255, .2), 248 | 1px -1px 1px rgba(255, 255, 255, .2), 249 | -1px 1px 1px rgba(255, 255, 255, .2), 250 | -1px -1px 1px rgba(255, 255, 255, .2); 251 | background: #ccc; 252 | overflow: hidden; 253 | } 254 | 255 | .mathbox-progress > div { 256 | display: block; 257 | width: 0px; 258 | height: 10px; 259 | background: #888; 260 | } 261 | 262 | .mathbox-logo { 263 | position: relative; 264 | width: 140px; 265 | height: 100px; 266 | margin: 0 auto 10px; 267 | -webkit-perspective: 200px; 268 | perspective: 200px; 269 | } 270 | 271 | .mathbox-logo > div { 272 | position: absolute; 273 | left: 0; 274 | top: 0; 275 | bottom: 0; 276 | right: 0; 277 | -webkit-transform-style: preserve-3d; 278 | transform-style: preserve-3d; 279 | } 280 | 281 | .mathbox-logo > :nth-child(1) { 282 | -webkit-transform: rotateZ(22deg) rotateX(24deg) rotateY(30deg); 283 | transform: rotateZ(22deg) rotateX(24deg) rotateY(30deg); 284 | } 285 | 286 | .mathbox-logo > :nth-child(2) { 287 | -webkit-transform: rotateZ(11deg) rotateX(12deg) rotateY(15deg) scale3d(.6, .6, .6); 288 | transform: rotateZ(11deg) rotateX(12deg) rotateY(15deg) scale3d(.6, .6, .6); 289 | } 290 | 291 | .mathbox-logo > div > div { 292 | position: absolute; 293 | top: 50%; 294 | left: 50%; 295 | margin-left: -100px; 296 | margin-top: -100px; 297 | width: 200px; 298 | height: 200px; 299 | box-sizing: border-box; 300 | border-radius: 50%; 301 | } 302 | 303 | .mathbox-logo > div > :nth-child(1) { 304 | -webkit-transform: scale(0.5, 0.5); 305 | transform: rotateX(30deg) scale(0.5, 0.5); 306 | } 307 | 308 | .mathbox-logo > div > :nth-child(2) { 309 | -webkit-transform: rotateX(90deg) scale(0.42, 0.42); 310 | transform: rotateX(90deg) scale(0.42, 0.42); 311 | } 312 | 313 | .mathbox-logo > div > :nth-child(3) { 314 | -webkit-transform: rotateY(90deg) scale(0.35, 0.35); 315 | transform: rotateY(90deg) scale(0.35, 0.35); 316 | } 317 | 318 | .mathbox-logo > :nth-child(1) > :nth-child(1) { 319 | border: 16px solid #808080; 320 | } 321 | .mathbox-logo > :nth-child(1) > :nth-child(2) { 322 | border: 19px solid #A0A0A0; 323 | } 324 | .mathbox-logo > :nth-child(1) > :nth-child(3) { 325 | border: 23px solid #C0C0C0; 326 | } 327 | .mathbox-logo > :nth-child(2) > :nth-child(1) { 328 | border: 27px solid #808080; 329 | } 330 | .mathbox-logo > :nth-child(2) > :nth-child(2) { 331 | border: 32px solid #A0A0A0; 332 | } 333 | .mathbox-logo > :nth-child(2) > :nth-child(3) { 334 | border: 38px solid #C0C0C0; 335 | } 336 | 337 | .mathbox-splash-blue .mathbox-progress { 338 | background: #def; 339 | } 340 | .mathbox-splash-blue .mathbox-progress > div { 341 | background: #1979e7; 342 | } 343 | .mathbox-splash-blue .mathbox-logo > :nth-child(1) > :nth-child(1) { 344 | border-color: #1979e7; 345 | } 346 | .mathbox-splash-blue .mathbox-logo > :nth-child(1) > :nth-child(2) { 347 | border-color: #33b0ff; 348 | } 349 | .mathbox-splash-blue .mathbox-logo > :nth-child(1) > :nth-child(3) { 350 | border-color: #75eaff; 351 | } 352 | .mathbox-splash-blue .mathbox-logo > :nth-child(2) > :nth-child(1) { 353 | border-color: #18487F; 354 | } 355 | .mathbox-splash-blue .mathbox-logo > :nth-child(2) > :nth-child(2) { 356 | border-color: #33b0ff; 357 | } 358 | .mathbox-splash-blue .mathbox-logo > :nth-child(2) > :nth-child(3) { 359 | border-color: #75eaff; 360 | } 361 | 362 | 363 | 364 | 365 | .mathbox-overlays { 366 | position: absolute; 367 | left: 0; 368 | top: 0; 369 | right: 0; 370 | bottom: 0; 371 | pointer-events: none; 372 | transform-style: preserve-3d; 373 | overflow: hidden; 374 | } 375 | .mathbox-overlays > div { 376 | transform-style: preserve-3d; 377 | } 378 | .mathbox-overlay > div { 379 | position: absolute; 380 | will-change: transform, opacity; 381 | } 382 | .mathbox-label { 383 | font-family: sans-serif; 384 | } 385 | .mathbox-outline-1 { 386 | text-shadow: 387 | -1px -1px 0px rgb(255, 255, 255), 388 | 1px 1px 0px rgb(255, 255, 255), 389 | -1px 1px 0px rgb(255, 255, 255), 390 | 1px -1px 0px rgb(255, 255, 255), 391 | 1px 0px 1px rgb(255, 255, 255), 392 | -1px 0px 1px rgb(255, 255, 255), 393 | 0px -1px 1px rgb(255, 255, 255), 394 | 0px 1px 1px rgb(255, 255, 255); 395 | } 396 | .mathbox-outline-2 { 397 | text-shadow: 398 | 0px -2px 0px rgb(255, 255, 255), 399 | 0px 2px 0px rgb(255, 255, 255), 400 | -2px 0px 0px rgb(255, 255, 255), 401 | 2px 0px 0px rgb(255, 255, 255), 402 | -1px -2px 0px rgb(255, 255, 255), 403 | -2px -1px 0px rgb(255, 255, 255), 404 | -1px 2px 0px rgb(255, 255, 255), 405 | -2px 1px 0px rgb(255, 255, 255), 406 | 1px 2px 0px rgb(255, 255, 255), 407 | 2px 1px 0px rgb(255, 255, 255), 408 | 1px -2px 0px rgb(255, 255, 255), 409 | 2px -1px 0px rgb(255, 255, 255); 410 | } 411 | .mathbox-outline-3 { 412 | text-shadow: 413 | 3px 0px 0px rgb(255, 255, 255), 414 | -3px 0px 0px rgb(255, 255, 255), 415 | 0px 3px 0px rgb(255, 255, 255), 416 | 0px -3px 0px rgb(255, 255, 255), 417 | 418 | -2px -2px 0px rgb(255, 255, 255), 419 | -2px 2px 0px rgb(255, 255, 255), 420 | 2px 2px 0px rgb(255, 255, 255), 421 | 2px -2px 0px rgb(255, 255, 255), 422 | 423 | -1px -2px 1px rgb(255, 255, 255), 424 | -2px -1px 1px rgb(255, 255, 255), 425 | -1px 2px 1px rgb(255, 255, 255), 426 | -2px 1px 1px rgb(255, 255, 255), 427 | 1px 2px 1px rgb(255, 255, 255), 428 | 2px 1px 1px rgb(255, 255, 255), 429 | 1px -2px 1px rgb(255, 255, 255), 430 | 2px -1px 1px rgb(255, 255, 255); 431 | } 432 | .mathbox-outline-4 { 433 | text-shadow: 434 | 4px 0px 0px rgb(255, 255, 255), 435 | -4px 0px 0px rgb(255, 255, 255), 436 | 0px 4px 0px rgb(255, 255, 255), 437 | 0px -4px 0px rgb(255, 255, 255), 438 | 439 | -3px -2px 0px rgb(255, 255, 255), 440 | -3px 2px 0px rgb(255, 255, 255), 441 | 3px 2px 0px rgb(255, 255, 255), 442 | 3px -2px 0px rgb(255, 255, 255), 443 | 444 | -2px -3px 0px rgb(255, 255, 255), 445 | -2px 3px 0px rgb(255, 255, 255), 446 | 2px 3px 0px rgb(255, 255, 255), 447 | 2px -3px 0px rgb(255, 255, 255), 448 | 449 | -1px -2px 1px rgb(255, 255, 255), 450 | -2px -1px 1px rgb(255, 255, 255), 451 | -1px 2px 1px rgb(255, 255, 255), 452 | -2px 1px 1px rgb(255, 255, 255), 453 | 1px 2px 1px rgb(255, 255, 255), 454 | 2px 1px 1px rgb(255, 255, 255), 455 | 1px -2px 1px rgb(255, 255, 255), 456 | 2px -1px 1px rgb(255, 255, 255); 457 | 458 | } 459 | .mathbox-outline-fill, .mathbox-outline-fill * { 460 | color: #fff !important; 461 | } 462 | -------------------------------------------------------------------------------- /client/public/css/slides.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | margin: 0 auto; 4 | -webkit-font-smoothing: antialiased; 5 | font-size: 2vw; 6 | color: #000; 7 | line-height: 1.5em; 8 | } 9 | 10 | pre { 11 | font-size: 1.5vw !important; 12 | line-height: 1.2em; 13 | } 14 | 15 | h1 { 16 | font-size: 3.5vw; 17 | } 18 | 19 | h2 { 20 | font-size: 3vw; 21 | } 22 | 23 | h3 { 24 | font-size: 1.5em 25 | } 26 | 27 | img { 28 | max-width: 100%; 29 | } 30 | 31 | .flexbox { 32 | margin: 5px; 33 | 34 | display: -webkit-box; 35 | display: -moz-box; 36 | display: -ms-flexbox; 37 | display: -webkit-flex; 38 | display: flex; 39 | 40 | flex-flow: row wrap; 41 | } 42 | 43 | 44 | .slide { 45 | width: 80%; 46 | height: 80%; 47 | margin: auto; 48 | display: flex; 49 | justify-content: space-around; 50 | align-items: center; 51 | } 52 | 53 | .title { 54 | display: inline-block; 55 | text-align: center; 56 | margin: auto; 57 | } 58 | 59 | .marwid { 60 | display: inline-block; 61 | margin: auto; 62 | } 63 | 64 | .rowflex { 65 | display: flex; 66 | flex-flow: row wrap; 67 | } 68 | 69 | .colflex { 70 | display: flex; 71 | flex-flow: column wrap; 72 | } 73 | 74 | .block { 75 | display: block; 76 | } 77 | 78 | .padapp { 79 | padding: 0.2vw; 80 | } 81 | 82 | .counter { 83 | margin: 10px; 84 | font-size: initial; 85 | } 86 | 87 | .center { 88 | display: flex; 89 | margin: auto; 90 | justify-content: center; 91 | } 92 | 93 | 94 | .boldEl { 95 | font-weight: bold !important; 96 | } 97 | .italicEl { 98 | font-style: italic !important; 99 | } 100 | -------------------------------------------------------------------------------- /client/public/css/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Roboto|Roboto+Slab'); 2 | 3 | body 4 | { 5 | font-family: 'Roboto', sans-serif; 6 | color: #404040; 7 | } 8 | 9 | h1, h2, h3, h4, h5, h6 { 10 | font-family: 'Roboto Slab', sans-serif; 11 | color: #1d222d; 12 | } 13 | 14 | a, a:visited, a:active, a:hover { 15 | color: #c4953a; 16 | } 17 | 18 | pre { 19 | background-color: rgb(240, 240, 240); 20 | padding: 10px; 21 | border-radius: 10px; 22 | } 23 | -------------------------------------------------------------------------------- /client/public/frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Try PureScript! 5 | 6 | 7 | 8 | 12 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /client/public/img/favicon-black.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/purescript/trypurescript/c41dfbc6e53bc23ea6f3d8a973c425da820a4de3/client/public/img/favicon-black.ico -------------------------------------------------------------------------------- /client/public/img/favicon-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 27 | 28 | 29 | 30 | 31 | 32 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /client/public/img/favicon-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 27 | 28 | 29 | 30 | 31 | 32 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /client/public/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/purescript/trypurescript/c41dfbc6e53bc23ea6f3d8a973c425da820a4de3/client/public/img/loading.gif -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Try PureScript! 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /client/public/js/frame.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var parent; 3 | 4 | document.addEventListener("DOMContentLoaded", function() { 5 | window.addEventListener("message", function(event) { 6 | parent = event.source; 7 | parent.postMessage("trypurescript", "*"); 8 | const code = event.data.code; 9 | const scriptEl = document.createElement("script"); 10 | scriptEl.type = "module"; 11 | scriptEl.appendChild(document.createTextNode(code)); 12 | document.body.appendChild(scriptEl); 13 | }, { once: true }); 14 | }, { once: true }); 15 | 16 | document.addEventListener("click", function(event) { 17 | if (parent && event.target.nodeName === "A" && event.target.hostname === "gist.github.com") { 18 | event.preventDefault(); 19 | parent.postMessage({ 20 | gistId: event.target.pathname.split("/").slice(-1)[0] 21 | }, "*"); 22 | } 23 | if (parent && event.target.nodeName === "A" && event.target.hostname === "github.com") { 24 | event.preventDefault(); 25 | parent.postMessage({ 26 | githubId: event.target.pathname 27 | }, "*"); 28 | } 29 | }, false); 30 | })(); 31 | -------------------------------------------------------------------------------- /client/spago.dhall: -------------------------------------------------------------------------------- 1 | { name = "try-purescript" 2 | , dependencies = 3 | [ "ace" 4 | , "aff" 5 | , "affjax" 6 | , "affjax-web" 7 | , "argonaut-codecs" 8 | , "argonaut-core" 9 | , "arrays" 10 | , "assert" 11 | , "bifunctors" 12 | , "console" 13 | , "control" 14 | , "datetime" 15 | , "effect" 16 | , "either" 17 | , "exceptions" 18 | , "foldable-traversable" 19 | , "foreign-object" 20 | , "functions" 21 | , "functors" 22 | , "halogen" 23 | , "halogen-subscriptions" 24 | , "integers" 25 | , "js-timers" 26 | , "js-uri" 27 | , "lists" 28 | , "maybe" 29 | , "newtype" 30 | , "node-buffer" 31 | , "node-fs" 32 | , "nullable" 33 | , "partial" 34 | , "prelude" 35 | , "random" 36 | , "refs" 37 | , "strings" 38 | , "transformers" 39 | , "tuples" 40 | , "unsafe-coerce" 41 | , "web-html" 42 | ] 43 | , packages = ./packages.dhall 44 | , sources = [ "src/**/*.purs", "test/**/*.purs" ] 45 | } 46 | -------------------------------------------------------------------------------- /client/src/Main.purs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Prelude 4 | 5 | import Effect (Effect) 6 | import Effect.Aff (launchAff_) 7 | import Halogen.Aff as HA 8 | import Halogen.VDom.Driver (runUI) 9 | import Try.Container as Container 10 | 11 | main :: Effect Unit 12 | main = void $ launchAff_ do 13 | body <- HA.awaitBody 14 | void $ runUI Container.component unit body 15 | -------------------------------------------------------------------------------- /client/src/Try/API.purs: -------------------------------------------------------------------------------- 1 | module Try.API 2 | ( ErrorPosition(..) 3 | , CompilerError(..) 4 | , CompileError(..) 5 | , CompileWarning(..) 6 | , Suggestion(..) 7 | , SuccessResult(..) 8 | , FailedResult(..) 9 | , CompileResult(..) 10 | , get 11 | , compile 12 | ) where 13 | 14 | import Prelude 15 | 16 | import Affjax (URL, printError) 17 | import Affjax.RequestBody as AXRB 18 | import Affjax.ResponseFormat as AXRF 19 | import Affjax.StatusCode (StatusCode(..)) 20 | import Affjax.Web as AX 21 | import Control.Alt ((<|>)) 22 | import Control.Monad.Except (ExceptT(..)) 23 | import Data.Argonaut.Decode (class DecodeJson, JsonDecodeError(..), decodeJson, printJsonDecodeError, (.:)) 24 | import Data.Argonaut.Encode (encodeJson) 25 | import Data.Bifunctor (lmap) 26 | import Data.Either (Either(..)) 27 | import Data.Maybe (Maybe(..)) 28 | import Data.Traversable (traverse) 29 | import Effect.Aff (Aff) 30 | import Effect.Aff.Class (class MonadAff, liftAff) 31 | 32 | -- | The range of text associated with an error 33 | type ErrorPosition = 34 | { startLine :: Int 35 | , endLine :: Int 36 | , startColumn :: Int 37 | , endColumn :: Int 38 | } 39 | 40 | type CompilerError = 41 | { message :: String 42 | , position :: Maybe ErrorPosition 43 | } 44 | 45 | -- | An error reported from the compile API. 46 | data CompileError 47 | = CompilerErrors (Array CompilerError) 48 | | OtherError String 49 | 50 | instance decodeJsonCompileError :: DecodeJson CompileError where 51 | decodeJson = decodeJson >=> \obj -> do 52 | contents <- obj .: "contents" 53 | obj .: "tag" >>= case _ of 54 | "OtherError" -> 55 | map OtherError $ decodeJson contents 56 | "CompilerErrors" -> 57 | map CompilerErrors $ traverse decodeJson =<< decodeJson contents 58 | j -> 59 | Left $ AtKey "tag" $ UnexpectedValue $ encodeJson $ "- Expected a value of `OtherError` or `CompilerErrors` but got '" <> j <> "'" 60 | 61 | type Suggestion = 62 | { replacement :: String 63 | , replaceRange :: Maybe ErrorPosition 64 | } 65 | 66 | type CompileWarning = 67 | { errorCode :: String 68 | , message :: String 69 | , position :: Maybe ErrorPosition 70 | , suggestion :: Maybe Suggestion 71 | } 72 | 73 | type SuccessResult = 74 | { js :: String 75 | , warnings :: Maybe (Array CompileWarning) 76 | } 77 | 78 | type FailedResult = 79 | { error :: CompileError 80 | } 81 | 82 | -- | The result of calling the compile API. 83 | data CompileResult 84 | = CompileSuccess SuccessResult 85 | | CompileFailed FailedResult 86 | 87 | -- | Parse the result from the compile API and verify it 88 | instance decodeJsonCompileResult :: DecodeJson CompileResult where 89 | decodeJson json = 90 | map CompileSuccess (decodeJson json) 91 | <|> map CompileFailed (decodeJson json) 92 | 93 | get :: URL -> ExceptT String Aff String 94 | get url = ExceptT $ AX.get AXRF.string url >>= case _ of 95 | Left e -> 96 | pure $ Left $ printError e 97 | Right { status } | status >= StatusCode 400 -> 98 | pure $ Left $ "Received error status code: " <> show status 99 | Right { body } -> 100 | pure $ Right body 101 | 102 | -- | POST the specified code to the Try PureScript API, and wait for a response. 103 | compile :: forall m. MonadAff m => String -> String -> ExceptT String m (Either String CompileResult) 104 | compile endpoint code = ExceptT $ liftAff $ AX.post AXRF.json (endpoint <> "/compile") requestBody >>= case _ of 105 | Left e -> 106 | pure $ Left $ printError e 107 | Right { status } | status >= StatusCode 400 -> 108 | pure $ Left $ "Received error status code: " <> show status 109 | Right { body } -> 110 | pure $ Right $ lmap printJsonDecodeError $ decodeJson body 111 | where 112 | requestBody = Just $ AXRB.string code 113 | -------------------------------------------------------------------------------- /client/src/Try/Container.js: -------------------------------------------------------------------------------- 1 | $.ajaxSetup({ 2 | dataType: "text", 3 | }); 4 | 5 | export function teardownIFrame() { 6 | var $ctr = $("iframe#output-iframe"); 7 | $ctr.remove() 8 | } 9 | 10 | export function setupIFrame(data, loadCb, failCb) { 11 | var $ctr = $("#column2"); 12 | var $iframe = $( 13 | '