Use elm-spa #1
|
@ -88,6 +88,7 @@ func init() {
|
||||||
"/static/favicon.png": true,
|
"/static/favicon.png": true,
|
||||||
"/debug/requests": true,
|
"/debug/requests": true,
|
||||||
"/debug/events": true,
|
"/debug/events": true,
|
||||||
|
"/sign-in": true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
elm-stuff
|
elm-stuff
|
||||||
|
node_modules
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
elm-stuff
|
||||||
|
node_modules
|
|
@ -0,0 +1,24 @@
|
||||||
|
# your elm-spa
|
||||||
|
> learn more at [https://elm-spa.dev](https://elm-spa.dev)
|
||||||
|
|
||||||
|
### local development
|
||||||
|
|
||||||
|
```
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## folder structure
|
||||||
|
|
||||||
|
```elm
|
||||||
|
README.md -- this file you're reading đź‘€
|
||||||
|
elm.json -- has project dependencies
|
||||||
|
src/
|
||||||
|
Main.elm -- the entrypoint to the app
|
||||||
|
Global.elm -- share state across pages
|
||||||
|
Transitions.elm -- smoothly animate between pages
|
||||||
|
Ports.elm -- communicate with JS
|
||||||
|
Pages/ -- where all your pages go
|
||||||
|
Layouts/ -- reusable views around pages
|
||||||
|
Components/ -- views shared across the site
|
||||||
|
Utils/ -- a place for helper functions
|
||||||
|
```
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"ui": "Element"
|
||||||
|
}
|
|
@ -1,24 +1,25 @@
|
||||||
{
|
{
|
||||||
"type": "application",
|
"type": "application",
|
||||||
"source-directories": [
|
"source-directories": [
|
||||||
"src"
|
"src",
|
||||||
|
"elm-stuff/.elm-spa"
|
||||||
],
|
],
|
||||||
"elm-version": "0.19.1",
|
"elm-version": "0.19.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"direct": {
|
"direct": {
|
||||||
"elm/browser": "1.0.2",
|
|
||||||
"elm/core": "1.0.4",
|
"elm/core": "1.0.4",
|
||||||
"elm/html": "1.0.0",
|
"elm/html": "1.0.0",
|
||||||
"elm/http": "2.0.0",
|
"elm/http": "2.0.0",
|
||||||
"elm/json": "1.1.3",
|
"elm/json": "1.1.3",
|
||||||
"elm/time": "1.0.0",
|
|
||||||
"elm/url": "1.0.0",
|
"elm/url": "1.0.0",
|
||||||
"rtfeldman/elm-iso8601-date-strings": "1.1.3"
|
"mdgriffith/elm-ui": "1.1.5",
|
||||||
|
"ryannhg/elm-spa": "3.0.0"
|
||||||
},
|
},
|
||||||
"indirect": {
|
"indirect": {
|
||||||
|
"elm/browser": "1.0.2",
|
||||||
"elm/bytes": "1.0.8",
|
"elm/bytes": "1.0.8",
|
||||||
"elm/file": "1.0.5",
|
"elm/file": "1.0.5",
|
||||||
"elm/parser": "1.1.0",
|
"elm/time": "1.0.0",
|
||||||
"elm/virtual-dom": "1.0.2"
|
"elm/virtual-dom": "1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
# sends all routes to /index.html
|
||||||
|
# (so you can handle 404s there!)
|
||||||
|
[[redirects]]
|
||||||
|
from = "/*"
|
||||||
|
to = "/index.html"
|
||||||
|
status = 200
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"name": "my-elm-spa-project",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "learn more at https://elm-spa.dev",
|
||||||
|
"scripts": {
|
||||||
|
"start": "npm install && npm run dev",
|
||||||
|
"dev": "npm run elm:spa:build && npm run build:watch",
|
||||||
|
"build": "npm run elm:spa:build && npm run elm:compile",
|
||||||
|
"build:watch": "concurrently --raw --kill-others \"npm run elm:spa:watch\" \"npm run elm:live\"",
|
||||||
|
"elm:compile": "elm make src/Main.elm --output=public/dist/elm.compiled.js --optimize",
|
||||||
|
"elm:live": "elm-live src/Main.elm --dir=public --start-page=index.html --open --pushstate --port=1234 -- --output=public/dist/elm.compiled.js --debug",
|
||||||
|
"elm:spa:build": "elm-spa build .",
|
||||||
|
"elm:spa:watch": "chokidar src/Pages -c \"npm run elm:spa:build\""
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"chokidar-cli": "2.1.0",
|
||||||
|
"concurrently": "5.0.0",
|
||||||
|
"elm": "0.19.1-3",
|
||||||
|
"elm-live": "4.0.1",
|
||||||
|
"elm-spa": "3.0.3"
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,19 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||||
|
<title>our-elm-spa</title>
|
||||||
|
<link rel="stylesheet" href="/styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script src="/dist/elm.compiled.js"></script>
|
||||||
|
<script src="/ports.js"></script>
|
||||||
|
<script>
|
||||||
|
window.addEventListener('load', _ =>
|
||||||
|
window.ports.init(Elm.Main.init())
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,17 @@
|
||||||
|
// On load, listen to Elm!
|
||||||
|
window.addEventListener('load', _ => {
|
||||||
|
window.ports = {
|
||||||
|
init: (app) =>
|
||||||
|
app.ports.outgoing.subscribe(({ action, data }) =>
|
||||||
|
actions[action]
|
||||||
|
? actions[action](data)
|
||||||
|
: console.warn(`I didn't recognize action "${action}".`)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// maps actions to functions!
|
||||||
|
const actions = {
|
||||||
|
'LOG': (message) =>
|
||||||
|
console.log(`From Elm:`, message)
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
/* you can include CSS here */
|
||||||
|
html, body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
|
height: 100%;
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
# src/Components
|
||||||
|
> views shared across the site
|
|
@ -0,0 +1,60 @@
|
||||||
|
module Global exposing
|
||||||
|
( Flags
|
||||||
|
, Model
|
||||||
|
, Msg(..)
|
||||||
|
, init
|
||||||
|
, subscriptions
|
||||||
|
, update
|
||||||
|
)
|
||||||
|
|
||||||
|
import Generated.Routes as Routes exposing (Route)
|
||||||
|
import Ports
|
||||||
|
|
||||||
|
|
||||||
|
type alias Flags =
|
||||||
|
()
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ token : Maybe String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= SignIn String
|
||||||
|
| SignOut
|
||||||
|
|
||||||
|
|
||||||
|
type alias Commands msg =
|
||||||
|
{ navigate : Route -> Cmd msg
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init : Commands msg -> Flags -> ( Model, Cmd Msg, Cmd msg )
|
||||||
|
init _ _ =
|
||||||
|
( { token = Nothing
|
||||||
|
}
|
||||||
|
, Cmd.none
|
||||||
|
, Ports.log "Hello!"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
update : Commands msg -> Msg -> Model -> ( Model, Cmd Msg, Cmd msg )
|
||||||
|
update _ msg model =
|
||||||
|
case msg of
|
||||||
|
SignIn token ->
|
||||||
|
( { model | token = Just token }
|
||||||
|
, Cmd.none
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
SignOut ->
|
||||||
|
( { model | token = Nothing }
|
||||||
|
, Cmd.none
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
subscriptions : Model -> Sub Msg
|
||||||
|
subscriptions _ =
|
||||||
|
Sub.none
|
|
@ -0,0 +1,70 @@
|
||||||
|
module Layout exposing (view)
|
||||||
|
|
||||||
|
import Element exposing (..)
|
||||||
|
import Element.Font as Font
|
||||||
|
import Generated.Routes as Routes exposing (Route, routes)
|
||||||
|
import Utils.Spa as Spa
|
||||||
|
|
||||||
|
|
||||||
|
view : Spa.LayoutContext msg -> Element msg
|
||||||
|
view { page, route, global } =
|
||||||
|
case global.token of
|
||||||
|
Just _ ->
|
||||||
|
column [ height fill, width (fill |> maximum 750), centerX ]
|
||||||
|
[ el [ padding 15 ] (viewHeader route)
|
||||||
|
, page
|
||||||
|
]
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
if route /= routes.signIn then
|
||||||
|
el [ centerX, centerY ]
|
||||||
|
(link
|
||||||
|
[ Font.underline
|
||||||
|
, Font.color (rgb255 204 75 75)
|
||||||
|
, Font.size 48
|
||||||
|
]
|
||||||
|
{ label = text "Login"
|
||||||
|
, url = Routes.toPath routes.signIn
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
else
|
||||||
|
el
|
||||||
|
[ centerX, centerY ]
|
||||||
|
page
|
||||||
|
|
||||||
|
|
||||||
|
viewHeader : Route -> Element msg
|
||||||
|
viewHeader currentRoute =
|
||||||
|
row
|
||||||
|
[ spacing 24
|
||||||
|
, paddingEach { top = 32, left = 16, right = 16, bottom = 0 }
|
||||||
|
, centerX
|
||||||
|
, width (fill |> maximum 750)
|
||||||
|
]
|
||||||
|
[ viewLink currentRoute ( "Mi", routes.top )
|
||||||
|
, viewLink currentRoute ( "Switch Data", routes.switches )
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
viewLink : Route -> ( String, Route ) -> Element msg
|
||||||
|
viewLink currentRoute ( label, route ) =
|
||||||
|
if currentRoute == route then
|
||||||
|
el
|
||||||
|
[ Font.underline
|
||||||
|
, Font.color (rgb255 204 75 75)
|
||||||
|
, alpha 0.5
|
||||||
|
, Font.size 16
|
||||||
|
]
|
||||||
|
(text label)
|
||||||
|
|
||||||
|
else
|
||||||
|
link
|
||||||
|
[ Font.underline
|
||||||
|
, Font.color (rgb255 204 75 75)
|
||||||
|
, Font.size 16
|
||||||
|
, mouseOver [ alpha 0.5 ]
|
||||||
|
]
|
||||||
|
{ label = text label
|
||||||
|
, url = Routes.toPath route
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
# src/Layouts
|
||||||
|
> where all your pages go
|
|
@ -1,185 +1,27 @@
|
||||||
module Main exposing (main)
|
module Main exposing (main)
|
||||||
|
|
||||||
import Browser
|
import Generated.Pages as Pages
|
||||||
import Browser.Navigation as Nav
|
import Generated.Routes as Routes exposing (routes)
|
||||||
import Html exposing (..)
|
import Global
|
||||||
import Html.Attributes exposing (..)
|
import Spa
|
||||||
import Html.Events exposing (onInput)
|
import Transitions
|
||||||
import Http
|
|
||||||
import Json.Decode as D
|
|
||||||
import Mi exposing (..)
|
|
||||||
import Page exposing (..)
|
|
||||||
import Page.SwitchData as PSD
|
|
||||||
import SwitchData
|
|
||||||
import Url
|
|
||||||
import Url.Builder
|
|
||||||
|
|
||||||
|
|
||||||
main : Program () Model Msg
|
main : Spa.Program Global.Flags Global.Model Global.Msg Pages.Model Pages.Msg
|
||||||
main =
|
main =
|
||||||
Browser.application
|
Spa.create
|
||||||
{ init = init
|
{ ui = Spa.usingElmUi
|
||||||
, view = view
|
, transitions = Transitions.transitions
|
||||||
, update = update
|
, routing =
|
||||||
, subscriptions = subscriptions
|
{ routes = Routes.parsers
|
||||||
, onUrlChange = UrlChanged
|
, toPath = Routes.toPath
|
||||||
, onUrlRequest = LinkClicked
|
, notFound = routes.notFound
|
||||||
|
, afterNavigate = Nothing
|
||||||
}
|
}
|
||||||
|
, global =
|
||||||
|
{ init = Global.init
|
||||||
type alias Model =
|
, update = Global.update
|
||||||
{ key : Nav.Key
|
, subscriptions = Global.subscriptions
|
||||||
, url : Url.Url
|
|
||||||
, token : Maybe String
|
|
||||||
, token_data : Maybe TokenData
|
|
||||||
, switch_data_model : PSD.Model
|
|
||||||
}
|
}
|
||||||
|
, page = Pages.page
|
||||||
|
|
||||||
init : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
|
|
||||||
init flags url key =
|
|
||||||
( Model
|
|
||||||
key
|
|
||||||
url
|
|
||||||
Nothing
|
|
||||||
Nothing
|
|
||||||
PSD.init
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
type Page
|
|
||||||
= NotFound
|
|
||||||
| Index
|
|
||||||
| SwitchData PSD.Model
|
|
||||||
|
|
||||||
|
|
||||||
type Msg
|
|
||||||
= LinkClicked Browser.UrlRequest
|
|
||||||
| UrlChanged Url.Url
|
|
||||||
| TokenInput String
|
|
||||||
| TokenValidate (Result Http.Error TokenData)
|
|
||||||
| Logout
|
|
||||||
| GotSwitchDataMSG PSD.Msg
|
|
||||||
|
|
||||||
|
|
||||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
|
||||||
update msg model =
|
|
||||||
case msg of
|
|
||||||
LinkClicked urlRequest ->
|
|
||||||
case urlRequest of
|
|
||||||
Browser.Internal url ->
|
|
||||||
( model
|
|
||||||
, Nav.pushUrl
|
|
||||||
model.key
|
|
||||||
(Url.toString url)
|
|
||||||
)
|
|
||||||
|
|
||||||
Browser.External href ->
|
|
||||||
( model, Nav.load href )
|
|
||||||
|
|
||||||
UrlChanged url ->
|
|
||||||
case url.path of
|
|
||||||
"/logout" ->
|
|
||||||
( { model | token = Nothing, token_data = Nothing }
|
|
||||||
, Nav.pushUrl
|
|
||||||
model.key
|
|
||||||
"/"
|
|
||||||
)
|
|
||||||
|
|
||||||
default ->
|
|
||||||
( { model | url = url }
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
TokenInput token ->
|
|
||||||
( { model | token = Just token }
|
|
||||||
, request
|
|
||||||
"GET"
|
|
||||||
token
|
|
||||||
"/.within/tokeninfo"
|
|
||||||
Http.emptyBody
|
|
||||||
(expectJson TokenValidate tokenDecoder)
|
|
||||||
)
|
|
||||||
|
|
||||||
TokenValidate result ->
|
|
||||||
case result of
|
|
||||||
Ok data ->
|
|
||||||
( { model | token_data = Just data }
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
Err _ ->
|
|
||||||
( { model | token = Nothing }
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
Logout ->
|
|
||||||
( { model | token = Nothing, token_data = Nothing }
|
|
||||||
, Nav.load "/"
|
|
||||||
)
|
|
||||||
|
|
||||||
GotSwitchDataMSG psd_msg ->
|
|
||||||
let
|
|
||||||
( psd_model, cmd ) =
|
|
||||||
PSD.update
|
|
||||||
(Maybe.withDefault "" model.token)
|
|
||||||
psd_msg
|
|
||||||
model.switch_data_model
|
|
||||||
in
|
|
||||||
( { model | switch_data_model = psd_model }
|
|
||||||
, Cmd.map GotSwitchDataMSG cmd
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
subscriptions : Model -> Sub Msg
|
|
||||||
subscriptions _ =
|
|
||||||
Sub.none
|
|
||||||
|
|
||||||
|
|
||||||
view : Model -> Browser.Document Msg
|
|
||||||
view model =
|
|
||||||
case model.token_data of
|
|
||||||
Nothing ->
|
|
||||||
{ title = "Login"
|
|
||||||
, body =
|
|
||||||
[ node "main"
|
|
||||||
[ style "align" "center" ]
|
|
||||||
[ h1 [] [ text "Login" ]
|
|
||||||
, viewInput "password" "API Token" "" TokenInput
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Just token_data ->
|
|
||||||
case model.url.path of
|
|
||||||
"/" ->
|
|
||||||
template "Mi"
|
|
||||||
[ h1 [] [ text "Mi" ]
|
|
||||||
, h2 [] [ text "TODO" ]
|
|
||||||
, ul []
|
|
||||||
[ li [] [ text "Switch CRUD" ]
|
|
||||||
, li [] [ text "POSSE manual announcement" ]
|
|
||||||
]
|
|
||||||
, h2 [] [ text "Token data" ]
|
|
||||||
, p []
|
|
||||||
[ text "Token sub: "
|
|
||||||
, text token_data.sub
|
|
||||||
, Html.br [] []
|
|
||||||
, text "ID: "
|
|
||||||
, text token_data.jti
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
"/switch" ->
|
|
||||||
PSD.view model.switch_data_model
|
|
||||||
|
|
||||||
other ->
|
|
||||||
template "Not found"
|
|
||||||
[ h1 [] [ text "Not found" ]
|
|
||||||
, p []
|
|
||||||
[ text "The requested URL "
|
|
||||||
, b [] [ text other ]
|
|
||||||
, text " was not found."
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
module Page exposing (..)
|
|
||||||
|
|
||||||
import Browser exposing (Document)
|
|
||||||
import Html exposing (Html, a, div, input, node, p, span, text)
|
|
||||||
import Html.Attributes exposing (class, href, placeholder, style, type_, value)
|
|
||||||
import Html.Events exposing (onInput)
|
|
||||||
|
|
||||||
|
|
||||||
template : String -> List (Html msg) -> Browser.Document msg
|
|
||||||
template title body =
|
|
||||||
{ title = title
|
|
||||||
, body =
|
|
||||||
[ node "main"
|
|
||||||
[]
|
|
||||||
[ navBar
|
|
||||||
, div [] body
|
|
||||||
, footer
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
navBar : Html msg
|
|
||||||
navBar =
|
|
||||||
node "nav"
|
|
||||||
[]
|
|
||||||
[ p []
|
|
||||||
[ viewLink "/" "Mi"
|
|
||||||
, text " - "
|
|
||||||
, viewLink "/switch" "Switch tracker"
|
|
||||||
, span
|
|
||||||
[ class "right" ]
|
|
||||||
[ viewLink "/logout" "Logout" ]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
footer : Html msg
|
|
||||||
footer =
|
|
||||||
node "footer"
|
|
||||||
[]
|
|
||||||
[ p []
|
|
||||||
[ a [ href "https://within.website" ] [ text "From Within" ]
|
|
||||||
, text " - "
|
|
||||||
, a [ href "https://tulpa.dev/cadey/mi" ] [ text "Source code" ]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
viewInput : String -> String -> String -> (String -> msg) -> Html msg
|
|
||||||
viewInput t p v toMsg =
|
|
||||||
input [ type_ t, placeholder p, value v, onInput toMsg ] []
|
|
||||||
|
|
||||||
|
|
||||||
viewLink : String -> String -> Html msg
|
|
||||||
viewLink path title =
|
|
||||||
a [ href path ] [ text title ]
|
|
|
@ -1,102 +0,0 @@
|
||||||
module Page.SwitchData exposing
|
|
||||||
( Model
|
|
||||||
, Msg
|
|
||||||
, init
|
|
||||||
, update
|
|
||||||
, view
|
|
||||||
)
|
|
||||||
|
|
||||||
import Browser exposing (Document)
|
|
||||||
import Html exposing (..)
|
|
||||||
import Html.Attributes exposing (..)
|
|
||||||
import Http
|
|
||||||
import Json.Decode exposing (list)
|
|
||||||
import Mi
|
|
||||||
import Page
|
|
||||||
import SwitchData
|
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
|
||||||
{ page : Int
|
|
||||||
, limit : Int
|
|
||||||
, data : Data
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type Data
|
|
||||||
= Init
|
|
||||||
| HaveData (List SwitchData.Switch)
|
|
||||||
| Error String
|
|
||||||
|
|
||||||
|
|
||||||
init : Model
|
|
||||||
init =
|
|
||||||
Model
|
|
||||||
0
|
|
||||||
40
|
|
||||||
Init
|
|
||||||
|
|
||||||
|
|
||||||
type Msg
|
|
||||||
= NeedData
|
|
||||||
| Settings Int Int
|
|
||||||
| GotData (Result Http.Error (List SwitchData.Switch))
|
|
||||||
|
|
||||||
|
|
||||||
update : String -> Msg -> Model -> ( Model, Cmd Msg )
|
|
||||||
update token msg model =
|
|
||||||
if token == "" then
|
|
||||||
( model, Cmd.none )
|
|
||||||
|
|
||||||
else
|
|
||||||
case msg of
|
|
||||||
NeedData ->
|
|
||||||
( model
|
|
||||||
, Mi.request
|
|
||||||
"GET"
|
|
||||||
token
|
|
||||||
(SwitchData.listURL model.limit model.page)
|
|
||||||
Http.emptyBody
|
|
||||||
(Mi.expectJson GotData (Json.Decode.list SwitchData.decoder))
|
|
||||||
)
|
|
||||||
|
|
||||||
Settings page limit ->
|
|
||||||
( { model | page = page, limit = limit }
|
|
||||||
, Mi.request
|
|
||||||
"GET"
|
|
||||||
token
|
|
||||||
(SwitchData.listURL model.limit model.page)
|
|
||||||
Http.emptyBody
|
|
||||||
(Mi.expectJson GotData (Json.Decode.list SwitchData.decoder))
|
|
||||||
)
|
|
||||||
|
|
||||||
GotData result ->
|
|
||||||
case result of
|
|
||||||
Ok data ->
|
|
||||||
( { model | data = HaveData data }
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
Err _ ->
|
|
||||||
( { model | data = Error "got an error" }
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
view : Model -> Browser.Document msg
|
|
||||||
view model =
|
|
||||||
case model.data of
|
|
||||||
Init ->
|
|
||||||
Page.template "Switch data"
|
|
||||||
[ h1 [] [ text "loading data..." ] ]
|
|
||||||
|
|
||||||
HaveData _ ->
|
|
||||||
Page.template "Switch data"
|
|
||||||
[ h1 [] [ text "Switch data here" ]
|
|
||||||
]
|
|
||||||
|
|
||||||
Error msg ->
|
|
||||||
Page.template "Switch data error"
|
|
||||||
[ h1 [] [ text "oh no got an error" ]
|
|
||||||
, p [] [ text msg ]
|
|
||||||
]
|
|
|
@ -1,104 +0,0 @@
|
||||||
module Page.Token exposing
|
|
||||||
( Model
|
|
||||||
, Msg
|
|
||||||
, init
|
|
||||||
, update
|
|
||||||
, view
|
|
||||||
)
|
|
||||||
|
|
||||||
import Browser
|
|
||||||
import Html exposing (Html, h1, h2, li, p, text, ul)
|
|
||||||
import Http exposing (..)
|
|
||||||
import Mi exposing (TokenData, expectJson, request, tokenDecoder)
|
|
||||||
import Page
|
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
|
||||||
{ token : Maybe String
|
|
||||||
, token_data : Maybe TokenData
|
|
||||||
, error : Maybe String
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
init : Model
|
|
||||||
init =
|
|
||||||
Model Nothing Nothing Nothing
|
|
||||||
|
|
||||||
|
|
||||||
type Msg
|
|
||||||
= Init
|
|
||||||
| GotTokenData (Result Http.Error TokenData)
|
|
||||||
|
|
||||||
|
|
||||||
update : Msg -> String -> Model -> ( Model, Cmd Msg )
|
|
||||||
update msg token model =
|
|
||||||
case msg of
|
|
||||||
Init ->
|
|
||||||
( { model | token = Just token }
|
|
||||||
, Mi.request
|
|
||||||
"GET"
|
|
||||||
token
|
|
||||||
"/.within/tokeninfo"
|
|
||||||
Http.emptyBody
|
|
||||||
(Mi.expectJson GotTokenData tokenDecoder)
|
|
||||||
)
|
|
||||||
|
|
||||||
GotTokenData result ->
|
|
||||||
case result of
|
|
||||||
Ok data ->
|
|
||||||
( { model | token_data = Just data }
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
Err (BadUrl val) ->
|
|
||||||
( { model | error = Just ("bad URL " ++ val) }
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
Err Timeout ->
|
|
||||||
( { model | error = Just "Timeout" }
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
Err NetworkError ->
|
|
||||||
( { model | error = Just "network error" }
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
Err (BadStatus code) ->
|
|
||||||
( { model | error = Just ("bad status code " ++ String.fromInt code) }
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
Err (BadBody err_msg) ->
|
|
||||||
( { model | error = Just err_msg }
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
view : Model -> Browser.Document msg
|
|
||||||
view model =
|
|
||||||
case model.token_data of
|
|
||||||
Nothing ->
|
|
||||||
Page.template "No token data?"
|
|
||||||
[ h1 [] [ text "No token data?" ]
|
|
||||||
, p [] [ text "this should be impossible" ]
|
|
||||||
]
|
|
||||||
|
|
||||||
Just token_data ->
|
|
||||||
Page.template "Mi"
|
|
||||||
[ h1 [] [ text "Mi" ]
|
|
||||||
, h2 [] [ text "TODO" ]
|
|
||||||
, ul []
|
|
||||||
[ li [] [ text "Switch CRUD" ]
|
|
||||||
, li [] [ text "POSSE manual announcement" ]
|
|
||||||
]
|
|
||||||
, h2 [] [ text "Token data" ]
|
|
||||||
, p []
|
|
||||||
[ text "Token sub: "
|
|
||||||
, text token_data.sub
|
|
||||||
, Html.br [] []
|
|
||||||
, text "ID: "
|
|
||||||
, text token_data.jti
|
|
||||||
]
|
|
||||||
]
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
module Pages.NotFound exposing (Model, Msg, page)
|
||||||
|
|
||||||
|
import Element exposing (..)
|
||||||
|
import Element.Font as Font
|
||||||
|
import Generated.Params as Params
|
||||||
|
import Generated.Routes as Routes exposing (routes)
|
||||||
|
import Spa.Page
|
||||||
|
import Utils.Spa exposing (Page)
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
()
|
||||||
|
|
||||||
|
|
||||||
|
type alias Msg =
|
||||||
|
Never
|
||||||
|
|
||||||
|
|
||||||
|
page : Page Params.NotFound Model Msg model msg appMsg
|
||||||
|
page =
|
||||||
|
Spa.Page.static
|
||||||
|
{ title = always "not found | elm-spa"
|
||||||
|
, view = always view
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
view : Element Msg
|
||||||
|
view =
|
||||||
|
column [ centerX, centerY, spacing 16 ]
|
||||||
|
[ el [ Font.size 32, Font.semiBold ] (text "404 is life.")
|
||||||
|
, link [ Font.size 16, Font.underline, centerX, Font.color (rgb255 204 75 75), mouseOver [ alpha 0.5 ] ]
|
||||||
|
{ label = text "back home?"
|
||||||
|
, url = Routes.toPath routes.top
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,3 @@
|
||||||
|
# src/Pages
|
||||||
|
> where all your pages go
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
module Pages.SignIn exposing (Model, Msg, page)
|
||||||
|
|
||||||
|
import Element exposing (..)
|
||||||
|
import Element.Input as Input
|
||||||
|
import Generated.Params as Params
|
||||||
|
import Global
|
||||||
|
import Http
|
||||||
|
import Mi
|
||||||
|
import Spa.Page
|
||||||
|
import Utils.Spa exposing (Page)
|
||||||
|
|
||||||
|
|
||||||
|
page : Page Params.SignIn Model Msg model msg appMsg
|
||||||
|
page =
|
||||||
|
Spa.Page.component
|
||||||
|
{ title = always "Login"
|
||||||
|
, init = always init
|
||||||
|
, update = always update
|
||||||
|
, subscriptions = always subscriptions
|
||||||
|
, view = always view
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- INIT
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ token : String
|
||||||
|
, tokenData : Maybe Mi.TokenData
|
||||||
|
, error : Maybe String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init : Params.SignIn -> ( Model, Cmd Msg, Cmd Global.Msg )
|
||||||
|
init _ =
|
||||||
|
( { token = ""
|
||||||
|
, tokenData = Nothing
|
||||||
|
, error = Nothing
|
||||||
|
}
|
||||||
|
, Cmd.none
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- UPDATE
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= SignIn
|
||||||
|
| SignOut
|
||||||
|
| ValidateToken (Result Http.Error Mi.TokenData)
|
||||||
|
| UpdateToken String
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> ( Model, Cmd Msg, Cmd Global.Msg )
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
UpdateToken token ->
|
||||||
|
( { model | token = token }
|
||||||
|
, Cmd.none
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
ValidateToken result ->
|
||||||
|
case result of
|
||||||
|
Ok data ->
|
||||||
|
( { model | tokenData = Just data }
|
||||||
|
, Cmd.none
|
||||||
|
, Spa.Page.send (Global.SignIn model.token)
|
||||||
|
)
|
||||||
|
|
||||||
|
Err _ ->
|
||||||
|
( { model | token = "", error = Just "got an error :(" }
|
||||||
|
, Cmd.none
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
SignIn ->
|
||||||
|
( model
|
||||||
|
, Mi.request
|
||||||
|
"GET"
|
||||||
|
model.token
|
||||||
|
"/.within/tokeninfo"
|
||||||
|
Http.emptyBody
|
||||||
|
(Mi.expectJson ValidateToken Mi.tokenDecoder)
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
SignOut ->
|
||||||
|
( model
|
||||||
|
, Cmd.none
|
||||||
|
, Spa.Page.send Global.SignOut
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- SUBSCRIPTIONS
|
||||||
|
|
||||||
|
|
||||||
|
subscriptions : Model -> Sub Msg
|
||||||
|
subscriptions model =
|
||||||
|
Sub.none
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
view : Model -> Element Msg
|
||||||
|
view model =
|
||||||
|
let
|
||||||
|
errMsg =
|
||||||
|
case model.error of
|
||||||
|
Just msg ->
|
||||||
|
msg
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
""
|
||||||
|
|
||||||
|
tData =
|
||||||
|
case model.tokenData of
|
||||||
|
Just data ->
|
||||||
|
Element.paragraph []
|
||||||
|
[ text ("ID: " ++ data.jti)
|
||||||
|
, paragraph [] [ text ("Sub: " ++ data.sub) ]
|
||||||
|
]
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
Element.column []
|
||||||
|
[ Input.text []
|
||||||
|
{ label = Input.labelAbove [] (text "API Token")
|
||||||
|
, onChange = UpdateToken
|
||||||
|
, text = model.token
|
||||||
|
, placeholder = Just (Input.placeholder [] (text ""))
|
||||||
|
}
|
||||||
|
, Input.button [] { onPress = Just SignIn, label = text "Login" }
|
||||||
|
]
|
||||||
|
in
|
||||||
|
column [ spacing 16 ]
|
||||||
|
[ tData
|
||||||
|
, text errMsg
|
||||||
|
]
|
|
@ -0,0 +1,68 @@
|
||||||
|
module Pages.Switches exposing (Model, Msg, page)
|
||||||
|
|
||||||
|
import Spa.Page
|
||||||
|
import Element exposing (..)
|
||||||
|
import Generated.Params as Params
|
||||||
|
import Global
|
||||||
|
import Utils.Spa exposing (Page)
|
||||||
|
|
||||||
|
|
||||||
|
page : Page Params.Switches Model Msg model msg appMsg
|
||||||
|
page =
|
||||||
|
Spa.Page.component
|
||||||
|
{ title = always "Switches"
|
||||||
|
, init = always init
|
||||||
|
, update = always update
|
||||||
|
, subscriptions = always subscriptions
|
||||||
|
, view = always view
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- INIT
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
init : Params.Switches -> ( Model, Cmd Msg, Cmd Global.Msg )
|
||||||
|
init _ =
|
||||||
|
( {}
|
||||||
|
, Cmd.none
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- UPDATE
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= Msg
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> ( Model, Cmd Msg, Cmd Global.Msg )
|
||||||
|
update msg model =
|
||||||
|
( model
|
||||||
|
, Cmd.none
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- SUBSCRIPTIONS
|
||||||
|
|
||||||
|
|
||||||
|
subscriptions : Model -> Sub Msg
|
||||||
|
subscriptions model =
|
||||||
|
Sub.none
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
view : Model -> Element Msg
|
||||||
|
view model =
|
||||||
|
text "Switches"
|
|
@ -0,0 +1,59 @@
|
||||||
|
module Pages.Top exposing (Model, Msg, page)
|
||||||
|
|
||||||
|
import Element exposing (..)
|
||||||
|
import Element.Font as Font
|
||||||
|
import Generated.Params as Params
|
||||||
|
import Generated.Routes as Routes exposing (Route, routes)
|
||||||
|
import Spa.Page
|
||||||
|
import Utils.Spa exposing (Page)
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
()
|
||||||
|
|
||||||
|
|
||||||
|
type alias Msg =
|
||||||
|
Never
|
||||||
|
|
||||||
|
|
||||||
|
page : Page Params.Top Model Msg model msg appMsg
|
||||||
|
page =
|
||||||
|
Spa.Page.static
|
||||||
|
{ title = always "/"
|
||||||
|
, view = view
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
view : Utils.Spa.PageContext -> Element Msg
|
||||||
|
view { global } =
|
||||||
|
case global.token of
|
||||||
|
Just _ ->
|
||||||
|
column
|
||||||
|
[ spacing 12
|
||||||
|
]
|
||||||
|
[ row [ spacing 14 ]
|
||||||
|
[ el [ Font.size 48, Font.semiBold ] (text "Mi")
|
||||||
|
, el [ alpha 0.5 ] (text "POSSE and stuff")
|
||||||
|
]
|
||||||
|
, text "TODO:"
|
||||||
|
, text "* POSSE"
|
||||||
|
, text "* Switch Data"
|
||||||
|
]
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
el
|
||||||
|
[ Font.size 48, centerX, centerY ]
|
||||||
|
(link
|
||||||
|
[ Font.underline
|
||||||
|
, Font.color (rgb255 204 75 75)
|
||||||
|
, Font.size 48
|
||||||
|
, mouseOver [ alpha 0.5 ]
|
||||||
|
]
|
||||||
|
{ label = text "Login"
|
||||||
|
, url = Routes.toPath routes.signIn
|
||||||
|
}
|
||||||
|
)
|
|
@ -0,0 +1,14 @@
|
||||||
|
port module Ports exposing (log)
|
||||||
|
|
||||||
|
import Json.Encode as Json
|
||||||
|
|
||||||
|
|
||||||
|
port outgoing : { action : String, data : Json.Value } -> Cmd msg
|
||||||
|
|
||||||
|
|
||||||
|
log : String -> Cmd msg
|
||||||
|
log message =
|
||||||
|
outgoing
|
||||||
|
{ action = "LOG"
|
||||||
|
, data = Json.string message
|
||||||
|
}
|
|
@ -1,64 +0,0 @@
|
||||||
module SwitchData exposing
|
|
||||||
( Switch
|
|
||||||
, decoder
|
|
||||||
, frontURL
|
|
||||||
, idURL
|
|
||||||
, listURL
|
|
||||||
, switchURL
|
|
||||||
)
|
|
||||||
|
|
||||||
import Html exposing (..)
|
|
||||||
import Html.Attributes exposing (..)
|
|
||||||
import Iso8601
|
|
||||||
import Json.Decode exposing (Decoder, field, int, map5, nullable, string)
|
|
||||||
import Time exposing (Posix)
|
|
||||||
import Url.Builder as UB
|
|
||||||
|
|
||||||
|
|
||||||
type alias Switch =
|
|
||||||
{ id : String
|
|
||||||
, who : String
|
|
||||||
, started_at : Posix
|
|
||||||
, ended_at : Maybe Posix
|
|
||||||
, duration : Int
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
decoder : Decoder Switch
|
|
||||||
decoder =
|
|
||||||
map5 Switch
|
|
||||||
(field "id" string)
|
|
||||||
(field "who" string)
|
|
||||||
(field "started_at" Iso8601.decoder)
|
|
||||||
(field "ended_at" (nullable Iso8601.decoder))
|
|
||||||
(field "duration" int)
|
|
||||||
|
|
||||||
|
|
||||||
switchURL : String
|
|
||||||
switchURL =
|
|
||||||
UB.absolute
|
|
||||||
[ "switches", "switch" ]
|
|
||||||
[]
|
|
||||||
|
|
||||||
|
|
||||||
idURL : String -> String
|
|
||||||
idURL id =
|
|
||||||
UB.absolute
|
|
||||||
[ "switches", "id", id ]
|
|
||||||
[]
|
|
||||||
|
|
||||||
|
|
||||||
frontURL : String
|
|
||||||
frontURL =
|
|
||||||
UB.absolute
|
|
||||||
[ "switches", "current" ]
|
|
||||||
[]
|
|
||||||
|
|
||||||
|
|
||||||
listURL : Int -> Int -> String
|
|
||||||
listURL limit page =
|
|
||||||
UB.absolute
|
|
||||||
[ "switches", "" ]
|
|
||||||
[ UB.int "limit" limit
|
|
||||||
, UB.int "page" page
|
|
||||||
]
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
module Transitions exposing (transitions)
|
||||||
|
|
||||||
|
import Spa.Transition as Transition
|
||||||
|
import Utils.Spa as Spa
|
||||||
|
|
||||||
|
|
||||||
|
transitions : Spa.Transitions msg
|
||||||
|
transitions =
|
||||||
|
{ layout = Transition.fadeElmUi 500
|
||||||
|
, page = Transition.fadeElmUi 300
|
||||||
|
, pages = []
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
# src/Utils
|
||||||
|
> a place for helper functions
|
|
@ -0,0 +1,72 @@
|
||||||
|
module Utils.Spa exposing
|
||||||
|
( Bundle
|
||||||
|
, Init
|
||||||
|
, LayoutContext
|
||||||
|
, Page
|
||||||
|
, PageContext
|
||||||
|
, Recipe
|
||||||
|
, Transitions
|
||||||
|
, Update
|
||||||
|
, layout
|
||||||
|
, recipe
|
||||||
|
)
|
||||||
|
|
||||||
|
import Element exposing (Element)
|
||||||
|
import Generated.Routes as Routes exposing (Route)
|
||||||
|
import Global
|
||||||
|
import Spa.Page
|
||||||
|
import Spa.Types
|
||||||
|
|
||||||
|
|
||||||
|
type alias Page params model msg layoutModel layoutMsg appMsg =
|
||||||
|
Spa.Types.Page Route params model msg (Element msg) layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg)
|
||||||
|
|
||||||
|
|
||||||
|
type alias Recipe params model msg layoutModel layoutMsg appMsg =
|
||||||
|
Spa.Types.Recipe Route params model msg layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg)
|
||||||
|
|
||||||
|
|
||||||
|
type alias Init model msg =
|
||||||
|
Spa.Types.Init Route model msg Global.Model Global.Msg
|
||||||
|
|
||||||
|
|
||||||
|
type alias Update model msg =
|
||||||
|
Spa.Types.Update Route model msg Global.Model Global.Msg
|
||||||
|
|
||||||
|
|
||||||
|
type alias Bundle msg appMsg =
|
||||||
|
Spa.Types.Bundle Route msg (Element msg) Global.Model Global.Msg appMsg (Element appMsg)
|
||||||
|
|
||||||
|
|
||||||
|
type alias LayoutContext msg =
|
||||||
|
Spa.Types.LayoutContext Route msg (Element msg) Global.Model Global.Msg
|
||||||
|
|
||||||
|
|
||||||
|
type alias PageContext =
|
||||||
|
Spa.Types.PageContext Route Global.Model
|
||||||
|
|
||||||
|
|
||||||
|
type alias Layout params model msg appMsg =
|
||||||
|
Spa.Types.Layout Route params model msg (Element msg) Global.Model Global.Msg appMsg (Element appMsg)
|
||||||
|
|
||||||
|
|
||||||
|
layout :
|
||||||
|
Layout params model msg appMsg
|
||||||
|
-> Page params model msg layoutModel layoutMsg appMsg
|
||||||
|
layout =
|
||||||
|
Spa.Page.layout Element.map
|
||||||
|
|
||||||
|
|
||||||
|
type alias Upgrade params model msg layoutModel layoutMsg appMsg =
|
||||||
|
Spa.Types.Upgrade Route params model msg (Element msg) layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg)
|
||||||
|
|
||||||
|
|
||||||
|
recipe :
|
||||||
|
Upgrade params model msg layoutModel layoutMsg appMsg
|
||||||
|
-> Recipe params model msg layoutModel layoutMsg appMsg
|
||||||
|
recipe =
|
||||||
|
Spa.Page.recipe Element.map
|
||||||
|
|
||||||
|
|
||||||
|
type alias Transitions msg =
|
||||||
|
Spa.Types.Transitions (Element msg)
|
|
@ -1,78 +1,5 @@
|
||||||
main {
|
/* you can include CSS here */
|
||||||
font-family: monospace, monospace;
|
html, body {
|
||||||
max-width: 38rem;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
padding: 2rem;
|
height: 100%;
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-device-width: 736px) {
|
|
||||||
main {
|
|
||||||
padding: 0rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::selection {
|
|
||||||
background: #d3869b;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: #282828;
|
|
||||||
color: #ebdbb2;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
background-color: #3c3836;
|
|
||||||
padding: 1em;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, a:active, a:visited {
|
|
||||||
color: #b16286;
|
|
||||||
background-color: #1d2021;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5 {
|
|
||||||
margin-bottom: .1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
border-left: 1px solid #bdae93;
|
|
||||||
margin: 0.5em 10px;
|
|
||||||
padding: 0.5em 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
|
||||||
body {
|
|
||||||
background: #fbf1c7;
|
|
||||||
color: #3c3836;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
background-color: #ebdbb2;
|
|
||||||
padding: 1em;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, a:active, a:visited {
|
|
||||||
color: #b16286;
|
|
||||||
background-color: #f9f5d7;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5 {
|
|
||||||
margin-bottom: .1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
border-left: 1px solid #655c54;
|
|
||||||
margin: 0.5em 10px;
|
|
||||||
padding: 0.5em 10px;
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue