mirror of
https://github.com/ryannhg/elm-spa.git
synced 2024-11-22 17:52:33 +03:00
fix highlighting, add a bunch of docs
This commit is contained in:
parent
8b7e741e50
commit
cf2a65660d
@ -4,71 +4,50 @@ Welcome to __elm-spa__, a framework for building web applications with [Elm](htt
|
||||
If you are new to Elm, you should check out [the official guide](https://guide.elm-lang.org), which
|
||||
is a great introduction to the language.
|
||||
|
||||
The goal of _this_ guide is to help you solve common problems you might run into with real world single-page web applications.
|
||||
The goal of this guide is to help you solve any problems you might run into when building real world single-page web applications.
|
||||
|
||||
## Features
|
||||
|
||||
Here are a few benefits to using elm-spa:
|
||||
Here are some of the benefits for using __elm-spa__:
|
||||
1. __Automatic routing__ - routes for your web app are automatically generated based on file names. No need to maintain URL routing logic or wire pages together manually.
|
||||
1. __Simple shared state__ - comes with a straightforward way to share data within and between pages. You can also make pages as simple or complex as you need!
|
||||
1. __User authentication__ - provides an easy way to guarantee certain pages are only visible to signed-in users. Checkout the [user authentication](/examples/user-authentication) section for more details!
|
||||
1. __Zero configuration__ - Includes a hot-reloading dev server, build tool, and everything you need in one CLI tool! No need for webpack, uglify, or other NPM packages.
|
||||
|
||||
|
||||
## Quickstart
|
||||
|
||||
### Creating your first project
|
||||
|
||||
You can create a new __elm-spa__ project from scratch my creating a new folder:
|
||||
|
||||
```terminal
|
||||
mkdir my-new-project && cd my-new-project
|
||||
```
|
||||
|
||||
And then running this command in your terminal:
|
||||
If you already have [NodeJS](https://nodejs.org) installed, getting started with __elm-spa__ is easy:
|
||||
|
||||
```terminal
|
||||
mkdir our-cool-app
|
||||
cd our-cool-app
|
||||
npx elm-spa new
|
||||
```
|
||||
|
||||
This will create a brand new project in the `my-new-project` folder! It should only contain these three files:
|
||||
This will create a new project in the `our-cool-app` folder! It will only create __three__ new files:
|
||||
|
||||
```bash
|
||||
my-new-project/
|
||||
- .gitignore # folders to ignore in git
|
||||
- elm.json # project dependencies
|
||||
- src/
|
||||
- public/
|
||||
- index.html # entrypoint to your application
|
||||
our-cool-app/
|
||||
- elm.json # project dependencies
|
||||
- src/Pages/Home_.elm # our homepage
|
||||
- public/index.html # entrypoint to your application
|
||||
```
|
||||
|
||||
### Running the dev server
|
||||
|
||||
Running this command will start a development server at `http://localhost:1234`:
|
||||
Let's run it at `http://localhost:1234`:
|
||||
|
||||
```terminal
|
||||
npx elm-spa server
|
||||
```
|
||||
|
||||
### Adding your first page
|
||||
|
||||
To add a homepage, run the `elm-spa add` command:
|
||||
|
||||
```terminal
|
||||
npx elm-spa add /
|
||||
```
|
||||
|
||||
This will create a new page at `./src/Pages/Home_.elm`. Try editing the text in that file's `view` function, it will automatically change in the browser!
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
So far, we've been using the [npx command](https://www.npmjs.com/package/npx) built into Node.js to run the `elm-spa` CLI. If we would rather use the CLI without this prefix, we can install __elm-spa__ globally:
|
||||
So far, we've used [npx](https://www.npmjs.com/package/npx) so we don't need to install __elm-spa__ directly. If you'd like to run commands from the terminal, without the `npx` prefix, you can install __elm-spa__ like this:
|
||||
|
||||
```terminal
|
||||
npm install -g elm-spa@latest
|
||||
npm install -g elm-spa@next
|
||||
```
|
||||
|
||||
This will ensure we have the latest version of elm-spa available in our terminal. You can make sure it works by calling `elm-spa` directly:
|
||||
You can verify the install succeeded by running `elm-spa help` from your terminal:
|
||||
|
||||
```terminal
|
||||
elm-spa help
|
||||
@ -76,15 +55,17 @@ elm-spa help
|
||||
elm-spa – version 6.0.0
|
||||
|
||||
Commands:
|
||||
elm-spa new . . . . . . . . . create a new project
|
||||
elm-spa add <url> . . . . . . . . create a new page
|
||||
elm-spa build . . . . . . one-time production build
|
||||
elm-spa watch . . . . . . . runs build as you code
|
||||
elm-spa server . . . . . . start a live dev server
|
||||
elm-spa new . . . . . . . . create a new project
|
||||
elm-spa add <url> . . . . . . . create a new page
|
||||
elm-spa build . . . . . one-time production build
|
||||
elm-spa watch . . . . . . runs build as you code
|
||||
elm-spa server . . . . . start a live dev server
|
||||
|
||||
Visit https://next.elm-spa.dev for more!
|
||||
```
|
||||
|
||||
If you see this message, you can run all the CLI commands without needing to prefix them with `npx`!
|
||||
---
|
||||
|
||||
__Next up:__ [The CLI](/guide/cli)
|
||||
__Ready for more?__
|
||||
|
||||
Let's check out [the CLI](/guide/cli) to learn more about those five commands!
|
||||
|
@ -1,138 +1,111 @@
|
||||
# The CLI
|
||||
|
||||
The [official __elm-spa__ CLI tool](https://npmjs.org/elm-spa) has a few commands to help you build single page applications. As we saw in [the previous section](/guide/overview), you can use the CLI from your terminal by running:
|
||||
At the end of the last section, we installed the __elm-spa__ CLI using [npm](https://npmjs.org) like this:
|
||||
|
||||
```terminal
|
||||
npm install -g elm-spa@beta
|
||||
npm install -g elm-spa@next
|
||||
```
|
||||
|
||||
At any time running `elm-spa` or `elm-spa help` will show you the available commands:
|
||||
This gave us the ability to run a few commands:
|
||||
|
||||
```
|
||||
elm-spa new . . . . . . . . . create a new project
|
||||
elm-spa add <url> . . . . . . . . create a new page
|
||||
elm-spa build . . . . . . one-time production build
|
||||
elm-spa watch . . . . . . . runs build as you code
|
||||
elm-spa server . . . . . . start a live dev server
|
||||
```
|
||||
1. __[`elm-spa new`](#elm-spa-new)__ - creates a new project
|
||||
1. __[`elm-spa server`](#elm-spa-server)__ - runs a dev server as you code
|
||||
1. __[`elm-spa watch`](#elm-spa-watch)__ - builds as you code
|
||||
1. __[`elm-spa build`](#elm-spa-build)__ - one-time production build
|
||||
1. __[`elm-spa add`](#elm-spa-add)__ - adds a page to an existing project
|
||||
|
||||
## Creating new projects
|
||||
What do these do? This section of the guide dives into more detail on each command!
|
||||
|
||||
The `new` command creates a new project in the current folder:
|
||||
## elm-spa new
|
||||
|
||||
When you want to create a new project, you can use the `elm-spa new` command. This creates a new project in the current folder:
|
||||
|
||||
```terminal
|
||||
elm-spa new
|
||||
```
|
||||
|
||||
This command will only create a few files, so don't worry about getting overwhelmed with new files in your repo! Other than a `.gitignore`, there are only 3 new files created.
|
||||
```bash
|
||||
New project created in:
|
||||
/Users/ryan/code/my-new-app
|
||||
```
|
||||
|
||||
File | Description
|
||||
This command only creates __three__ files:
|
||||
|
||||
Filename | Description
|
||||
--- | ---
|
||||
`elm.json` | Your project's dependencies.
|
||||
`src/Pages/Home_.elm` | The homepage.
|
||||
`public/index.html` | The entrypoint to your application.
|
||||
`elm.json` | Keeps track of [Elm packages](https://package.elm-lang.org).
|
||||
`src/Pages/Home_.elm` | The project's homepage.
|
||||
`public/index.html` | The HTML entrypoint to the app.
|
||||
|
||||
```
|
||||
your-project/
|
||||
|- elm.json
|
||||
|- src/
|
||||
| |- Pages/
|
||||
| |- Home_.elm
|
||||
|- public/
|
||||
|- index.html
|
||||
```
|
||||
## elm-spa server
|
||||
|
||||
The `public` folder is a place for static assets! For example, a file at `./public/style.css` will be available at `/style.css` in your web browser.
|
||||
|
||||
## Adding pages
|
||||
|
||||
The next section will dive into deeper detail, but __elm-spa__ directly maps file names to URLs.
|
||||
|
||||
URL | File Location
|
||||
--- | ---
|
||||
`/` | `src/Pages/Home_.elm`
|
||||
`/about-us` | `src/Pages/AboutUs.elm`
|
||||
`/people/ryan` | `src/Pages/People/Ryan.elm`
|
||||
|
||||
The `elm-spa add` command makes it easy to scaffold out new pages in your application!
|
||||
|
||||
### Adding a homepage
|
||||
|
||||
Here's how you can add a homepage with the `elm-spa add` command:
|
||||
|
||||
```terminal
|
||||
elm-spa add /
|
||||
```
|
||||
|
||||
### Adding static pages
|
||||
|
||||
You can add [static routes](/guide/basics/routing#static-routes) with the add command also:
|
||||
|
||||
```terminal
|
||||
elm-spa add /settings
|
||||
```
|
||||
|
||||
This command will create a new page at `src/Pages/Settings.elm`, and be available at `/settings`.
|
||||
|
||||
### Adding dynamic pages
|
||||
|
||||
In the [next section](/guide/basics/routing), you'll learn more about static and dynamic pages, which can handle dynamic URL parameters to make life easy. For example, if we wanted a "Person Detail" page, we could do something like this:
|
||||
|
||||
```terminal
|
||||
elm-spa add /people/:name
|
||||
```
|
||||
|
||||
This creates a new page at `src/Pages/People/Name_.elm`. The underscore (`_`) at the end of the filename indicates a __dynamic__ route! This dynamic route handles requests to pages like these:
|
||||
|
||||
URL | Params
|
||||
--- | ---
|
||||
`/people/ryan` | `{ name = "ryan" }`
|
||||
`/people/erik` | `{ name = "erik" }`
|
||||
`/people/alexa` | `{ name = "alexa" }`
|
||||
|
||||
The name of the file (`Name_.elm`) determines the variable name.
|
||||
|
||||
### Removing pages
|
||||
|
||||
Removing pages with __elm-spa__ is as simple as __deleting the file__
|
||||
|
||||
```terminal
|
||||
rm src/Pages/Settings.elm
|
||||
```
|
||||
|
||||
You can do this however you prefer, but there isn't an `elm-spa remove` command in the CLI.
|
||||
|
||||
|
||||
## Developing locally
|
||||
|
||||
The __elm-spa__ CLI comes with a hot-reloading development server built in. As you save files in the `src` and `public` folders, your local site will automatically refresh.
|
||||
The first thing you'll want to do after creating a new project is try it out in the browser! The `elm-spa server` is all you need to see your app in action:
|
||||
|
||||
```terminal
|
||||
elm-spa server
|
||||
```
|
||||
|
||||
By default, the server will start on port 1234. You can specify a different port with the `PORT` environment variable:
|
||||
This will start a development server for your project at [http://localhost:1234](http://localhost:1234).
|
||||
|
||||
```terminal
|
||||
PORT=8000 elm-spa server
|
||||
```
|
||||
When we edit our code, `elm-spa server` automatically compiles your application.
|
||||
|
||||
__Note:__ The `server` command is not designed for production use! To
|
||||
## elm-spa watch
|
||||
|
||||
### Prefer webpack or parcel?
|
||||
|
||||
You can use the `watch` command to build assets without running the development server. This will build your application, and allow you to use something like [Parcel](https://parceljs.org/elm.html) or [webpack](https://github.com/elm-community/elm-webpack-loader).
|
||||
If you want the automatic compilation on change, but don't need a HTTP server, you can use the `elm-spa watch` command:
|
||||
|
||||
```terminal
|
||||
elm-spa watch
|
||||
```
|
||||
|
||||
## Building for production
|
||||
This will automatically generate and compile on save, but without the server. This is a great choice when combining __elm-spa__ with another tool like [Parcel](https://parceljs.org/elm.html)
|
||||
|
||||
When you are ready you ship your application, the `build` command will create a minified and optimized JS file for production.
|
||||
|
||||
## elm-spa build
|
||||
|
||||
The `server` and `watch` commands are great for development, but for __production__, we'll want the `elm-spa build` command.
|
||||
|
||||
```terminal
|
||||
elm-spa build
|
||||
```
|
||||
|
||||
For more information about deployments and hosting, you should check out the [Hosting & Development](/guide/hosting) section!
|
||||
This compiles your app into __an optimized and minified JS file__. This makes it great for serving your application in the real world!
|
||||
|
||||
|
||||
## elm-spa add
|
||||
|
||||
You can add new pages to your app with the `elm-spa add` command:
|
||||
|
||||
```terminal
|
||||
elm-spa add /contact
|
||||
```
|
||||
|
||||
This creates a new file at `src/Pages/Contact.elm`. If you visit [http://localhost:1234/contact](http://localhost:1234/contact) in the browser, you'll see a new page with the text `"Contact"` displayed.
|
||||
|
||||
### adding other pages
|
||||
|
||||
Here are a few examples of other routes you can create with the add command
|
||||
|
||||
```bash
|
||||
elm-spa add / # src/Pages/Home_.elm
|
||||
elm-spa add /settings # src/Pages/Settings.elm
|
||||
elm-spa add /people/:id # src/Pages/People/Id_.elm
|
||||
```
|
||||
|
||||
We'll cover this in more detail in the [routing section](./routing)
|
||||
|
||||
### using page templates
|
||||
|
||||
The `elm-spa add` command also accepts an optional `template` argument to for common
|
||||
pages you might create.
|
||||
|
||||
```bash
|
||||
elm-spa add /example static
|
||||
elm-spa add /example sandbox
|
||||
elm-spa add /example element
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
__So, what's a page?__
|
||||
|
||||
Let's answer that next in the [pages section](./pages)!
|
@ -1,32 +1,76 @@
|
||||
# Pages
|
||||
|
||||
Every route in your Elm application will be connected to a `Page` file. These files
|
||||
all have the same general shape:
|
||||
In __elm-spa__, every URL connects to a single page. Let's take a closer look at the homepage we created earlier with the `elm-spa new` command:
|
||||
|
||||
```elm
|
||||
module Pages._____ exposing (Model, Msg, page)
|
||||
module Pages.Home_ exposing (view)
|
||||
|
||||
page : Shared.Model -> Request Params -> Page Model Msg
|
||||
```
|
||||
import Html
|
||||
|
||||
This section of the guide will introduce you to the __four__ kinds of pages you might
|
||||
need to use in any Elm application.
|
||||
|
||||
> It's important that _every_ page exposes `Model`, `Msg`, and `page`. If any of these three are missing or renamed, the generated code will not work.
|
||||
|
||||
## Static pages
|
||||
|
||||
If you only need to render some HTML on the page, use `Page.static`:
|
||||
|
||||
```elm
|
||||
Page.static
|
||||
{ view : View Msg
|
||||
view =
|
||||
{ title = "Homepage"
|
||||
, body = [ Html.text "Hello, world!" ]
|
||||
}
|
||||
```
|
||||
|
||||
## Sandbox pages
|
||||
This homepage renders the tab `title`, and a HTML `body` onto the page. This is great when you have a static page that just needs to render some elements.
|
||||
|
||||
Need to keep track of local state, like the current tab? Check out `Page.sandbox`!
|
||||
Because the file is named `Home_.elm`, we know it's the homepage. These 8 lines of code are all we need to tell __elm-spa__ we'd like to render this when users visit the homepage.
|
||||
|
||||
For real world applications, we'll need pages that can do more. That's where the `Page` module comes in handy.
|
||||
|
||||
### Upgrading "Hello World!"
|
||||
|
||||
Let's start by introducing the `page` function, marking the start of our journey from "Hello world!" to the real world:
|
||||
|
||||
```elm
|
||||
module Pages.Home_ exposing (page)
|
||||
|
||||
-- our other imports
|
||||
import Page exposing (Page)
|
||||
|
||||
page :
|
||||
Shared.Model
|
||||
-> Request Params
|
||||
-> Page () Never
|
||||
page shared req =
|
||||
Page.static
|
||||
{ view = view
|
||||
}
|
||||
|
||||
view =
|
||||
{ title = "Homepage"
|
||||
, body = [ Html.text "Hello, world!" ]
|
||||
}
|
||||
```
|
||||
|
||||
Here, our code hasn't changed very much- except now we have this new `page` function that:
|
||||
|
||||
1. Takes in two inputs: `Shared.Model` and `Request Params`
|
||||
2. Returns a `Page () Never` value.
|
||||
3. Is exposed at the top of our `Pages.Home_` module.
|
||||
|
||||
> Without exposing `page`, __elm-spa__ will not understand how to compile your application. Make sure to _always_ expose `page` from modules within the `src/Pages` folder.
|
||||
|
||||
This new `page` will always get the latest `Shared.Model` and URL information, which means you don't have to worry about tracking that stuff yourself.
|
||||
|
||||
This is great, but there is still more that our `page` function can do other than render a view!
|
||||
|
||||
### Beyond static pages
|
||||
|
||||
The right page to use is the __simplest page__ that can support what you need! As we move from `Page.static` to `Page.advanced`, we'll have more capabilities, but at the cost of more code.
|
||||
|
||||
This section of the guide will introduce you to the functions exposed by the `Page` module, so you have all the information you need.
|
||||
|
||||
__[Page.static](#pagestatic)__ - for pages that only render a view.
|
||||
|
||||
```elm
|
||||
Page.static
|
||||
{ view : View Never
|
||||
}
|
||||
```
|
||||
|
||||
__[Page.sandbox](#pagesandbox)__ - for pages that need to keep track of state.
|
||||
|
||||
```elm
|
||||
Page.sandbox
|
||||
@ -36,9 +80,7 @@ Page.sandbox
|
||||
}
|
||||
```
|
||||
|
||||
## Element pages
|
||||
|
||||
If you want to send [HTTP requests](https://guide.elm-lang.org/effects/http.html) or subscribe to other external events, you're ready for `Page.element`:
|
||||
__[Page.element](#pageelement)__ - for pages that send HTTP requests or continually listen for events from the browser or user.
|
||||
|
||||
```elm
|
||||
Page.element
|
||||
@ -49,15 +91,138 @@ Page.element
|
||||
}
|
||||
```
|
||||
|
||||
## Shared pages
|
||||
|
||||
When it comes time to update the global state shared from page to page, you can upgrade to `Page.shared`:
|
||||
__[Page.advanced](#pageadvanced)__ - For pages that need to sign in a user or work with other details that should persist between page navigation.
|
||||
|
||||
```elm
|
||||
Page.shared
|
||||
{ init : ( Model, Cmd Msg, List Shared.Msg )
|
||||
, update : Msg -> Model -> ( Model, Cmd Msg, List Shared.Msg )
|
||||
Page.advanced
|
||||
{ init : ( Model, Effect Msg )
|
||||
, update : Msg -> Model -> ( Model, Effect Msg )
|
||||
, view : Model -> View Msg
|
||||
, subscriptions : Model -> Sub Msg
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
### Working with pages
|
||||
|
||||
The `page` function allows us to pass `Shared.Model` and `Request` information to any inner function that needs it.
|
||||
|
||||
```elm
|
||||
page shared req =
|
||||
Page.sandbox
|
||||
{ init = init shared -- pass init "shared"
|
||||
, update = update req -- pass update "req"
|
||||
, view = view
|
||||
}
|
||||
```
|
||||
|
||||
Imagine your `init` function needs access to `shared` data, and your `update` function needs URL information from the current `req`.
|
||||
|
||||
Because `page` is a function, you can pass those values in where you see fit. This means the type annotations of those inner functions should also update:
|
||||
|
||||
__Before__
|
||||
```elm
|
||||
page shared req =
|
||||
Page.sandbox
|
||||
{ init = init
|
||||
, update = update
|
||||
, view = view
|
||||
}
|
||||
|
||||
init : Model
|
||||
update : Msg -> Model -> Model
|
||||
view : Model -> View Msg
|
||||
```
|
||||
|
||||
__After__
|
||||
|
||||
```elm
|
||||
page shared req =
|
||||
Page.sandbox
|
||||
{ init = init shared
|
||||
, update = update req
|
||||
, view = view
|
||||
}
|
||||
|
||||
init : Shared.Model -> Model
|
||||
update : Request Params -> Msg -> Model -> Model
|
||||
view : Model -> View Msg
|
||||
```
|
||||
|
||||
Notice how the type annotations of `init` and `update` changed to accept their input? (The `view` function didn't change because it didn't get any new values)
|
||||
|
||||
## Page.static
|
||||
|
||||
```elm
|
||||
Page.static
|
||||
{ view : View Never
|
||||
}
|
||||
```
|
||||
|
||||
( video introducing concept )
|
||||
|
||||
## Page.sandbox
|
||||
|
||||
```elm
|
||||
Page.sandbox
|
||||
{ init : Model
|
||||
, update : Msg -> Model -> Model
|
||||
, view : Model -> View Msg
|
||||
}
|
||||
```
|
||||
|
||||
( video introducing concept )
|
||||
|
||||
## Page.element
|
||||
|
||||
```elm
|
||||
Page.element
|
||||
{ init : ( Model, Cmd Msg )
|
||||
, update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
, view : Model -> View Msg
|
||||
, subscriptions : Model -> Sub Msg
|
||||
}
|
||||
```
|
||||
|
||||
( video introducing concept )
|
||||
|
||||
## Page.advanced
|
||||
|
||||
```elm
|
||||
Page.advanced
|
||||
{ init : ( Model, Effect Msg )
|
||||
, update : Msg -> Model -> ( Model, Effect Msg )
|
||||
, view : Model -> View Msg
|
||||
, subscriptions : Model -> Sub Msg
|
||||
}
|
||||
```
|
||||
|
||||
( video introducing concept )
|
||||
|
||||
|
||||
## Page.protected
|
||||
|
||||
Each of the four page types also have a "protected" version, that is guaranteed to receive a `User` or redirect if no user is signed in.
|
||||
|
||||
```elm
|
||||
Page.protected.static
|
||||
{ view : User -> View Never
|
||||
}
|
||||
|
||||
Page.protected.sandbox
|
||||
{ init : User -> Model
|
||||
, update : User -> Msg -> Model -> Model
|
||||
, view : User -> Model -> View Msg
|
||||
}
|
||||
|
||||
-- Page.protected.element
|
||||
|
||||
-- Page.protected.advanced
|
||||
```
|
||||
|
||||
When you are ready, you can learn more about this in the [user authentication example](/examples/authentication).
|
||||
|
||||
---
|
||||
|
||||
__What's next?__
|
||||
|
||||
Let me introduce you to the `Request Params` type we pass into our pages in the [next section on requests](./requests)
|
@ -10,6 +10,7 @@ type alias Request params =
|
||||
{ params : params
|
||||
, query : Dict String String
|
||||
, url : Url
|
||||
, route : Route
|
||||
, key : Nav.Key
|
||||
}
|
||||
```
|
||||
@ -39,7 +40,7 @@ __Note:__ When working with [shared state](/guide/shared-state), all requests ar
|
||||
|
||||
## Query Parameters
|
||||
|
||||
For convenience, query parameters are automatically turned into a `Dict String String`, making it easy to handle URLs like this:
|
||||
For convenience, query parameters are automatically turned into a `Dict String String`, making it easy to handle common query URL parameters like these:
|
||||
|
||||
```
|
||||
/people?team=design&ascending
|
||||
@ -70,6 +71,21 @@ type alias Url =
|
||||
|
||||
This is less common than `req.params` and `req.query`, but can be useful for getting the `hash` at the end of a URL too!
|
||||
|
||||
## Getting the current route
|
||||
|
||||
The `Request` type also has access to the `Route` value, so you can easily do comparisons agains the current route!
|
||||
|
||||
```elm
|
||||
-- "/"
|
||||
req.route == Gen.Route.Home_
|
||||
|
||||
-- "/about-us"
|
||||
req.route == Gen.Route.AboutUs
|
||||
|
||||
-- "/people/ryan"
|
||||
req.route == Gen.Route.People_ { name = "ryan" }
|
||||
```
|
||||
|
||||
## Programmatic Navigation
|
||||
|
||||
Most of the time, navigation in Elm is as easy as giving an `href` attribute to an anchor tag:
|
||||
@ -80,7 +96,7 @@ a [ href "/guide" ] [ text "elm-spa guide" ]
|
||||
|
||||
Other times, you'll want to do __programmatic navigation__ – navigating to another page after some event completes. Maybe you want to __redirect__ to a sign in page, or head to the __dashboard after signing in successfully__.
|
||||
|
||||
In that case you'll need access to `req.key` in order to use `Nav.pushUrl` or `Nav.replaceUrl`. Here's a quick example of what that looks like:
|
||||
In that case we store `req.key` in order to use `Request.pushRoute` or `Request.replaceRoute`. Here's a quick example of what that looks like:
|
||||
|
||||
```elm
|
||||
type Msg = SignedIn User
|
||||
@ -90,8 +106,8 @@ update req msg model =
|
||||
case msg of
|
||||
SignedIn user ->
|
||||
( model
|
||||
, Nav.pushUrl req.key "/dashboard"
|
||||
, Request.pushRoute Gen.Route.Dashboard req
|
||||
)
|
||||
```
|
||||
|
||||
When the `SignedIn` message is fired, this code will redirect the user to the dashboard. Feel free to check out the [elm/browser](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Navigation) package docs for more in-depth examples.
|
||||
When the `SignedIn` message is fired, this code will redirect the user to the `Dashboard` route.
|
@ -1,406 +0,0 @@
|
||||
/*
|
||||
Highlight.js 10.4.1 (e96b915a)
|
||||
License: BSD-3-Clause
|
||||
Copyright (c) 2006-2020, Ivan Sagalaev
|
||||
*/
|
||||
var hljs=function(){"use strict";function e(t){
|
||||
return t instanceof Map?t.clear=t.delete=t.set=()=>{
|
||||
throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{
|
||||
throw Error("set is read-only")
|
||||
}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{var s=t[n]
|
||||
;"object"!=typeof s||Object.isFrozen(s)||e(s)})),t}var t=e,n=e;t.default=n
|
||||
;class s{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data}
|
||||
ignoreMatch(){this.ignore=!0}}function r(e){
|
||||
return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")
|
||||
}function a(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t]
|
||||
;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}function i(e){
|
||||
return e.nodeName.toLowerCase()}var o=Object.freeze({__proto__:null,
|
||||
escapeHTML:r,inherit:a,nodeStream:e=>{const t=[];return function e(n,s){
|
||||
for(let r=n.firstChild;r;r=r.nextSibling)3===r.nodeType?s+=r.nodeValue.length:1===r.nodeType&&(t.push({
|
||||
event:"start",offset:s,node:r}),s=e(r,s),i(r).match(/br|hr|img|input/)||t.push({
|
||||
event:"stop",offset:s,node:r}));return s}(e,0),t},mergeStreams:(e,t,n)=>{
|
||||
let s=0,a="";const o=[];function l(){
|
||||
return e.length&&t.length?e[0].offset!==t[0].offset?e[0].offset<t[0].offset?e:t:"start"===t[0].event?e:t:e.length?e:t
|
||||
}function c(e){
|
||||
a+="<"+i(e)+[].map.call(e.attributes,(e=>" "+e.nodeName+'="'+r(e.value)+'"')).join("")+">"
|
||||
}function u(e){a+="</"+i(e)+">"}function g(e){("start"===e.event?c:u)(e.node)}
|
||||
for(;e.length||t.length;){let t=l()
|
||||
;if(a+=r(n.substring(s,t[0].offset)),s=t[0].offset,t===e){o.reverse().forEach(u)
|
||||
;do{g(t.splice(0,1)[0]),t=l()}while(t===e&&t.length&&t[0].offset===s)
|
||||
;o.reverse().forEach(c)
|
||||
}else"start"===t[0].event?o.push(t[0].node):o.pop(),g(t.splice(0,1)[0])}
|
||||
return a+r(n.substr(s))}});const l=e=>!!e.kind;class c{constructor(e,t){
|
||||
this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){
|
||||
this.buffer+=r(e)}openNode(e){if(!l(e))return;let t=e.kind
|
||||
;e.sublanguage||(t=`${this.classPrefix}${t}`),this.span(t)}closeNode(e){
|
||||
l(e)&&(this.buffer+="</span>")}value(){return this.buffer}span(e){
|
||||
this.buffer+=`<span class="${e}">`}}class u{constructor(){this.rootNode={
|
||||
children:[]},this.stack=[this.rootNode]}get top(){
|
||||
return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){
|
||||
this.top.children.push(e)}openNode(e){const t={kind:e,children:[]}
|
||||
;this.add(t),this.stack.push(t)}closeNode(){
|
||||
if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){
|
||||
for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}
|
||||
walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){
|
||||
return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t),
|
||||
t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){
|
||||
"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{
|
||||
u._collapse(e)})))}}class g extends u{constructor(e){super(),this.options=e}
|
||||
addKeyword(e,t){""!==e&&(this.openNode(t),this.addText(e),this.closeNode())}
|
||||
addText(e){""!==e&&this.add(e)}addSublanguage(e,t){const n=e.root
|
||||
;n.kind=t,n.sublanguage=!0,this.add(n)}toHTML(){
|
||||
return new c(this,this.options).value()}finalize(){return!0}}function d(e){
|
||||
return e?"string"==typeof e?e:e.source:null}
|
||||
const h="[a-zA-Z]\\w*",f="[a-zA-Z_]\\w*",p="\\b\\d+(\\.\\d+)?",m="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",b="\\b(0b[01]+)",x={
|
||||
begin:"\\\\[\\s\\S]",relevance:0},E={className:"string",begin:"'",end:"'",
|
||||
illegal:"\\n",contains:[x]},v={className:"string",begin:'"',end:'"',
|
||||
illegal:"\\n",contains:[x]},_={
|
||||
begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/
|
||||
},w=(e,t,n={})=>{const s=a({className:"comment",begin:e,end:t,contains:[]},n)
|
||||
;return s.contains.push(_),s.contains.push({className:"doctag",
|
||||
begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),s
|
||||
},N=w("//","$"),y=w("/\\*","\\*/"),R=w("#","$");var k=Object.freeze({
|
||||
__proto__:null,IDENT_RE:h,UNDERSCORE_IDENT_RE:f,NUMBER_RE:p,C_NUMBER_RE:m,
|
||||
BINARY_NUMBER_RE:b,
|
||||
RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",
|
||||
SHEBANG:(e={})=>{const t=/^#![ ]*\//
|
||||
;return e.binary&&(e.begin=((...e)=>e.map((e=>d(e))).join(""))(t,/.*\b/,e.binary,/\b.*/)),
|
||||
a({className:"meta",begin:t,end:/$/,relevance:0,"on:begin":(e,t)=>{
|
||||
0!==e.index&&t.ignoreMatch()}},e)},BACKSLASH_ESCAPE:x,APOS_STRING_MODE:E,
|
||||
QUOTE_STRING_MODE:v,PHRASAL_WORDS_MODE:_,COMMENT:w,C_LINE_COMMENT_MODE:N,
|
||||
C_BLOCK_COMMENT_MODE:y,HASH_COMMENT_MODE:R,NUMBER_MODE:{className:"number",
|
||||
begin:p,relevance:0},C_NUMBER_MODE:{className:"number",begin:m,relevance:0},
|
||||
BINARY_NUMBER_MODE:{className:"number",begin:b,relevance:0},CSS_NUMBER_MODE:{
|
||||
className:"number",
|
||||
begin:p+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",
|
||||
relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",
|
||||
begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[x,{begin:/\[/,end:/\]/,
|
||||
relevance:0,contains:[x]}]}]},TITLE_MODE:{className:"title",begin:h,relevance:0
|
||||
},UNDERSCORE_TITLE_MODE:{className:"title",begin:f,relevance:0},METHOD_GUARD:{
|
||||
begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:e=>Object.assign(e,{
|
||||
"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{
|
||||
t.data._beginMatch!==e[1]&&t.ignoreMatch()}})})
|
||||
;const M=["of","and","for","in","not","or","if","then","parent","list","value"]
|
||||
;function O(e){function t(t,n){
|
||||
return RegExp(d(t),"m"+(e.case_insensitive?"i":"")+(n?"g":""))}class n{
|
||||
constructor(){
|
||||
this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}
|
||||
addRule(e,t){
|
||||
t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]),
|
||||
this.matchAt+=(e=>RegExp(e.toString()+"|").exec("").length-1)(e)+1}compile(){
|
||||
0===this.regexes.length&&(this.exec=()=>null)
|
||||
;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(((e,t="|")=>{
|
||||
const n=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;let s=0,r=""
|
||||
;for(let a=0;a<e.length;a++){s+=1;const i=s;let o=d(e[a])
|
||||
;for(a>0&&(r+=t),r+="(";o.length>0;){const e=n.exec(o);if(null==e){r+=o;break}
|
||||
r+=o.substring(0,e.index),
|
||||
o=o.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?r+="\\"+(Number(e[1])+i):(r+=e[0],
|
||||
"("===e[0]&&s++)}r+=")"}return r})(e),!0),this.lastIndex=0}exec(e){
|
||||
this.matcherRe.lastIndex=this.lastIndex;const t=this.matcherRe.exec(e)
|
||||
;if(!t)return null
|
||||
;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),s=this.matchIndexes[n]
|
||||
;return t.splice(0,n),Object.assign(t,s)}}class s{constructor(){
|
||||
this.rules=[],this.multiRegexes=[],
|
||||
this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){
|
||||
if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n
|
||||
;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))),
|
||||
t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){
|
||||
return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){
|
||||
this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){
|
||||
const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex
|
||||
;let n=t.exec(e)
|
||||
;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{
|
||||
const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)}
|
||||
return n&&(this.regexIndex+=n.position+1,
|
||||
this.regexIndex===this.count&&this.considerAll()),n}}function r(e,t){
|
||||
"."===e.input[e.index-1]&&t.ignoreMatch()}
|
||||
if(e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.")
|
||||
;return e.classNameAliases=a(e.classNameAliases||{}),function n(i,o){const l=i
|
||||
;if(i.compiled)return l
|
||||
;i.compiled=!0,i.__beforeBegin=null,i.keywords=i.keywords||i.beginKeywords
|
||||
;let c=null
|
||||
;if("object"==typeof i.keywords&&(c=i.keywords.$pattern,delete i.keywords.$pattern),
|
||||
i.keywords&&(i.keywords=((e,t)=>{const n={}
|
||||
;return"string"==typeof e?s("keyword",e):Object.keys(e).forEach((t=>{s(t,e[t])
|
||||
})),n;function s(e,s){t&&(s=s.toLowerCase()),s.split(" ").forEach((t=>{
|
||||
const s=t.split("|");n[s[0]]=[e,A(s[0],s[1])]}))}
|
||||
})(i.keywords,e.case_insensitive)),
|
||||
i.lexemes&&c)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ")
|
||||
;return l.keywordPatternRe=t(i.lexemes||c||/\w+/,!0),
|
||||
o&&(i.beginKeywords&&(i.begin="\\b("+i.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",
|
||||
i.__beforeBegin=r),
|
||||
i.begin||(i.begin=/\B|\b/),l.beginRe=t(i.begin),i.endSameAsBegin&&(i.end=i.begin),
|
||||
i.end||i.endsWithParent||(i.end=/\B|\b/),
|
||||
i.end&&(l.endRe=t(i.end)),l.terminator_end=d(i.end)||"",
|
||||
i.endsWithParent&&o.terminator_end&&(l.terminator_end+=(i.end?"|":"")+o.terminator_end)),
|
||||
i.illegal&&(l.illegalRe=t(i.illegal)),
|
||||
void 0===i.relevance&&(i.relevance=1),i.contains||(i.contains=[]),
|
||||
i.contains=[].concat(...i.contains.map((e=>(e=>(e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((t=>a(e,{
|
||||
variants:null},t)))),e.cached_variants?e.cached_variants:L(e)?a(e,{
|
||||
starts:e.starts?a(e.starts):null
|
||||
}):Object.isFrozen(e)?a(e):e))("self"===e?i:e)))),i.contains.forEach((e=>{n(e,l)
|
||||
})),i.starts&&n(i.starts,o),l.matcher=(e=>{const t=new s
|
||||
;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin"
|
||||
}))),e.terminator_end&&t.addRule(e.terminator_end,{type:"end"
|
||||
}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(l),l}(e)}function L(e){
|
||||
return!!e&&(e.endsWithParent||L(e.starts))}function A(e,t){
|
||||
return t?Number(t):(e=>M.includes(e.toLowerCase()))(e)?0:1}function j(e){
|
||||
const t={props:["language","code","autodetect"],data:()=>({detectedLanguage:"",
|
||||
unknownLanguage:!1}),computed:{className(){
|
||||
return this.unknownLanguage?"":"hljs "+this.detectedLanguage},highlighted(){
|
||||
if(!this.autoDetect&&!e.getLanguage(this.language))return console.warn(`The language "${this.language}" you specified could not be found.`),
|
||||
this.unknownLanguage=!0,r(this.code);let t
|
||||
;return this.autoDetect?(t=e.highlightAuto(this.code),
|
||||
this.detectedLanguage=t.language):(t=e.highlight(this.language,this.code,this.ignoreIllegals),
|
||||
this.detectedLanguage=this.language),t.value},autoDetect(){
|
||||
return!(this.language&&(e=this.autodetect,!e&&""!==e));var e},
|
||||
ignoreIllegals:()=>!0},render(e){return e("pre",{},[e("code",{
|
||||
class:this.className,domProps:{innerHTML:this.highlighted}})])}};return{
|
||||
Component:t,VuePlugin:{install(e){e.component("highlightjs",t)}}}}
|
||||
const I=r,S=a,{nodeStream:T,mergeStreams:B}=o,P=Symbol("nomatch");return(e=>{
|
||||
const n=[],r=Object.create(null),a=Object.create(null),i=[];let o=!0
|
||||
;const l=/(^(<[^>]+>|\t|)+|\n)/gm,c="Could not find the language '{}', did you forget to load/include a language module?",u={
|
||||
disableAutodetect:!0,name:"Plain text",contains:[]};let d={
|
||||
noHighlightRe:/^(no-?highlight)$/i,
|
||||
languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",
|
||||
tabReplace:null,useBR:!1,languages:null,__emitter:g};function h(e){
|
||||
return d.noHighlightRe.test(e)}function f(e,t,n,s){const r={code:t,language:e}
|
||||
;N("before:highlight",r);const a=r.result?r.result:p(r.language,r.code,n,s)
|
||||
;return a.code=r.code,N("after:highlight",a),a}function p(e,t,n,a){const i=t
|
||||
;function l(e,t){const n=_.case_insensitive?t[0].toLowerCase():t[0]
|
||||
;return Object.prototype.hasOwnProperty.call(e.keywords,n)&&e.keywords[n]}
|
||||
function u(){null!=y.subLanguage?(()=>{if(""===M)return;let e=null
|
||||
;if("string"==typeof y.subLanguage){
|
||||
if(!r[y.subLanguage])return void k.addText(M)
|
||||
;e=p(y.subLanguage,M,!0,R[y.subLanguage]),R[y.subLanguage]=e.top
|
||||
}else e=m(M,y.subLanguage.length?y.subLanguage:null)
|
||||
;y.relevance>0&&(L+=e.relevance),k.addSublanguage(e.emitter,e.language)
|
||||
})():(()=>{if(!y.keywords)return void k.addText(M);let e=0
|
||||
;y.keywordPatternRe.lastIndex=0;let t=y.keywordPatternRe.exec(M),n="";for(;t;){
|
||||
n+=M.substring(e,t.index);const s=l(y,t);if(s){const[e,r]=s
|
||||
;k.addText(n),n="",L+=r;const a=_.classNameAliases[e]||e;k.addKeyword(t[0],a)
|
||||
}else n+=t[0];e=y.keywordPatternRe.lastIndex,t=y.keywordPatternRe.exec(M)}
|
||||
n+=M.substr(e),k.addText(n)})(),M=""}function g(e){
|
||||
return e.className&&k.openNode(_.classNameAliases[e.className]||e.className),
|
||||
y=Object.create(e,{parent:{value:y}}),y}function h(e,t,n){let r=((e,t)=>{
|
||||
const n=e&&e.exec(t);return n&&0===n.index})(e.endRe,n);if(r){if(e["on:end"]){
|
||||
const n=new s(e);e["on:end"](t,n),n.ignore&&(r=!1)}if(r){
|
||||
for(;e.endsParent&&e.parent;)e=e.parent;return e}}
|
||||
if(e.endsWithParent)return h(e.parent,t,n)}function f(e){
|
||||
return 0===y.matcher.regexIndex?(M+=e[0],1):(S=!0,0)}function b(e){
|
||||
const t=e[0],n=i.substr(e.index),s=h(y,e,n);if(!s)return P;const r=y
|
||||
;r.skip?M+=t:(r.returnEnd||r.excludeEnd||(M+=t),u(),r.excludeEnd&&(M=t));do{
|
||||
y.className&&k.closeNode(),y.skip||y.subLanguage||(L+=y.relevance),y=y.parent
|
||||
}while(y!==s.parent)
|
||||
;return s.starts&&(s.endSameAsBegin&&(s.starts.endRe=s.endRe),
|
||||
g(s.starts)),r.returnEnd?0:t.length}let x={};function E(t,r){const a=r&&r[0]
|
||||
;if(M+=t,null==a)return u(),0
|
||||
;if("begin"===x.type&&"end"===r.type&&x.index===r.index&&""===a){
|
||||
if(M+=i.slice(r.index,r.index+1),!o){const t=Error("0 width match regex")
|
||||
;throw t.languageName=e,t.badRule=x.rule,t}return 1}
|
||||
if(x=r,"begin"===r.type)return function(e){
|
||||
const t=e[0],n=e.rule,r=new s(n),a=[n.__beforeBegin,n["on:begin"]]
|
||||
;for(const n of a)if(n&&(n(e,r),r.ignore))return f(t)
|
||||
;return n&&n.endSameAsBegin&&(n.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),
|
||||
n.skip?M+=t:(n.excludeBegin&&(M+=t),
|
||||
u(),n.returnBegin||n.excludeBegin||(M=t)),g(n),n.returnBegin?0:t.length}(r)
|
||||
;if("illegal"===r.type&&!n){
|
||||
const e=Error('Illegal lexeme "'+a+'" for mode "'+(y.className||"<unnamed>")+'"')
|
||||
;throw e.mode=y,e}if("end"===r.type){const e=b(r);if(e!==P)return e}
|
||||
if("illegal"===r.type&&""===a)return 1
|
||||
;if(j>1e5&&j>3*r.index)throw Error("potential infinite loop, way more iterations than matches")
|
||||
;return M+=a,a.length}const _=v(e);if(!_)throw console.error(c.replace("{}",e)),
|
||||
Error('Unknown language: "'+e+'"');const w=O(_);let N="",y=a||w
|
||||
;const R={},k=new d.__emitter(d);(()=>{const e=[]
|
||||
;for(let t=y;t!==_;t=t.parent)t.className&&e.unshift(t.className)
|
||||
;e.forEach((e=>k.openNode(e)))})();let M="",L=0,A=0,j=0,S=!1;try{
|
||||
for(y.matcher.considerAll();;){
|
||||
j++,S?S=!1:y.matcher.considerAll(),y.matcher.lastIndex=A
|
||||
;const e=y.matcher.exec(i);if(!e)break;const t=E(i.substring(A,e.index),e)
|
||||
;A=e.index+t}return E(i.substr(A)),k.closeAllNodes(),k.finalize(),N=k.toHTML(),{
|
||||
relevance:L,value:N,language:e,illegal:!1,emitter:k,top:y}}catch(t){
|
||||
if(t.message&&t.message.includes("Illegal"))return{illegal:!0,illegalBy:{
|
||||
msg:t.message,context:i.slice(A-100,A+100),mode:t.mode},sofar:N,relevance:0,
|
||||
value:I(i),emitter:k};if(o)return{illegal:!1,relevance:0,value:I(i),emitter:k,
|
||||
language:e,top:y,errorRaised:t};throw t}}function m(e,t){
|
||||
t=t||d.languages||Object.keys(r);const n=(e=>{const t={relevance:0,
|
||||
emitter:new d.__emitter(d),value:I(e),illegal:!1,top:u}
|
||||
;return t.emitter.addText(e),t})(e),s=t.filter(v).filter(w).map((t=>p(t,e,!1)))
|
||||
;s.unshift(n);const a=s.sort(((e,t)=>{
|
||||
if(e.relevance!==t.relevance)return t.relevance-e.relevance
|
||||
;if(e.language&&t.language){if(v(e.language).supersetOf===t.language)return 1
|
||||
;if(v(t.language).supersetOf===e.language)return-1}return 0})),[i,o]=a,l=i
|
||||
;return l.second_best=o,l}function b(e){
|
||||
return d.tabReplace||d.useBR?e.replace(l,(e=>"\n"===e?d.useBR?"<br>":e:d.tabReplace?e.replace(/\t/g,d.tabReplace):e)):e
|
||||
}function x(e){let t=null;const n=(e=>{let t=e.className+" "
|
||||
;t+=e.parentNode?e.parentNode.className:"";const n=d.languageDetectRe.exec(t)
|
||||
;if(n){const t=v(n[1])
|
||||
;return t||(console.warn(c.replace("{}",n[1])),console.warn("Falling back to no-highlight mode for this block.",e)),
|
||||
t?n[1]:"no-highlight"}return t.split(/\s+/).find((e=>h(e)||v(e)))})(e)
|
||||
;if(h(n))return;N("before:highlightBlock",{block:e,language:n
|
||||
}),d.useBR?(t=document.createElement("div"),
|
||||
t.innerHTML=e.innerHTML.replace(/\n/g,"").replace(/<br[ /]*>/g,"\n")):t=e
|
||||
;const s=t.textContent,r=n?f(n,s,!0):m(s),i=T(t);if(i.length){
|
||||
const e=document.createElement("div");e.innerHTML=r.value,r.value=B(i,T(e),s)}
|
||||
r.value=b(r.value),N("after:highlightBlock",{block:e,result:r
|
||||
}),e.innerHTML=r.value,e.className=((e,t,n)=>{const s=t?a[t]:n,r=[e.trim()]
|
||||
;return e.match(/\bhljs\b/)||r.push("hljs"),
|
||||
e.includes(s)||r.push(s),r.join(" ").trim()
|
||||
})(e.className,n,r.language),e.result={language:r.language,re:r.relevance,
|
||||
relavance:r.relevance},r.second_best&&(e.second_best={
|
||||
language:r.second_best.language,re:r.second_best.relevance,
|
||||
relavance:r.second_best.relevance})}const E=()=>{if(E.called)return;E.called=!0
|
||||
;const e=document.querySelectorAll("pre code");n.forEach.call(e,x)}
|
||||
;function v(e){return e=(e||"").toLowerCase(),r[e]||r[a[e]]}
|
||||
function _(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{a[e]=t
|
||||
}))}function w(e){const t=v(e);return t&&!t.disableAutodetect}function N(e,t){
|
||||
const n=e;i.forEach((e=>{e[n]&&e[n](t)}))}Object.assign(e,{highlight:f,
|
||||
highlightAuto:m,
|
||||
fixMarkup:e=>(console.warn("fixMarkup is deprecated and will be removed entirely in v11.0"),
|
||||
console.warn("Please see https://github.com/highlightjs/highlight.js/issues/2534"),
|
||||
b(e)),highlightBlock:x,configure:e=>{
|
||||
e.useBR&&(console.warn("'useBR' option is deprecated and will be removed entirely in v11.0"),
|
||||
console.warn("Please see https://github.com/highlightjs/highlight.js/issues/2559")),
|
||||
d=S(d,e)},initHighlighting:E,initHighlightingOnLoad:()=>{
|
||||
window.addEventListener("DOMContentLoaded",E,!1)},registerLanguage:(t,n)=>{
|
||||
let s=null;try{s=n(e)}catch(e){
|
||||
if(console.error("Language definition for '{}' could not be registered.".replace("{}",t)),
|
||||
!o)throw e;console.error(e),s=u}
|
||||
s.name||(s.name=t),r[t]=s,s.rawDefinition=n.bind(null,e),
|
||||
s.aliases&&_(s.aliases,{languageName:t})},listLanguages:()=>Object.keys(r),
|
||||
getLanguage:v,registerAliases:_,requireLanguage:e=>{
|
||||
console.warn("requireLanguage is deprecated and will be removed entirely in the future."),
|
||||
console.warn("Please see https://github.com/highlightjs/highlight.js/pull/2844")
|
||||
;const t=v(e);if(t)return t
|
||||
;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))},
|
||||
autoDetection:w,inherit:S,addPlugin:e=>{i.push(e)},vuePlugin:j(e).VuePlugin
|
||||
}),e.debugMode=()=>{o=!1},e.safeMode=()=>{o=!0},e.versionString="10.4.1"
|
||||
;for(const e in k)"object"==typeof k[e]&&t(k[e]);return Object.assign(e,k),e
|
||||
})({})}()
|
||||
;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);hljs.registerLanguage("xml",(()=>{"use strict";function e(e){
|
||||
return e?"string"==typeof e?e:e.source:null}function n(e){return a("(?=",e,")")}
|
||||
function a(...n){return n.map((n=>e(n))).join("")}function s(...n){
|
||||
return"("+n.map((n=>e(n))).join("|")+")"}return e=>{
|
||||
const t=a(/[A-Z_]/,a("(",/[A-Z0-9_.-]+:/,")?"),/[A-Z0-9_.-]*/),i={
|
||||
className:"symbol",begin:"&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;"},c={begin:"\\s",
|
||||
contains:[{className:"meta-keyword",begin:"#?[a-z_][a-z1-9_-]+",illegal:"\\n"}]
|
||||
},r=e.inherit(c,{begin:"\\(",end:"\\)"}),l=e.inherit(e.APOS_STRING_MODE,{
|
||||
className:"meta-string"}),g=e.inherit(e.QUOTE_STRING_MODE,{
|
||||
className:"meta-string"}),m={endsWithParent:!0,illegal:/</,relevance:0,
|
||||
contains:[{className:"attr",begin:"[A-Za-z0-9\\._:-]+",relevance:0},{
|
||||
begin:/=\s*/,relevance:0,contains:[{className:"string",endsParent:!0,variants:[{
|
||||
begin:/"/,end:/"/,contains:[i]},{begin:/'/,end:/'/,contains:[i]},{
|
||||
begin:/[^\s"'=<>`]+/}]}]}]};return{name:"HTML, XML",
|
||||
aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],
|
||||
case_insensitive:!0,contains:[{className:"meta",begin:"<![a-z]",end:">",
|
||||
relevance:10,contains:[c,g,l,r,{begin:"\\[",end:"\\]",contains:[{
|
||||
className:"meta",begin:"<![a-z]",end:">",contains:[c,r,g,l]}]}]
|
||||
},e.COMMENT("\x3c!--","--\x3e",{relevance:10}),{begin:"<!\\[CDATA\\[",
|
||||
end:"\\]\\]>",relevance:10},i,{className:"meta",begin:/<\?xml/,end:/\?>/,
|
||||
relevance:10},{className:"tag",begin:"<style(?=\\s|>)",end:">",keywords:{
|
||||
name:"style"},contains:[m],starts:{end:"</style>",returnEnd:!0,
|
||||
subLanguage:["css","xml"]}},{className:"tag",begin:"<script(?=\\s|>)",end:">",
|
||||
keywords:{name:"script"},contains:[m],starts:{end:/<\/script>/,returnEnd:!0,
|
||||
subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:/<>|<\/>/
|
||||
},{className:"tag",begin:a(/</,n(a(t,s(/\/>/,/>/,/\s/)))),end:/\/?>/,contains:[{
|
||||
className:"name",begin:t,relevance:0,starts:m}]},{className:"tag",
|
||||
begin:a(/<\//,n(a(t,/>/))),contains:[{className:"name",begin:t,relevance:0},{
|
||||
begin:/>/,relevance:0}]}]}}})());hljs.registerLanguage("css",(()=>{"use strict";return e=>{
|
||||
var n="[a-zA-Z-][a-zA-Z0-9_-]*",a={
|
||||
begin:/([*]\s?)?(?:[A-Z_.\-\\]+|--[a-zA-Z0-9_-]+)\s*(\/\*\*\/)?:/,
|
||||
returnBegin:!0,end:";",endsWithParent:!0,contains:[{className:"attribute",
|
||||
begin:/\S/,end:":",excludeEnd:!0,starts:{endsWithParent:!0,excludeEnd:!0,
|
||||
contains:[{begin:/[\w-]+\(/,returnBegin:!0,contains:[{className:"built_in",
|
||||
begin:/[\w-]+/},{begin:/\(/,end:/\)/,
|
||||
contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]}]
|
||||
},e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{
|
||||
className:"number",begin:"#[0-9A-Fa-f]+"},{className:"meta",begin:"!important"}]
|
||||
}}]};return{name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,
|
||||
contains:[e.C_BLOCK_COMMENT_MODE,{className:"selector-id",
|
||||
begin:/#[A-Za-z0-9_-]+/},{className:"selector-class",begin:"\\."+n},{
|
||||
className:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",
|
||||
contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},{className:"selector-pseudo",
|
||||
begin:/:(:)?[a-zA-Z0-9_+()"'.-]+/},{begin:"@(page|font-face)",
|
||||
lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]",
|
||||
illegal:/:/,returnBegin:!0,contains:[{className:"keyword",
|
||||
begin:/@-?\w[\w]*(-\w+)*/},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,
|
||||
relevance:0,keywords:"and or not only",contains:[{begin:/[a-z-]+:/,
|
||||
className:"attribute"},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]
|
||||
}]},{className:"selector-tag",begin:n,relevance:0},{begin:/\{/,end:/\}/,
|
||||
illegal:/\S/,contains:[e.C_BLOCK_COMMENT_MODE,{begin:/;/},a]}]}}})());hljs.registerLanguage("javascript",(()=>{"use strict"
|
||||
;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],s=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"])
|
||||
;function r(e){return i("(?=",e,")")}function i(...e){return e.map((e=>{
|
||||
return(n=e)?"string"==typeof n?n:n.source:null;var n})).join("")}return t=>{
|
||||
const c=e,o={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,
|
||||
isTrulyOpeningTag:(e,n)=>{const a=e[0].length+e.index,s=e.input[a]
|
||||
;"<"!==s?">"===s&&(((e,{after:n})=>{const a="</"+e[0].slice(1)
|
||||
;return-1!==e.input.indexOf(a,n)})(e,{after:a
|
||||
})||n.ignoreMatch()):n.ignoreMatch()}},l={$pattern:e,keyword:n.join(" "),
|
||||
literal:a.join(" "),built_in:s.join(" ")
|
||||
},b="\\.([0-9](_?[0-9])*)",g="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",d={
|
||||
className:"number",variants:[{
|
||||
begin:`(\\b(${g})((${b})|\\.)?|(${b}))[eE][+-]?([0-9](_?[0-9])*)\\b`},{
|
||||
begin:`\\b(${g})\\b((${b})\\b|\\.)?|(${b})\\b`},{
|
||||
begin:"\\b(0|[1-9](_?[0-9])*)n\\b"},{
|
||||
begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b"},{
|
||||
begin:"\\b0[bB][0-1](_?[0-1])*n?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*n?\\b"},{
|
||||
begin:"\\b0[0-7]+n?\\b"}],relevance:0},E={className:"subst",begin:"\\$\\{",
|
||||
end:"\\}",keywords:l,contains:[]},u={begin:"html`",end:"",starts:{end:"`",
|
||||
returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"xml"}},_={
|
||||
begin:"css`",end:"",starts:{end:"`",returnEnd:!1,
|
||||
contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"css"}},m={className:"string",
|
||||
begin:"`",end:"`",contains:[t.BACKSLASH_ESCAPE,E]},N={className:"comment",
|
||||
variants:[t.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",
|
||||
begin:"@[A-Za-z]+",contains:[{className:"type",begin:"\\{",end:"\\}",relevance:0
|
||||
},{className:"variable",begin:c+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{
|
||||
begin:/(?=[^\n])\s/,relevance:0}]}]
|
||||
}),t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE]
|
||||
},y=[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,u,_,m,d,t.REGEXP_MODE]
|
||||
;E.contains=y.concat({begin:/\{/,end:/\}/,keywords:l,contains:["self"].concat(y)
|
||||
});const f=[].concat(N,E.contains),A=f.concat([{begin:/\(/,end:/\)/,keywords:l,
|
||||
contains:["self"].concat(f)}]),p={className:"params",begin:/\(/,end:/\)/,
|
||||
excludeBegin:!0,excludeEnd:!0,keywords:l,contains:A};return{name:"Javascript",
|
||||
aliases:["js","jsx","mjs","cjs"],keywords:l,exports:{PARAMS_CONTAINS:A},
|
||||
illegal:/#(?![$_A-z])/,contains:[t.SHEBANG({label:"shebang",binary:"node",
|
||||
relevance:5}),{label:"use_strict",className:"meta",relevance:10,
|
||||
begin:/^\s*['"]use (strict|asm)['"]/
|
||||
},t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,u,_,m,N,d,{
|
||||
begin:i(/[{,\n]\s*/,r(i(/(((\/\/.*$)|(\/\*(\*[^/]|[^*])*\*\/))\s*)*/,c+"\\s*:"))),
|
||||
relevance:0,contains:[{className:"attr",begin:c+r("\\s*:"),relevance:0}]},{
|
||||
begin:"("+t.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",
|
||||
keywords:"return throw case",contains:[N,t.REGEXP_MODE,{className:"function",
|
||||
begin:"(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+t.UNDERSCORE_IDENT_RE+")\\s*=>",
|
||||
returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{
|
||||
begin:t.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0
|
||||
},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:l,contains:A}]}]
|
||||
},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{
|
||||
variants:[{begin:"<>",end:"</>"},{begin:o.begin,"on:begin":o.isTrulyOpeningTag,
|
||||
end:o.end}],subLanguage:"xml",contains:[{begin:o.begin,end:o.end,skip:!0,
|
||||
contains:["self"]}]}],relevance:0},{className:"function",
|
||||
beginKeywords:"function",end:/[{;]/,excludeEnd:!0,keywords:l,
|
||||
contains:["self",t.inherit(t.TITLE_MODE,{begin:c}),p],illegal:/%/},{
|
||||
beginKeywords:"while if switch catch for"},{className:"function",
|
||||
begin:t.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",
|
||||
returnBegin:!0,contains:[p,t.inherit(t.TITLE_MODE,{begin:c})]},{variants:[{
|
||||
begin:"\\."+c},{begin:"\\$"+c}],relevance:0},{className:"class",
|
||||
beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"[\]]/,contains:[{
|
||||
beginKeywords:"extends"},t.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/,
|
||||
end:/[{;]/,excludeEnd:!0,contains:[t.inherit(t.TITLE_MODE,{begin:c}),"self",p]
|
||||
},{begin:"(get|set)\\s+(?="+c+"\\()",end:/\{/,keywords:"get set",
|
||||
contains:[t.inherit(t.TITLE_MODE,{begin:c}),{begin:/\(\)/},p]},{begin:/\$[(.]/}]
|
||||
}}})());hljs.registerLanguage("elm",(()=>{"use strict";return e=>{const n={
|
||||
variants:[e.COMMENT("--","$"),e.COMMENT(/\{-/,/-\}/,{contains:["self"]})]},i={
|
||||
className:"type",begin:"\\b[A-Z][\\w']*",relevance:0},s={begin:"\\(",end:"\\)",
|
||||
illegal:'"',contains:[{className:"type",
|
||||
begin:"\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?"},n]};return{name:"Elm",
|
||||
keywords:"let in if then else case of where module import exposing type alias as infix infixl infixr port effect command subscription",
|
||||
contains:[{beginKeywords:"port effect module",end:"exposing",
|
||||
keywords:"port effect module where command subscription exposing",
|
||||
contains:[s,n],illegal:"\\W\\.|;"},{begin:"import",end:"$",
|
||||
keywords:"import as exposing",contains:[s,n],illegal:"\\W\\.|;"},{begin:"type",
|
||||
end:"$",keywords:"type alias",contains:[i,s,{begin:/\{/,end:/\}/,
|
||||
contains:s.contains},n]},{beginKeywords:"infix infixl infixr",end:"$",
|
||||
contains:[e.C_NUMBER_MODE,n]},{begin:"port",end:"$",keywords:"port",contains:[n]
|
||||
},{className:"string",begin:"'\\\\?.",end:"'",illegal:"."
|
||||
},e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,i,e.inherit(e.TITLE_MODE,{
|
||||
begin:"^[_a-z][\\w']*"}),n,{begin:"->|<-"}],illegal:/;/}}})());
|
@ -5,14 +5,14 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;700&family=Nunito:ital,wght@0,300;0,600;0,800;1,300&family=Nunito+Sans:wght@800&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.5.0/build/styles/a11y-dark.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css">
|
||||
<link rel="stylesheet" href="https://nope.rhg.dev/dist/3.0.0/core.min.css">
|
||||
<link rel="stylesheet" href="/vendor/prism.css">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<link rel="shortcut icon" href="/favicon.png" type="image/x-png">
|
||||
</head>
|
||||
<body>
|
||||
<script src="/highlight.pack.js"></script>
|
||||
<script src="/vendor/prism.js" data-manual></script>
|
||||
<script src="/dist/elm.js"></script>
|
||||
<script src="/dist/flags.js"></script>
|
||||
<script src="/main.js"></script>
|
||||
|
@ -27,19 +27,16 @@ window.addEventListener('keypress', (e) => {
|
||||
})
|
||||
|
||||
// HighlightJS custom element
|
||||
customElements.define('highlight-js', class HighlightJS extends HTMLElement {
|
||||
customElements.define('prism-js', class HighlightJS extends HTMLElement {
|
||||
constructor () { super() }
|
||||
connectedCallback () {
|
||||
const pre = document.createElement('pre')
|
||||
const code = document.createElement('code')
|
||||
|
||||
pre.className = `language-elm`
|
||||
code.innerText = this.body
|
||||
|
||||
pre.appendChild(code)
|
||||
pre.textContent = this.body
|
||||
|
||||
this.appendChild(pre)
|
||||
window.hljs.highlightBlock(pre)
|
||||
window.Prism.highlightElement(pre)
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -28,7 +28,6 @@
|
||||
--shadow-dark: 0 0.5em 2em rgb(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
|
||||
/* Resets */
|
||||
@media screen and (min-width: 1920px ) {
|
||||
html { font-size: 20px; }
|
||||
@ -137,12 +136,16 @@ hr { border: 0; }
|
||||
max-width: 36em;
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
.markdown > h1:not(:first-child),
|
||||
.markdown > h2:not(:first-child),
|
||||
.markdown > h3:not(:first-child) {
|
||||
.markdown > h2:not(:first-child) {
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
.markdown > h3:not(:first-child),
|
||||
.markdown > h4:not(:first-child) {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
5
docs/public/vendor/prism.css
vendored
Normal file
5
docs/public/vendor/prism.css
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
|
||||
|
||||
code[class*=language-elm],pre[class*=language-elm]{color:#ccc;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-elm]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-elm],pre[class*=language-elm]{background:#2d2d2d}:not(pre)>code[class*=language-elm]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}
|
3
docs/public/vendor/prism.js
vendored
Normal file
3
docs/public/vendor/prism.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -2,6 +2,7 @@ module Main exposing (main)
|
||||
|
||||
import Browser
|
||||
import Browser.Navigation as Nav exposing (Key)
|
||||
import Effect
|
||||
import Gen.Pages as Pages
|
||||
import Gen.Route as Route
|
||||
import Ports
|
||||
@ -41,14 +42,13 @@ init flags url key =
|
||||
( shared, sharedCmd ) =
|
||||
Shared.init (request { url = url, key = key }) flags
|
||||
|
||||
( page, pageCmd, sharedPageCmd ) =
|
||||
( page, effect ) =
|
||||
Pages.init (Route.fromUrl url) shared url key
|
||||
in
|
||||
( Model url key shared page
|
||||
, Cmd.batch
|
||||
[ Cmd.map Shared sharedCmd
|
||||
, Cmd.map Shared sharedPageCmd
|
||||
, Cmd.map Page pageCmd
|
||||
, Effect.toCmd ( Shared, Page ) effect
|
||||
]
|
||||
)
|
||||
|
||||
@ -89,13 +89,12 @@ update msg model =
|
||||
|
||||
else
|
||||
let
|
||||
( page, pageCmd, sharedPageCmd ) =
|
||||
( page, effect ) =
|
||||
Pages.init (Route.fromUrl url) model.shared url model.key
|
||||
in
|
||||
( { model | url = url, page = page }
|
||||
, Cmd.batch
|
||||
[ Cmd.map Page pageCmd
|
||||
, Cmd.map Shared sharedPageCmd
|
||||
[ Effect.toCmd ( Shared, Page ) effect
|
||||
, Ports.onUrlChange ()
|
||||
]
|
||||
)
|
||||
@ -111,14 +110,11 @@ update msg model =
|
||||
|
||||
Page pageMsg ->
|
||||
let
|
||||
( page, pageCmd, sharedPageCmd ) =
|
||||
( page, effect ) =
|
||||
Pages.update pageMsg model.page model.shared model.url model.key
|
||||
in
|
||||
( { model | page = page }
|
||||
, Cmd.batch
|
||||
[ Cmd.map Page pageCmd
|
||||
, Cmd.map Shared sharedPageCmd
|
||||
]
|
||||
, Effect.toCmd ( Shared, Page ) effect
|
||||
)
|
||||
|
||||
|
||||
|
@ -21,7 +21,6 @@ type alias Flags =
|
||||
|
||||
type alias Model =
|
||||
{ index : Index
|
||||
, token : Maybe Token
|
||||
}
|
||||
|
||||
|
||||
|
@ -164,7 +164,7 @@ markdown options str =
|
||||
Html.Keyed.node "div"
|
||||
[]
|
||||
[ ( body
|
||||
, Html.node "highlight-js"
|
||||
, Html.node "prism-js"
|
||||
[ Attr.property "body" (Json.string body)
|
||||
, Attr.property "language" (Json.string "elm")
|
||||
]
|
||||
|
@ -15,7 +15,7 @@ module UI.Layout exposing
|
||||
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as Attr
|
||||
import Page exposing (Page, shared)
|
||||
import Page exposing (Page)
|
||||
import Request exposing (Request)
|
||||
import Shared
|
||||
import UI
|
||||
|
@ -16,7 +16,7 @@ toId : String -> String
|
||||
toId =
|
||||
String.toLower
|
||||
>> String.words
|
||||
>> List.map (String.filter Char.isAlphaNum)
|
||||
>> List.map (String.filter (\c -> c == '-' || Char.isAlphaNum c))
|
||||
>> String.join "-"
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user