[{"name":"Node","comment":" A state-interface program that can run in [node.js](https://nodejs.org/en)\nwhich is commonly used to create command line tools, scripts and servers.\n\nI recommend starting with [the minimal setup](https://github.com/lue-bird/elm-state-interface-node-hello),\nbrowsing [the examples](https://github.com/lue-bird/elm-state-interface-experimental/tree/main/example)\nand picking parts you want to experiment with.\n\n@docs program, Program\n\nYou can also [embed](#embed) a state-interface program as part of an existing app that uses The Elm Architecture.\n\n\n# interface\n\n@docs Interface, interfaceBatch, interfaceNone, interfaceFutureMap\n\n\n## time\n\nSee [`elm/time`](https://dark.elm.dmy.fr/packages/elm/time/)\n\n@docs timePosixRequest, timeZoneRequest, timeZoneNameRequest\n@docs timePeriodicallyListen, timeOnceAt\n\n\n## process and terminal\n\n@docs workingDirectoryPathRequest, launchArgumentsRequest, processTitleSet, exit\n@docs standardOutWrite, standardErrWrite, standardInListen, standardInRawListen\n@docs terminalSizeRequest, terminalSizeChangeListen\n\n\n## file system\n\n@docs FileKind, fileInfoRequest, directorySubPathsRequest\n@docs directoryMake, fileWrite, fileRequest, fileRemove\n@docs fileChangeListen, FileChange\n\n\n## HTTP\n\n\n### client\n\n@docs httpRequestSend, HttpError\n\n\n### server\n\n@docs httpRequestListenAndMaybeRespond, HttpServerEvent\n\n\n## random\n\nNot familiar with random \"generators\"? [`elm/random`](https://package.elm-lang.org/packages/elm/random/latest)\nexplains it nicely!\n\n@docs randomUnsignedInt32sRequest\n\n\n## embed\n\nIf you just want to replace a part of your elm app with this architecture. Make sure to wire in all 3:\n\n@docs ProgramConfig, programInit, programUpdate, programSubscriptions\n\nUnder the hood, [`Node.program`](Node#program) is then defined as just\n\n    program config =\n        Platform.worker\n            { init = \\() -> Node.programInit yourAppConfig\n            , update = Node.programUpdate yourAppConfig\n            , subscriptions = Node.programSubscriptions yourAppConfig\n            }\n\n\n## internals, safe to ignore for users\n\nExposed so can for example simulate it more easily in tests, add a debugger etc.\n\n@docs ProgramState, ProgramEvent, InterfaceSingle\n\nIf you need more things like json encoders/decoders, [open an issue](https://github.com/lue-bird/elm-state-interface-experimental/issues/new)\n\n","unions":[{"name":"FileChange","comment":" Did the file at a path get changed/created or moved away/removed?\n","args":[],"cases":[["FileAddedOrChanged",["String.String"]],["FileRemoved",["String.String"]]]},{"name":"FileKind","comment":" Does the path point to a directory or \"content\"-file\n","args":[],"cases":[["KindDirectory",[]],["KindFile",[]]]},{"name":"HttpError","comment":" A Request can fail in a couple ways:\n\n  - `BadUrl`: you did not provide a valid URL\n  - `NetworkError`: the user turned off their wifi, went in a cave, etc.\n    or the server CORS is misconfigured.\n    Note: A 404 for example does not constitute a network error\n  - `BadStatus`: you got a response back, but the status code indicates failure. Contains:\n      - the statusCode like 404 and statusText describing what the statusCode means a little\n      - headers like Content-Length and Expires\n      - the body as [`Bytes`](https://dark.elm.dmy.fr/packages/elm/bytes/latest/)\n\n","args":[],"cases":[["HttpBadUrl",[]],["HttpNetworkError",["String.String"]],["HttpBadStatus",["{ statusCode : Basics.Int, statusText : String.String, headers : List.List { name : String.String, value : String.String }, body : Bytes.Bytes }"]]]},{"name":"HttpServerEvent","comment":" How the connection has changed or what request has been sent.\n","args":[],"cases":[["HttpServerOpened",[]],["HttpRequestReceived",["{ method : String.String, headers : List.List { name : String.String, value : String.String }, data : Bytes.Bytes }"]],["HttpResponseSent",[]],["HttpServerFailed",["{ code : String.String, message : String.String }"]]]},{"name":"InterfaceSingle","comment":" A \"non-batched\" [`Interface`](#Interface).\nTo create one, use the helpers in [time](#time), [HTTP](#http) etc.\n","args":["future"],"cases":[["WorkingDirectoryPathRequest",["String.String -> future"]],["LaunchArgumentsRequest",["List.List String.String -> future"]],["ProcessTitleSet",["String.String"]],["StandardOutWrite",["String.String"]],["StandardErrWrite",["String.String"]],["StandardInListen",["String.String -> future"]],["StandardInRawListen",["String.String -> future"]],["TerminalSizeRequest",["{ lines : Basics.Int, columns : Basics.Int } -> future"]],["TerminalSizeChangeListen",["{ lines : Basics.Int, columns : Basics.Int } -> future"]],["HttpRequestSend",["{ url : String.String, method : String.String, headers : List.List { name : String.String, value : String.String }, bodyUnsignedInt8s : Maybe.Maybe (List.List Basics.Int), on : Result.Result Node.HttpError Bytes.Bytes -> future }"]],["HttpRequestListen",["{ portNumber : Basics.Int, on : Node.HttpServerEvent -> future }"]],["HttpResponseSend",["{ portNumber : Basics.Int, statusCode : Basics.Int, headers : List.List { name : String.String, value : String.String }, dataUnsignedInt8s : List.List Basics.Int }"]],["TimePosixRequest",["Time.Posix -> future"]],["TimezoneOffsetRequest",["Basics.Int -> future"]],["TimeOnce",["{ pointInTime : Time.Posix, on : Time.Posix -> future }"]],["TimePeriodicallyListen",["{ intervalDurationMilliSeconds : Basics.Int, on : Time.Posix -> future }"]],["TimezoneNameRequest",["String.String -> future"]],["RandomUnsignedInt32sRequest",["{ count : Basics.Int, on : List.List Basics.Int -> future }"]],["Exit",["Basics.Int"]],["DirectoryMake",["{ path : String.String, on : Result.Result { code : String.String, message : String.String } () -> future }"]],["FileRemove",["String.String"]],["FileWrite",["{ contentUnsignedInt8s : List.List Basics.Int, path : String.String, on : Result.Result { code : String.String, message : String.String } () -> future }"]],["FileRequest",["{ path : String.String, on : Result.Result { code : String.String, message : String.String } Bytes.Bytes -> future }"]],["FileChangeListen",["{ path : String.String, on : Node.FileChange -> future }"]],["FileInfoRequest",["{ path : String.String, on : Maybe.Maybe { byteCount : Basics.Int, lastContentChangeTime : Time.Posix, kind : Node.FileKind } -> future }"]],["DirectorySubPathsRequest",["{ path : String.String, on : Result.Result { code : String.String, message : String.String } (List.List String.String) -> future }"]]]},{"name":"ProgramEvent","comment":" The \"msg\" in a [`Node.program`](#program)\n","args":["appState"],"cases":[["JsEventFailedToDecode",["Json.Decode.Error"]],["JsEventEnabledConstructionOfNewAppState",["appState"]]]},{"name":"ProgramState","comment":" The \"model\" in a [`Node.program`](#program)\n","args":["appState"],"cases":[["State",["{ interface : Node.Interface appState, appState : appState }"]]]}],"aliases":[{"name":"Interface","comment":" Incoming and outgoing effects.\nTo create one, use the helpers in [time](#time), [HTTP](#http) etc.\n\nTo combine multiple, use [`Node.interfaceBatch`](#interfaceBatch) and [`Node.interfaceNone`](#interfaceNone).\nTo change the value that comes back in the future, use [`Node.interfaceFutureMap`](Node#interfaceFutureMap)\n\n","args":["future"],"type":"FastDict.Dict String.String (Node.InterfaceSingle future)"},{"name":"Program","comment":" A [`Platform.Program`](https://dark.elm.dmy.fr/packages/elm/core/latest/Platform#Program)\nthat elm can run,\nproduced by [`Node.program`](#program)\n","args":["state"],"type":"Platform.Program () (Node.ProgramState state) (Node.ProgramEvent state)"},{"name":"ProgramConfig","comment":" What's needed to create a state-interface [`program`](#program)\n","args":["state"],"type":"RecordWithoutConstructorFunction.RecordWithoutConstructorFunction { initialState : state, interface : state -> Node.Interface state, ports : { toJs : Json.Encode.Value -> Platform.Cmd.Cmd (Node.ProgramEvent state), fromJs : (Json.Encode.Value -> Node.ProgramEvent state) -> Platform.Sub.Sub (Node.ProgramEvent state) } }"}],"values":[{"name":"directoryMake","comment":" An [`Interface`](Node#Interface) for creating a directory including its parent directories\nat a given path unless it already exists.\n\nUses [`fs.mkdir({ recursive : true })`](https://nodejs.org/api/fs.html#fspromisesmkdirpath-options)\n\n","type":"String.String -> Node.Interface (Result.Result { code : String.String, message : String.String } ())"},{"name":"directorySubPathsRequest","comment":" An [`Interface`](Node#Interface) for getting relative paths of the files\ncontained in the directory at the given path (and any contained directory).\n\n    directoryFullSubPathsRequest : String -> Node.Interface (List String)\n    directoryFullSubPathsRequest directoryPath =\n        Node.directorySubPathsRequest directoryPath\n            |> Node.interfaceFutureRequest\n                (\\subNamesOrError ->\n                    subNamesOrError\n                        |> Result.withDefault []\n                        |> List.map (\\subName -> path ++ \"/\" ++ subName)\n                )\n\nUses [`fs.readdir({ recursive: true })`](https://nodejs.org/api/fs.html#fsreaddirpath-options-callback)\n\n","type":"String.String -> Node.Interface (Result.Result { code : String.String, message : String.String } (List.List String.String))"},{"name":"exit","comment":" Very gracefully stop the process,\ncompleting all other current interface operations.\nDo not keep listens in the interface,\notherwise the process will not die.\n\nKeep in mind that every process ends automatically\nonce no interfaces that expect future data are left\nwhich means you only need [`Node.exit`](#exit) in case you want to exit\nwith a non-zero error code.\n\nUses [`process.exitCode = ...`](https://nodejs.org/api/process.html#processexitcode_1)\ninstead of [`process.exit`](https://nodejs.org/api/process.html#processexitcode)\nto still make interfaces like writing an error come through before exiting.\n\n","type":"Basics.Int -> Node.Interface future_"},{"name":"fileChangeListen","comment":" An [`Interface`](Node#Interface) for detecting changes to\nthe file at a given path or any of its deepest sub-files.\n\nUses [`fs.watch({ recursive: true })`](https://nodejs.org/api/fs.html#fswatchfilename-options-listener)\n\n","type":"String.String -> Node.Interface Node.FileChange"},{"name":"fileInfoRequest","comment":" An [`Interface`](Node#Interface) for getting file metadata at a given path\nor `Nothing` if no file exists there.\n\n    fileExistsRequest : String -> Node.Interface Bool\n    fileExistsRequest path =\n        Node.fileInfoRequest path\n            |> Node.interfaceFutureRequest (\\info -> info /= Nothing)\n\nUses [`fs.stat`](https://nodejs.org/api/fs.html#fspromisesstatpath-options)\n\n","type":"String.String -> Node.Interface (Maybe.Maybe { byteCount : Basics.Int, lastContentChangeTime : Time.Posix, kind : Node.FileKind })"},{"name":"fileRemove","comment":" An [`Interface`](Node#Interface) for unlinking a file\nat a given path.\n\nUses [`fs.unlink`](https://nodejs.org/api/fs.html#fsunlinkpath-callback)\n\n","type":"String.String -> Node.Interface future_"},{"name":"fileRequest","comment":" An [`Interface`](Node#Interface) for reading the [`Bytes`](https://dark.elm.dmy.fr/packages/elm/bytes/latest/Bytes#Bytes) content\nof the file at a given path.\n\nUses [`fs.readFile`](https://nodejs.org/api/fs.html#fsreadfilepath-options-callback)\n\n","type":"String.String -> Node.Interface (Result.Result { code : String.String, message : String.String } Bytes.Bytes)"},{"name":"fileWrite","comment":" An [`Interface`](Node#Interface) for creating or overwriting a file\nat a given path with given [`Bytes`](https://dark.elm.dmy.fr/packages/elm/bytes/latest/Bytes#Bytes) content.\n\nUses [`fs.writeFile`](https://nodejs.org/api/fs.html#fswritefilefile-data-options-callback)\n\n","type":"{ path : String.String, content : Bytes.Bytes } -> Node.Interface (Result.Result { code : String.String, message : String.String } ())"},{"name":"httpRequestListenAndMaybeRespond","comment":" An [`Interface`](Web#Interface) for detecting when data has been sent from the server at a given address\nor the connection has changed, see [`Web.SocketEvent`](Web#SocketEvent).\n\nYou also have the ability to send data to the server whenever necessary.\n\n    Node.httpRequestListenAndMaybeRespond\n        { portNumber = 1248\n        , response =\n            case state.pendingRequest of\n                Nothing ->\n                    Nothing\n\n                Just pendingRequest ->\n                    case pendingRequest.data |> Bytes.Decode.decode Bytes.Decode.signedInt32 Bytes.BE of\n                        Nothing ->\n                            Just\n                                { statusCode = 404\n                                , headers = []\n                                , data = Bytes.Encode.sequence [] |> Bytes.Encode.encode\n                                }\n\n                        Just requestNumber ->\n                            Just\n                                { statusCode = 200\n                                , headers = []\n                                , data =\n                                    (requestNumber * 2)\n                                        |> Bytes.Encode.signedInt32 Bytes.BE\n                                        |> Bytes.Encode.encode\n                                }\n        }\n        |> Node.interfaceFutureMap\n            (\\socketEvent ->\n                case socketEvent of\n                    Node.HttpRequestReceived pendingRequest ->\n                        { state | pendingRequest = Just pendingRequest }\n\n                    Node.HttpResponseSent ->\n                        { state | pendingRequest = Nothing }\n\n                    Node.HttpServerOpened ->\n                        state\n\n                    Node.HttpServerClosed ->\n                        state\n            )\n\n\"Removing\" [`Node.httpRequestListenAndMaybeRespond`](#httpRequestListenAndMaybeRespond)\nfrom your interface will close the server at that port.\n\nIt's common to send encoded data with [`Json.Encode.encode 0`](https://dark.elm.dmy.fr/packages/elm/json/latest/Json-Encode#encode)\n(usually with the header `{ name = \"Content-Type\", value = \"application/json\" }`).\n\n","type":"{ portNumber : Basics.Int, response : Maybe.Maybe { statusCode : Basics.Int, headers : List.List { name : String.String, value : String.String }, data : Bytes.Bytes } } -> Node.Interface Node.HttpServerEvent"},{"name":"httpRequestSend","comment":" An [`Interface`](Web#Interface) for sending an HTTP request\nto a given url\n\n    exampleHttpPost : Web.Interface (Result Web.HttpError Bytes)\n    exampleHttpPost =\n        Web.httpRequestSend\n            { url = ...\n            , method = \"POST\"\n            , headers = [ { name = \"Content-Type\", value = \"application/json\" } ]\n            , body =\n                96\n                    |> Json.Encode.int\n                    |> Json.Encode.encode 0\n                    |> Bytes.Encode.encode\n                    |> Just\n            }\n\n    exampleHttpGet : Web.HttpRequest\n    exampleHttpGet =\n        Web.httpRequestSend\n            { url = ...\n            , method = \"GET\"\n            , headers = [ { name = \"X-Custom-Header\", value = \"ProcessThisImmediately\" } ]\n            , body = Nothing -- empty bytes with HEAD or GET can lead to an HttpError\n            }\n            |> Web.interfaceFutureMap\n                (\\responseOrError ->\n                    case responseOrError of\n                        Ok bytes ->\n                            case bytes |> Bytes.Decode.decode (Bytes.Decode.string (bytes |> Bytes.width)) of\n                                Just jsonString ->\n                                    case jsonString |> Json.Decode.decodeString ... of\n                                        Err jsonError ->\n                                            Err (jsonError |> Json.Decode.errorToString)\n\n                                        Ok parsed ->\n                                            Ok parsed\n\n                                Nothing ->\n                                    Err \"failed to decode bytes to a string\"\n\n                        Err _ ->\n                            Err \"HTTP response error\"\n                )\n\nUsually, if you have a request body,\nthe headers include `Content-Type` with a [MIME type](https://en.wikipedia.org/wiki/Media_type)\n\nThe response will either contain [`Bytes`](https://dark.elm.dmy.fr/packages/elm/bytes/latest/).\nor an [`HttpError`](Web#HttpError)\n\nIf you for some reason need a timeout,\nadd a [`Web.timeOnceAt`](Web#timeOnceAt) and remove the [`Web.httpRequestSend`](#httpRequestSend)\nonce it has passed.\n\nUses the [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)\n\n","type":"{ url : String.String, method : String.String, headers : List.List { name : String.String, value : String.String }, body : Maybe.Maybe Bytes.Bytes } -> Node.Interface (Result.Result Node.HttpError Bytes.Bytes)"},{"name":"interfaceBatch","comment":" Combine multiple [`Interface`](#Interface)s into one\n","type":"List.List (Node.Interface future) -> Node.Interface future"},{"name":"interfaceFutureMap","comment":" Take what the [`Interface`](#Interface) can come back with and return a different future value.\n\nIn practice, this is sometimes used like a kind of event-config pattern:\n\n    Node.timePosixRequest\n        |> Node.interfaceFutureMap (\\timeNow -> TimeReceived timeNow)\n\nsometimes as a way to deal with all events (like `update` in The Elm Architecture)\n\n    ...\n        |> Node.interfaceFutureMap\n            (\\event ->\n                case event of\n                    CursorMovedTo newCursorPoint ->\n                        { state | cursorPoint = newCursorPoint }\n\n                    MinusEntered ->\n                        { state | counter = state.counter - 1 }\n\n                    PlusEntered ->\n                        { state | counter = state.counter + 1 }\n            )\n\nIn all these examples, you end up converting the narrow future representation of part of the interface\nto a broader representation for the parent interface\n\n","type":"(future -> mappedFuture) -> Node.Interface future -> Node.Interface mappedFuture"},{"name":"interfaceNone","comment":" Doing nothing as an [`Interface`](#Interface). These two examples are equivalent:\n\n    Node.interfaceBatch [ a, Node.interfaceNone, b ]\n\nand\n\n    Node.interfaceBatch\n        (List.filterMap identity\n            [ a |> Just, Nothing, b |> Just ]\n        )\n\n","type":"Node.Interface future_"},{"name":"launchArgumentsRequest","comment":" An [`Interface`](Node#Interface) for getting\nthe command-line arguments passed when the Node.js process was launched.\n\nFor example, running\n\n    node process-args.js one two=three four\n\n[`Node.launchArgumentsRequest`](#launchArgumentsRequest)\nwould give back\n\n    [ \"/usr/local/bin/node\"\n    , \"/Users/mjr/work/node/process-args.js\"\n    , \"one\"\n    , \"two=three\"\n    , \"four\"\n    ]\n\nThe first is always the absolute path to the executable that started the Node.js process,\nthe second is always the path to the executed js file.\n\nUses [`process.argv`](https://nodejs.org/api/process.html#processargv)\n\n","type":"Node.Interface (List.List String.String)"},{"name":"processTitleSet","comment":" Replace the title of the running process.\nThis will usually display in the title bar of your terminal emulator or in activity monitors.\n\nUses [`process.title = ...`](https://nodejs.org/api/process.html#processtitle)\n\n","type":"String.String -> Node.Interface future_"},{"name":"program","comment":" Create a [`Program`](#Program):\n\n  - The state is everything the program knows (what The Elm Architecture calls model).\n    And it always starts with a given `initialState`\n\n  - The [`Interface`](#Interface) is the face to the outside world\n    and can be created using the helpers in [time](#time), [HTTP](#http) etc.\n    The given `interface` function constructs these interfaces based on the current state\n\n  - Connections to and from js\n\n        port toJs : Json.Encode.Value -> Cmd event_\n\n        port fromJs : (Json.Encode.Value -> event) -> Sub event\n\n","type":"{ initialState : state, interface : state -> Node.Interface state, ports : { toJs : Json.Encode.Value -> Platform.Cmd.Cmd (Node.ProgramEvent state), fromJs : (Json.Encode.Value -> Node.ProgramEvent state) -> Platform.Sub.Sub (Node.ProgramEvent state) } } -> Node.Program state"},{"name":"programInit","comment":" The \"init\" part for an embedded program\n","type":"Node.ProgramConfig state -> ( Node.ProgramState state, Platform.Cmd.Cmd (Node.ProgramEvent state) )"},{"name":"programSubscriptions","comment":" The \"subscriptions\" part for an embedded program\n","type":"Node.ProgramConfig state -> Node.ProgramState state -> Platform.Sub.Sub (Node.ProgramEvent state)"},{"name":"programUpdate","comment":" The \"update\" part for an embedded program\n","type":"Node.ProgramConfig state -> Node.ProgramEvent state -> Node.ProgramState state -> ( Node.ProgramState state, Platform.Cmd.Cmd (Node.ProgramEvent state) )"},{"name":"randomUnsignedInt32sRequest","comment":" An [`Interface`](Node#Interface) for generating a given count of cryptographically sound unsigned 32-bit `Int`s.\nYou can use these in all kinds of packages that allow creating an initial seed\nfrom ints like [NoRedInk/elm-random-pcg-extended](https://dark.elm.dmy.fr/packages/NoRedInk/elm-random-pcg-extended/latest/Random-Pcg-Extended#initialSeed)\n\nNote: uses [`crypto.getRandomValues`](https://nodejs.org/api/crypto.html#cryptogetrandomvaluestypedarray)\n\n","type":"Basics.Int -> Node.Interface (List.List Basics.Int)"},{"name":"standardErrWrite","comment":" An [`Interface`](Node#Interface) for writing text to standard err.\n\n> input file does not exits. Is the path correct?\n\nUses [`process.stderr.write`](https://nodejs.org/api/stream.html#writablewritechunk-encoding-callback)\n\n","type":"String.String -> Node.Interface future_"},{"name":"standardInListen","comment":" Read text from standard in.\nIf you want to intercept input before enter is sent,\nuse [`Node.standardInRawListen`](#standardInRawListen)\n\nUses [`process.stdin.addListener(\"data\", ...)`](https://nodejs.org/api/stream.html#event-data)\n\n","type":"Node.Interface String.String"},{"name":"standardInRawListen","comment":" Read text from standard in character by character (except modifiers).\nFor nice input parsing,\nsee for example [gren-tui's `stringToInput`](https://github.com/blaix/gren-tui/blob/main/src/Tui.gren#L562).\n\nAs long as [`Node.standardInRawListen`](#standardInRawListen) is part of the interface,\nthe terminal won't echo input characters.\nHowever, ctrl+c will still exit the process (SIGINT).\n\nUses [`process.stdin.addListener(\"data\", ...)`](https://nodejs.org/api/stream.html#event-data)\nin combination with [`process.stdin.setRawMode(true)`](https://nodejs.org/api/tty.html#readstreamsetrawmodemode)\n\n","type":"Node.Interface String.String"},{"name":"standardOutWrite","comment":" An [`Interface`](Node#Interface) for writing text to standard out, like\n\n> hit enter to apply changes or escape to cancel:\n\nIf you want to clear the screen, apply color or font effects,\nemploy [ansi codes](https://dark.elm.dmy.fr/packages/wolfadex/elm-ansi/latest/) instead\n(either use a library or go click on the declarations and copy source)\n\nIf you find that the last line gets eaten, append a `\\n`\n\nUses [`process.stdout.write`](https://nodejs.org/api/stream.html#writablewritechunk-encoding-callback)\n\n","type":"String.String -> Node.Interface future_"},{"name":"terminalSizeChangeListen","comment":" An [`Interface`](Node#Interface) for detecting changes to the inner window width and height\n","type":"Node.Interface { lines : Basics.Int, columns : Basics.Int }"},{"name":"terminalSizeRequest","comment":" An [`Interface`](Node#Interface) for getting the inner terminal window width and height in characters,\n","type":"Node.Interface { lines : Basics.Int, columns : Basics.Int }"},{"name":"timeOnceAt","comment":" An [`Interface`](Node#Interface) for getting a reminder\nonce a given [point in time](https://dark.elm.dmy.fr/packages/elm/time/latest/Time#Posix) has been reached.\n\nThis lets you for example wait until it's 15 minutes before the event,\ntimeout a request or schedule a certain action to a specific time.\n\n    import Node\n\n    type RequestState result\n        = NotAsked\n        | BeforeTimeout { start : Time.Posix }\n        | TimedOut\n        | GotResult result\n\n    { initialState = NotAsked\n    , interface =\n        \\state ->\n            [ case state of\n                NotAsked ->\n                    [ Node.timePosixRequest\n                        |> Node.interfaceFutureMap BeforeTimeout\n                    , ..request.. |> Node.futureMap GotResult\n                    ]\n\n                BeforeTimeout requestTime ->\n                    -- timeout after 10 seconds\n                    Node.timeOnceAt (Duration.addTo requestTime (Duration.seconds 10))\n                        |> Node.interfaceFutureMap (\\_ -> TimedOut)\n\n                TimedOut ->\n                    ...\n\n                GotResult result ->\n                    ...\n            ]\n    }\n\n  - 🧩 [`Duration` is from ianmackenzie/elm-units](https://dark.elm.dmy.fr/packages/ianmackenzie/elm-units/latest/Duration)\n\nYou can abstract this in various ways like adding a\n\n    withTimeout :\n        ..Request result..\n        -> Node.Interface (RequestState result)\n\nwhere the result can be put into the \"main state\" and therefore cased on.\n\n","type":"Time.Posix -> Node.Interface Time.Posix"},{"name":"timePeriodicallyListen","comment":" An [`Interface`](Node#Interface) for getting the current time\nevery time a given [`Duration`](https://dark.elm.dmy.fr/packages/ianmackenzie/elm-units/latest/Duration) has passed.\n","type":"Duration.Duration -> Node.Interface Time.Posix"},{"name":"timePosixRequest","comment":" An [`Interface`](Node#Interface) for getting the current [POSIX time](https://dark.elm.dmy.fr/packages/elm/time/latest/Time#Posix).\n\nReplacement for [`elm/time`'s `Time.now`](https://dark.elm.dmy.fr/packages/elm/time/latest/Time#now).\n\n","type":"Node.Interface Time.Posix"},{"name":"timeZoneNameRequest","comment":" Intended for package authors.\nAn [`Interface`](Node#Interface) for using [`Intl.DateTimeFormat().resolvedOptions().timeZone`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/resolvedOptions#timezone)\nto get names like `Europe/Moscow` or `America/Havana`.\nFrom there you can look it up in any [IANA data](https://www.iana.org/time-zones) you loaded yourself.\n\nReplacement for [`elm/time`'s `Time.getZoneName`](https://package.elm-lang.org/packages/elm/time/latest/Time#getZoneName).\n\n","type":"Node.Interface String.String"},{"name":"timeZoneRequest","comment":" An [`Interface`](Node#Interface) for getting a [`Time.Zone`](https://dark.elm.dmy.fr/packages/elm/time/latest/Time#Zone)\nbased on the current UTC offset.\n\nReplacement for [`elm/time`'s `Time.here`](https://dark.elm.dmy.fr/packages/elm/time/latest/Time#here).\n\n","type":"Node.Interface Time.Zone"},{"name":"workingDirectoryPathRequest","comment":" An [`Interface`](Node#Interface) for getting the current working directory.\n\nUses [`process.cwd`](https://nodejs.org/api/process.html#processcwd)\n\n","type":"Node.Interface String.String"}],"binops":[]},{"name":"Web","comment":" A state-interface program that can run in the browser\n\n@docs program, Program\n\nYou can also [embed](#embed) a state-interface program as part of an existing app that uses The Elm Architecture.\n\n\n# interface\n\n@docs Interface, interfaceBatch, interfaceNone, interfaceFutureMap\n\n\n## time\n\nSee [`elm/time`](https://dark.elm.dmy.fr/packages/elm/time/)\n\n@docs timePosixRequest, timeZoneRequest, timeZoneNameRequest\n@docs timePeriodicallyListen, timeOnceAt\n\n\n## DOM\n\nPrimitives used for SVG and HTML\n(filling the same role as [`elm/virtual-dom`](https://dark.elm.dmy.fr/packages/elm/virtual-dom/latest/))\n\n@docs domText, domElement, svgElement, domElementNamespaced, DomNode\n@docs domFutureMap, domRender\n@docs DomModifier, domModifierFutureMap, domModifierBatch, domModifierNone\n@docs domAttribute, domAttributeNamespaced, domStyle, domBoolProperty, domStringProperty\n@docs domListenTo, domListenToPreventingDefaultAction\n@docs domScrollToShow, DomElementVisibilityAlignment, domScrollPositionRequest, domScrollToPosition\n\n\n## navigation\n\n`history` interaction\n\n@docs urlRequest\n@docs urlPush, urlReplace\n@docs navigateForward, navigateBack, navigationListen\n@docs navigateTo, reload\n\n\n## HTTP client\n\n@docs httpRequestSend, HttpError\n\n\n## random\n\nNot familiar with random \"generators\"? [`elm/random`](https://package.elm-lang.org/packages/elm/random/latest)\nexplains it nicely!\n\nHere's an example showing a number between 1 and 6 and a button to reroll\nusing [NoRedInk/elm-random-pcg-extended](https://dark.elm.dmy.fr/packages/NoRedInk/elm-random-pcg-extended/latest/)\n\n    import Random.Pcg.Extended\n    import Web\n\n    type State\n        = WaitingForInitialRandomness\n        | DiceUiState { diceEyes : Int, seed : Random.Pcg.Extended.Seed }\n\n    type DiceUiEvent\n        = RerollClicked\n\n    diceEyesRandomGenerator : Random.Pcg.Extended.Generator Int\n    diceEyesRandomGenerator =\n        Random.Pcg.Extended.int 1 6\n\n    { initialState = WaitingForInitialRandomness\n    , interface =\n        \\state ->\n            case state of\n                WaitingForInitialRandomness ->\n                    Web.randomUnsignedInt32sRequest 4\n                        |> Web.interfaceFutureMap\n                            (\\unsignedInt32s ->\n                                let\n                                    initialSeed : Random.Pcg.Extended.Seed\n                                    initialSeed =\n                                        Random.Pcg.Extended.initialSeed (unsignedInt32s |> List.head |> Maybe.withDefault 0) (unsignedInt32s |> List.drop 1)\n\n                                    ( diceEyes, newSeed ) =\n                                        Random.Pcg.Extended.step diceEyesRandomGenerator initialSeed\n                                in\n                                DiceUiState { diceEyes = diceEyes, seed = newSeed }\n                            )\n\n                DiceUiState randomStuff ->\n                    Web.domElement \"div\"\n                        []\n                        [ randomStuff.diceEyes |> String.fromInt |> Web.domText\n                        , Web.domElement \"button\"\n                            [ Web.domListenTo \"click\"\n                                |> Web.domModifierFutureMap (\\_ -> RerollClicked)\n                            ]\n                            [ Web.domText \"roll the dice\" ]\n                        ]\n                        |> Web.domRender\n                        |> Web.interfaceFutureMap\n                            (\\RerollClicked ->\n                                let\n                                    ( diceEyes, newSeed ) =\n                                        Random.Pcg.Extended.step diceEyesRandomGenerator randomStuff.seed\n                                in\n                                DiceUiState { diceEyes = diceEyes, seed = newSeed }\n                            )\n    }\n\n@docs randomUnsignedInt32sRequest\n\n\n## console\n\n@docs consoleLog, consoleWarn, consoleError\n\n\n## local storage\n\nSaved data for the url origin (protocol, host name, port) across browser sessions.\n\nThis data doesn't expire and won't be cleared when the page is closed.\nThe only exception is \"incognito mode\", where all data is cleared once the last \"private\" tab is closed.\n\nsee [mdn on `Window.localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)\n\n@docs localStorageRequest, localStorageSet, localStorageRemove\n@docs localStorageSetOnADifferentTabListen, localStorageRemoveOnADifferentTabListen\n\n\n## web sockets\n\n@docs SocketEvent, socketListenAndMaybeSend\n\n\n## file download\n\nDownloading a dynamically generated file.\n\nSecurity note: Browsers require downloads to be initiated by a user event.\nSo rather than allowing malicious sites to put files on your computer however they please,\nthe user has to at least click a button first.\nAs a result, the following interfaces only work when they are triggered by some user event.\n\nNote: There's no equivalent module for file select\nsince you can easily replicate the behavior using an input element with type file or file drop area modifiers,\nsee for example [mpizenberg/elm-file](https://dark.elm.dmy.fr/packages/mpizenberg/elm-file/latest/FileValue#load-files-with-an-input).\n\n@docs fileDownloadBytes\n\n\n## audio\n\nPlay sounds and music.\n\n    import Time\n    import Web\n\n    type alias State =\n        { audioSource : Maybe (Result Web.AudioSourceLoadError Web.AudioSource)\n        , audioStartTime : Maybe Time.Posix\n        }\n\n    { initialState =\n        { audioSource = Nothing\n        , audioStartTime = Nothing\n        }\n    , interface =\n        \\state ->\n            case state.audioSource of\n                Just (Ok audioSource) ->\n                    case state.audioStartTime of\n                        Just startTime\n                            Web.audioFromSource audioSource startTime\n                                |> Web.audioPlay\n\n\n                        Nothing ->\n                            Web.timePosixRequest\n                                |> Web.interfaceFutureMap\n                                    (\\time -> { state | audioSTartTime = time |> Just })\n\n                Nothing ->\n                    Web.audioSourceLoad \"https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/Yodel_Sound_Effect.mp3\"\n                        |> Web.interfaceFutureMap\n                            (\\result -> { state | audioSource = result |> Just })\n\n                Just (Err _) ->\n                    Web.consoleError \"audio failed to load\"\n    }\n\n@docs Audio, AudioSource, AudioSourceLoadError, AudioProcessing, AudioParameterTimeline\n\n@docs audioSourceLoad, audioFromSource, audioPlay\n@docs audioVolumeScaleBy, audioSpeedScaleBy, audioStereoPan\n@docs audioAddLinearConvolutionWith, audioAddHighpassFromFrequency, audioAddLowpassUntilFrequency\n\nTo detune, use [`audioSpeedScaleBy`](#audioSpeedScaleBy). It's documentation also shows which scale relates to which semitone pitch.\n\nTo build an [`AudioParameterTimeline`](Web#AudioParameterTimeline):\n\n@docs audioParameterAt, audioParameterThrough\n\n\n## clipboard\n\n@docs clipboardRequest, clipboardReplaceBy\n\nNote: To listen for [copy, cut and paste events](https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent),\nuse [`Web.domListenTo`](Web#domListenTo)\n\n\n## geo location\n\nObserve the [`GeoLocation`](Web#GeoLocation)\nusing the [web geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API).\n\n@docs GeoLocation, geoLocationRequest, geoLocationChangeListen\n\n\n## gamepads\n\nObserve connected [gamepads and other game controllers](Web#Gamepad)\n(not including motion sensing, gesture recognition etc.).\n\n    import Web\n\n    interface =\n        \\state ->\n            [ Web.timePeriodicallyListen (Duration.seconds (1 / 50))\n                |> Web.interfaceFutureMap\n                    ..simulate one tick using gamepad inputs..\n            , [ Web.gamepadsRequest, Web.gamepadsChangeListen ]\n                |> Web.interfaceBatch\n                |> Web.interfaceFutureMap (\\gamepads -> { state | gamepads = gamepads })\n            ]\n                |> Web.interfaceBatch\n\nIf your gamepad isn't showing up in the list,\npress some buttons. On some devices, only certain buttons will wake up the gamepad API (the shapes on PS3 controllers, for instance)\n\n@docs Gamepad, GamepadButton, gamepadsRequest, gamepadsChangeListen\n\n\n## notification\n\nGive important notices to the user as push notifications.\nConsider this a convenience feature, not something users have to rely upon.\nAlways offer users alternative methods to view messages or initiate actions\nand allow users to opt out of getting more in the future.\n\n@docs notificationAskForPermission, notificationShow, NotificationClicked\n\nYou can combine it with [`Web.windowVisibilityChangeListen`](Web#windowVisibilityChangeListen)\nto only notify users when they're on a different page\n\n    import Web\n\n    type State\n        = State\n            { windowVisibility : Web.WindowVisibility\n            , whoseMove : Player\n            , mode : Mode\n            , notificationPermissionToggle : Permission\n            }\n\n    type Permission\n        = Rejected\n        | Accepted\n\n    type Mode\n        = LongGameBoardMode\n        | SettingsPage\n\n    type Player\n        = You\n        | Opponent\n\n    interface : State -> Web.Interface State\n    interface =\n        \\(State state) ->\n            [ case state.notificationPermissionToggle of\n                Accepted ->\n                    Web.notificationAskForPermission\n\n                Rejected ->\n                    Web.interfaceNone\n            , case state.windowVisibility of\n                Web.WindowShown ->\n                    Web.interfaceNone\n\n                Web.WindowHidden ->\n                    case state.whoseTurn of\n                        Opponent ->\n                            Web.interfaceNone\n\n                        You ->\n                            Web.Notification.show\n                                { message = \"opponent moved\", ... }\n                                |> Web.interfaceFutureMap\n                                    (\\Web.NotificationClicked ->\n                                        -- return to the game if previously in settings\n                                        State { state | mode = LongGameBoardMode }\n                                    )\n\n            , case state.mode of\n                LongGameBoardMode ->\n                    ..listen for opponent move from server..\n                        |> Web.interfaceFutureMap\n                            (\\... -> State { state | whoseMove = You })\n\n                SettingsPage ->\n                    ..toggle for accepting/rejecting notifications..\n                        |> Web.interfaceFutureMap\n                            (\\... -> State { state | notificationPermissionToggle = ..opposite.. })\n            ]\n                |> Web.interfaceBatch\n\n\n## window\n\nObserve and alter the page's global environment\n\n@docs animationFrameListen, windowVisibilityChangeListen, WindowVisibility\n@docs windowSizeRequest, windowSizeChangeListen\n@docs preferredLanguagesRequest, preferredLanguagesChangeListen\n@docs documentListenTo, windowListenTo\n\nWhen navigating to a new page on the same site,\nyou may want to change the document's context:\n\n@docs titleReplaceBy, authorSet, keywordsSet, descriptionSet\n\n\n## embed\n\nIf you just want to replace a part of your elm app with this architecture. Make sure to wire in all 3:\n\n@docs ProgramConfig, programInit, programUpdate, programSubscriptions\n\nUnder the hood, [`Web.program`](Web#program) is then defined as just\n\n    program config =\n        Platform.worker\n            { init = \\() -> Web.programInit yourAppConfig\n            , update = Web.programUpdate yourAppConfig\n            , subscriptions = Web.programSubscriptions yourAppConfig\n            }\n\n\n## internals, safe to ignore for users\n\nExposed so can for example simulate it more easily in tests, add a debugger etc.\n\n@docs DomElement, DefaultActionHandling, DomModifierSingle\n\n@docs ProgramState, ProgramEvent, InterfaceSingle, DomTextOrElementHeader, DomElementHeader\n\n@docs SortedKeyValueList\n\n@docs InternalFastDict, InternalFastDictInner, InternalFastDictNColor\n\nIf you need more things like json encoders/decoders, [open an issue](https://github.com/lue-bird/elm-state-interface-experimental/issues/new)\n\n","unions":[{"name":"AudioProcessing","comment":" A single effect filter applied to an [`Audio`](#Audio)\n","args":[],"cases":[["AudioLinearConvolution",["{ sourceUrl : String.String }"]],["AudioLowpass",["{ cutoffFrequency : Web.AudioParameterTimeline }"]],["AudioHighpass",["{ cutoffFrequency : Web.AudioParameterTimeline }"]]]},{"name":"AudioSourceLoadError","comment":" These are possible errors we can get when loading an audio source file.\n\n  - `AudioSourceLoadDecodeError`: This means we got the data but we couldn't decode it. One likely reason for this is that your url points to the wrong place and you're trying to decode a 404 page instead.\n  - `AudioSourceLoadNetworkError`: We couldn't reach the url. Either it's some kind of CORS issue, the server is down, or you're disconnected from the internet.\n\n","args":[],"cases":[["AudioSourceLoadDecodeError",[]],["AudioSourceLoadNetworkError",[]]]},{"name":"DefaultActionHandling","comment":" Setting for a listen [`Web.DomModifier`](Web#DomModifier)\nto keep or overwrite the browser's default action\n","args":[],"cases":[["DefaultActionPrevent",[]],["DefaultActionExecute",[]]]},{"name":"DomElementVisibilityAlignment","comment":" What part of the [`Web.domElement`](Web#domElement) should be visible\n\n  - `DomElementStart`: mostly for text to read\n  - `DomElementEnd`: mostly for text to write\n  - `DomElementCenter`: mostly for images\n\n","args":[],"cases":[["DomElementStart",[]],["DomElementEnd",[]],["DomElementCenter",[]]]},{"name":"DomModifierSingle","comment":" An individual [`DomModifier`](#DomModifier).\nCreate using [`domAttribute`](#domAttribute), [`domStyle`](#domStyle), [`domListenTo`](#domListenTo) etc.\n","args":["future"],"cases":[["Attribute",["{ namespace : Maybe.Maybe String.String, key : String.String, value : String.String }"]],["StringProperty",["{ key : String.String, value : String.String }"]],["BoolProperty",["{ key : String.String, value : Basics.Bool }"]],["Style",["{ key : String.String, value : String.String }"]],["ScrollToPosition",["{ fromLeft : Basics.Float, fromTop : Basics.Float }"]],["ScrollToShow",["{ x : Web.DomElementVisibilityAlignment, y : Web.DomElementVisibilityAlignment }"]],["ScrollPositionRequest",["{ fromLeft : Basics.Float, fromTop : Basics.Float } -> future"]],["Listen",["{ eventName : String.String, on : Json.Decode.Value -> future, defaultActionHandling : Web.DefaultActionHandling }"]]]},{"name":"DomNode","comment":" Plain text or an [`DomElement`](#DomElement). Create using [`domText`](#domText) and [`domElement`](#domElement)\n","args":["future"],"cases":[["DomText",["String.String"]],["DomElement",["Web.DomElement future"]]]},{"name":"DomTextOrElementHeader","comment":" Plain text or a [`DomElementHeader`](#DomElementHeader) for use in an [`Interface`](#Interface).\n","args":["future"],"cases":[["DomHeaderText",["String.String"]],["DomElementHeader",["Web.DomElementHeader future"]]]},{"name":"GamepadButton","comment":" Buttons are either held down with an optional value between 0 and 1 to measure how hard,\nor they are released with an optional detection of touch which defaults to false.\n","args":[],"cases":[["GamepadButtonPressed",["{ firmnessPercentage : Basics.Float }"]],["GamepadButtonReleased",["{ isTouched : Basics.Bool }"]]]},{"name":"HttpError","comment":" A Request can fail in a couple ways:\n\n  - `BadUrl`: you did not provide a valid URL\n  - `NetworkError`: the user turned off their wifi, went in a cave, etc.\n    or the server CORS is misconfigured.\n    Note: A 404 for example does not constitute a network error\n  - `BadStatus`: you got a response back, but the status code indicates failure. Contains:\n      - the statusCode like 404 and statusText describing what the statusCode means a little\n      - headers like Content-Length and Expires\n      - the body as [`Bytes`](https://dark.elm.dmy.fr/packages/elm/bytes/latest/)\n\n","args":[],"cases":[["HttpBadUrl",[]],["HttpNetworkError",["String.String"]],["HttpBadStatus",["{ statusCode : Basics.Int, statusText : String.String, headers : List.List { name : String.String, value : String.String }, body : Bytes.Bytes }"]]]},{"name":"InterfaceSingle","comment":" A \"non-batched\" [`Interface`](#Interface).\nTo create one, use the helpers in [time](#time), [DOM](#dom), [HTTP](#http-client) etc.\n","args":["future"],"cases":[["DocumentTitleReplaceBy",["String.String"]],["DocumentAuthorSet",["String.String"]],["DocumentKeywordsSet",["List.List String.String"]],["DocumentDescriptionSet",["String.String"]],["DocumentEventListen",["{ eventName : String.String, on : Json.Decode.Decoder future }"]],["ConsoleLog",["String.String"]],["ConsoleWarn",["String.String"]],["ConsoleError",["String.String"]],["NavigationReplaceUrl",["AppUrl.AppUrl"]],["NavigationPushUrl",["AppUrl.AppUrl"]],["NavigationGo",["Basics.Int"]],["NavigationLoad",["String.String"]],["NavigationReload",["()"]],["NavigationUrlRequest",["AppUrl.AppUrl -> future"]],["FileDownload",["{ mimeType : String.String, name : String.String, contentUnsignedInt8s : List.List Basics.Int }"]],["ClipboardReplaceBy",["String.String"]],["ClipboardRequest",["String.String -> future"]],["AudioSourceLoad",["{ url : String.String, on : Result.Result Web.AudioSourceLoadError Web.AudioSource -> future }"]],["AudioPlay",["Web.Audio"]],["DomNodeRender",["{ reversePath : List.List Basics.Int, node : Web.DomTextOrElementHeader future }"]],["NotificationAskForPermission",["()"]],["NotificationShow",["{ id : String.String, message : String.String, details : String.String, on : Web.NotificationClicked -> future }"]],["HttpRequestSend",["{ url : String.String, method : String.String, headers : List.List { name : String.String, value : String.String }, bodyUnsignedInt8s : Maybe.Maybe (List.List Basics.Int), on : Result.Result Web.HttpError Bytes.Bytes -> future }"]],["TimePosixRequest",["Time.Posix -> future"]],["TimezoneOffsetRequest",["Basics.Int -> future"]],["TimeOnce",["{ pointInTime : Time.Posix, on : Time.Posix -> future }"]],["TimePeriodicallyListen",["{ intervalDurationMilliSeconds : Basics.Int, on : Time.Posix -> future }"]],["TimezoneNameRequest",["String.String -> future"]],["RandomUnsignedInt32sRequest",["{ count : Basics.Int, on : List.List Basics.Int -> future }"]],["WindowSizeRequest",["{ width : Basics.Int, height : Basics.Int } -> future"]],["WindowPreferredLanguagesRequest",["List.List String.String -> future"]],["WindowEventListen",["{ eventName : String.String, on : Json.Decode.Decoder future }"]],["WindowVisibilityChangeListen",["Web.WindowVisibility -> future"]],["WindowAnimationFrameListen",["Time.Posix -> future"]],["WindowPreferredLanguagesChangeListen",["List.List String.String -> future"]],["SocketListen",["{ address : String.String, on : Web.SocketEvent -> future }"]],["SocketDataSend",["{ address : String.String, data : String.String }"]],["LocalStorageSet",["{ key : String.String, value : Maybe.Maybe String.String }"]],["LocalStorageRequest",["{ key : String.String, on : Maybe.Maybe String.String -> future }"]],["LocalStorageRemoveOnADifferentTabListen",["{ key : String.String, on : AppUrl.AppUrl -> future }"]],["LocalStorageSetOnADifferentTabListen",["{ key : String.String, on : { appUrl : AppUrl.AppUrl, oldValue : Maybe.Maybe String.String, newValue : String.String } -> future }"]],["GeoLocationRequest",["Web.GeoLocation -> future"]],["GeoLocationChangeListen",["Web.GeoLocation -> future"]],["GamepadsRequest",["Dict.Dict Basics.Int Web.Gamepad -> future"]],["GamepadsChangeListen",["Dict.Dict Basics.Int Web.Gamepad -> future"]]]},{"name":"InternalFastDict","comment":" Alternative to `Dict` that internally allows fast creation from already sorted elements\nwhich significantly speeds up DOM interface creation.\n","args":["key","value"],"cases":[["InternalFastDict",["Basics.Int","Web.InternalFastDictInner key value"]]]},{"name":"InternalFastDictInner","comment":" Structure of an [`InternalFastDict`](#InternalFastDict)\n","args":["key","value"],"cases":[["InternalFastDictInnerNode",["Web.InternalFastDictNColor","key","value","Web.InternalFastDictInner key value","Web.InternalFastDictInner key value"]],["InternalFastDictLeaf",[]]]},{"name":"InternalFastDictNColor","comment":" The color of an [`InternalFastDictInner`](#InternalFastDictInner) node. Leaves are considered Black.\n","args":[],"cases":[["InternalFastDictRed",[]],["InternalFastDictBlack",[]]]},{"name":"NotificationClicked","comment":" The user clicked a displayed notification,\nmoving the focus to our page\n","args":[],"cases":[["NotificationClicked",[]]]},{"name":"ProgramEvent","comment":" The \"msg\" in a [`Web.program`](#program)\n","args":["appState"],"cases":[["JsEventFailedToDecode",["Json.Decode.Error"]],["JsEventEnabledConstructionOfNewAppState",["appState"]]]},{"name":"ProgramState","comment":" The \"model\" in a [`Web.program`](#program)\n","args":["appState"],"cases":[["State",["{ interface : Web.Interface appState, appState : appState }"]]]},{"name":"SocketEvent","comment":" How the connection has changed or what message has been sent\n","args":[],"cases":[["SocketOpened",[]],["SocketDataReceived",["String.String"]],["SocketClosed",["{ code : Basics.Int, reason : String.String }"]]]},{"name":"SortedKeyValueList","comment":" Alternative to `Dict` optimized for fast merge and fast creation.\nWould be a terrible fit if we needed fast insert and get.\n","args":["key","value"],"cases":[["SortedKeyValueList",["List.List { key : key, value : value }"]]]},{"name":"WindowVisibility","comment":" The visibility to the user\n\n  - `WindowHidden`: the user has navigated to a new page, switched tabs, closed the tab, minimized or closed the browser, or, on mobile, switched from the browser to a different app\n  - `WindowShown` otherwise\n\n","args":[],"cases":[["WindowShown",[]],["WindowHidden",[]]]}],"aliases":[{"name":"Audio","comment":" Some kind of sound we want to play. To create `Audio`, start with [`Web.audioFromSource`](Web#audioFromSource)\n","args":[],"type":"RecordWithoutConstructorFunction.RecordWithoutConstructorFunction { url : String.String, startTime : Time.Posix, volume : Web.AudioParameterTimeline, speed : Web.AudioParameterTimeline, stereoPan : Web.AudioParameterTimeline, processingLastToFirst : List.List Web.AudioProcessing }"},{"name":"AudioParameterTimeline","comment":" defining how loud a sound should be at any point in time\n","args":[],"type":"{ startValue : Basics.Float, keyFrames : List.List { time : Time.Posix, value : Basics.Float } }"},{"name":"AudioSource","comment":" Audio data we can use to play sounds.\nUse [`Web.audioSourceLoad`](Web#audioSourceLoad) to fetch an [`AudioSource`](#AudioSource).\n\nYou can also use the contained source `duration`, for example to find fade-out times or to create a loop:\n\n    audioLoop : AudioSource -> Time.Posix -> Time.Posix -> Audio\n    audioLoop source initialTime lastTick =\n        Web.audioFromSource source\n            (Duration.addTo\n                initialTime\n                (source.duration\n                    |> Quantity.multiplyBy\n                        (((Duration.from initialTime lastTick |> Duration.inSeconds)\n                            / (source.duration |> Duration.inSeconds)\n                         )\n                            |> floor\n                            |> toFloat\n                        )\n                )\n            )\n\n","args":[],"type":"RecordWithoutConstructorFunction.RecordWithoutConstructorFunction { url : String.String, duration : Duration.Duration }"},{"name":"DomElement","comment":" A tagged DOM node that can itself contain child [node](#DomNode)s\n","args":["future"],"type":"RecordWithoutConstructorFunction.RecordWithoutConstructorFunction { header : Web.DomElementHeader future, subs : List.List (Web.DomNode future) }"},{"name":"DomElementHeader","comment":" Everything about a [tagged DOM element](Web#DomElement)\nexcept potential sub-[node](Web#DomNode)s\n","args":["future"],"type":"RecordWithoutConstructorFunction.RecordWithoutConstructorFunction { namespace : Maybe.Maybe String.String, tag : String.String, styles : Web.SortedKeyValueList String.String String.String, attributes : Web.SortedKeyValueList String.String String.String, attributesNamespaced : Web.SortedKeyValueList { namespace : String.String, key : String.String } String.String, stringProperties : Web.SortedKeyValueList String.String String.String, boolProperties : Web.SortedKeyValueList String.String Basics.Bool, scrollToPosition : Maybe.Maybe { fromLeft : Basics.Float, fromTop : Basics.Float }, scrollToShow : Maybe.Maybe { x : Web.DomElementVisibilityAlignment, y : Web.DomElementVisibilityAlignment }, scrollPositionRequest : Maybe.Maybe ({ fromLeft : Basics.Float, fromTop : Basics.Float } -> future), eventListens : Web.SortedKeyValueList String.String { on : Json.Decode.Value -> future, defaultActionHandling : Web.DefaultActionHandling } }"},{"name":"DomModifier","comment":" Setting of a [`Web.DomElement`](Web#DomElement).\nTo create one, use [`domAttribute`](#domAttribute), [`domStyle`](#domStyle), [`domListenTo`](#domListenTo) etc.\nTo combine multiple, use [`Web.domModifierBatch`](#domModifierBatch) and [`Web.domModifierNone`](#domModifierNone)\n\nFor example to get `<a href=\"https://elm-lang.org\">elm</a>`\n\n    Web.domElement \"a\"\n        [ Web.domAttribute \"href\" \"https://elm-lang.org\" ]\n        [ Web.domText \"elm\" ]\n\nBtw: If you can think of a nicer name for this like \"customization\", \"characteristic\" or \"aspect\",\nplease [open an issue](https://github.com/lue-bird/elm-state-interface-experimental/issues/new).\n\n\n## attribute vs property\n\n  - attribute: part of the HTML itself. E.g. `class` in `<div class=\"greeting\"></div>`\n  - property: an actual field on that js DOM object. E.g. `className` in `div.className = \"greeting\"`\n\nBut don't be surprised: There are cases where\n\n  - only a property _or_ an attribute of a name exists\n  - both exist but they have different effects\n\nFor example, trying to reset the text inside a a text input with\n\n    Web.domAttribute \"value\" \"user input\"\n\n    -- then later replace it with\n    Web.domAttribute \"value\" \"\"\n\nwill only provide a _default value_ and has no effect on the currently written text,\nso you'll have to use\n\n    Web.domStringProperty \"value\" \"\"\n\nSimilarly for checkboxes:\n\n    Web.domBoolProperty \"checked\" False\n\nMaybe a rule of thumb is:\nUse properties to set anything related to interactivity\nand attributes for everything else.\n\nIf you have some opinions or better explanations,\nplease [open an issue](https://github.com/lue-bird/elm-state-interface-experimental/issues/new).\n\n","args":["future"],"type":"Rope.Rope (Web.DomModifierSingle future)"},{"name":"Gamepad","comment":" Controller information on button presses, thumbstick positions etc.\n\n  - `primaryButton`: The most common action like \"enter\"/\"confirm\" or jump\n\n  - `secondaryButton`: Usually \"cancel\"/\"skip\" or a common ability like duck/sneak, drop or heal\n\n  - `tertiaryButton`: common-ish ability like consume, interact, reload or an ultimate power\n\n  - `quaternaryButton`: less common action like quick menu or mode switch\n\n  - `leftBumperButton`, `rightBumperButton`: The top row of smaller buttons on the side opposite of you, sometimes called shoulder buttons.\n    Often used for alternative menu actions like slot switching or quick map\n\n  - `leftTriggerButton`, `rightTriggerButton`: The bottom row of larger buttons on the side opposite of you.\n    Often used for holdable abilities like sliding or dashing\n\n  - `selectButton`: Usually opens an in-world menu with for example inventory, lore, ability overview or a map\n\n  - `startButton`: Usually pauses the game and opens a menu with options like settings and quit\n\n  - `leftThumbstickButton`, `rightThumbstickButton`: Not all gamepads have these, so they often either double existing actions like \"confirm\"\n    or perform actions that are only very rarely helpful, like hiding ui elements or making a screenshot\n\n  - `upButton`, `downBottom`, `leftButton`, `rightButton`: exactly one step in a direction, usually in a (quick) menu/inventory\n\n  - `homeButton`: Usually turns the gamepad on/off, or changes the state of the game\n\n  - `touchpadButton`: Not present on most gamepads. While the touchpad is often used for controlling the mouse, it can also be used as a simple button\n\n  - `thumbstickLeft`, `thumbstickRight`: Those wiggly that can be moved in any direction by any amount.\n    They are provided as `x, y` signed percentages\n\n  - `kindId`: some information about the gamepad, usually containing the USB vendor, product id of the gamepad\n    and the name of the gamepad as provided by the driver. See [mdn](https://developer.mozilla.org/en-US/docs/Web/API/Gamepad/id)\n\n    You can use this information to for example determine how to show controls\n\n  - `buttonsAdditional`, `thumbsticksAdditional`: Maybe you have a weird gamepad with 3 thumbsticks? These might help 🤷\n\nImplementation note:\nAs you know, gamepad layouts differ between models.\nFor most of them, we're able to map them to the buttons and thumbsticks above.\nIf you experience issues with some model, [open an issue](https://github.com/lue-bird/elm-state-interface-experimental/issues/new)\n\n","args":[],"type":"RecordWithoutConstructorFunction.RecordWithoutConstructorFunction { primaryButton : Web.GamepadButton, secondaryButton : Web.GamepadButton, tertiaryButton : Web.GamepadButton, quaternaryButton : Web.GamepadButton, leftBumperButton : Web.GamepadButton, rightBumperButton : Web.GamepadButton, leftTriggerButton : Web.GamepadButton, rightTriggerButton : Web.GamepadButton, selectButton : Web.GamepadButton, startButton : Web.GamepadButton, leftThumbstickButton : Web.GamepadButton, rightThumbstickButton : Web.GamepadButton, upButton : Web.GamepadButton, downButton : Web.GamepadButton, leftButton : Web.GamepadButton, rightButton : Web.GamepadButton, homeButton : Web.GamepadButton, touchpadButton : Web.GamepadButton, additionalButtons : List.List Web.GamepadButton, kindId : String.String, thumbstickLeft : { x : Basics.Float, y : Basics.Float }, thumbstickRight : { x : Basics.Float, y : Basics.Float }, thumbsticksAdditional : List.List { x : Basics.Float, y : Basics.Float } }"},{"name":"GeoLocation","comment":" Position and (if available) altitude of the device on Earth, as well as the accuracy with which these properties are calculated.\nThe geographic position information is provided in terms of World Geodetic System coordinates (WGS84).\n\nDevice movement direction and speed might also be provided.\n\n[`Length`](https://dark.elm.dmy.fr/packages/ianmackenzie/elm-units/latest/Length),\n[`Angle`](https://dark.elm.dmy.fr/packages/ianmackenzie/elm-units/latest/Angle) and\n[`Speed`](https://dark.elm.dmy.fr/packages/ianmackenzie/elm-units/latest/Speed)\nare from `ianmackenzie/elm-units`\n\n","args":[],"type":"RecordWithoutConstructorFunction.RecordWithoutConstructorFunction { latitudeInDecimalDegrees : Basics.Float, longitudeInDecimalDegrees : Basics.Float, latitudeLongitudeAccuracy : Maybe.Maybe Length.Length, altitudeAboveNominalSeaLevel : Maybe.Maybe Length.Length, altitudeAccuracy : Maybe.Maybe Length.Length, headingWith0AsTrueNorthAndIncreasingClockwise : Maybe.Maybe Angle.Angle, speed : Maybe.Maybe Speed.Speed }"},{"name":"Interface","comment":" Incoming and outgoing effects.\nTo create one, use the helpers in [time](#time), [DOM](#dom), [HTTP](#http-client) etc.\n\nTo combine multiple, use [`Web.interfaceBatch`](#interfaceBatch) and [`Web.interfaceNone`](#interfaceNone).\nTo change the value that comes back in the future, use [`Web.interfaceFutureMap`](Web#interfaceFutureMap)\n\n","args":["future"],"type":"Web.InternalFastDict String.String (Web.InterfaceSingle future)"},{"name":"Program","comment":" A [`Platform.Program`](https://dark.elm.dmy.fr/packages/elm/core/latest/Platform#Program)\nthat elm can run,\nproduced by [`Web.program`](#program)\n","args":["state"],"type":"Platform.Program () (Web.ProgramState state) (Web.ProgramEvent state)"},{"name":"ProgramConfig","comment":" What's needed to create a state-interface [`program`](#program)\n","args":["state"],"type":"RecordWithoutConstructorFunction.RecordWithoutConstructorFunction { initialState : state, interface : state -> Web.Interface state, ports : { toJs : Json.Encode.Value -> Platform.Cmd.Cmd (Web.ProgramEvent state), fromJs : (Json.Encode.Value -> Web.ProgramEvent state) -> Platform.Sub.Sub (Web.ProgramEvent state) } }"}],"values":[{"name":"animationFrameListen","comment":" An [`Interface`](Web#Interface) for detecting when animation frames occur.\nThis will be about 60 times per second, though 75, 120, and 144 are also widely used.\nTo balance this out in your animation, the [current time](https://dark.elm.dmy.fr/packages/elm/time/latest/Time#Posix) is provided each frame.\n\nTo get a delta, you could use [`Web.timePosixRequest`](Web#timePosixRequest)\nto get a start time and check with e.g. [`Duration.from`](https://dark.elm.dmy.fr/packages/ianmackenzie/elm-units/latest/Duration#from) how far you've progressed in the timeline.\n\nNote: To improve performance and battery life, most browsers pause these notifications when the app is running in a background tab or a hidden `<iframe>`.\n\nReplacement for [`Browser.Events.onAnimationFrame`](https://dark.elm.dmy.fr/packages/elm/browser/latest/Browser-Events#onAnimationFrame)\n\nNote: uses [`window.requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame).\n\n","type":"Web.Interface Time.Posix"},{"name":"audioAddHighpassFromFrequency","comment":" Frequencies below a given cutoff [parameter](Web#AudioParameterTimeline) are attenuated;\nfrequencies above it pass through.\n\nHas a 12dB/octave rolloff and no peak at the cutoff.\n\n","type":"Web.AudioParameterTimeline -> Web.Audio -> Web.Audio"},{"name":"audioAddLinearConvolutionWith","comment":" Usually used to apply reverb and or echo.\nGiven a loaded [`AudioSource`](Web#AudioSource) containing the impulse response,\nit performs a [Convolution](https://en.wikipedia.org/wiki/Convolution) with the [`Audio`](Web#Audio)\n\nIf you need some nice impulse wavs to try it out, there's a few at [`dhiogoboza/audio-convolution`](https://github.com/dhiogoboza/audio-convolution/tree/master/impulses).\nIf you know more nice ones, don't hesitate to open an issue or a PR.\n\n","type":"Web.AudioSource -> Web.Audio -> Web.Audio"},{"name":"audioAddLowpassUntilFrequency","comment":" Frequencies below a given cutoff [parameter](Web#AudioParameterTimeline) pass through;\nfrequencies above it are attenuated.\n\nHas a 12dB/octave rolloff and no peak at the cutoff.\n\n","type":"Web.AudioParameterTimeline -> Web.Audio -> Web.Audio"},{"name":"audioFromSource","comment":" Create [`Audio`](Web#Audio) from an given loaded [source](Web#AudioSource)\nwhich will play at a given [time](https://dark.elm.dmy.fr/packages/elm/time/latest/)\n\n    -- play a song at half speed and wait 2 seconds after the usual song start time before starting\n    Web.audioFromSource\n        myCoolSong\n        (Duration.addTo usualSongStartTime (Duration.seconds 2))\n        |> Web.audioSpeedScaleBy (Web.audioParameterAt 0.5)\n\nNote that in some browsers audio will be muted until the user interacts with the webpage.\n\n","type":"Web.AudioSource -> Time.Posix -> Web.Audio"},{"name":"audioParameterAt","comment":" Set it to a constant value. Add [`audioParameterThrough`](#audioParameterThrough) to make it transition from this starting value over time\n","type":"Basics.Float -> Web.AudioParameterTimeline"},{"name":"audioParameterThrough","comment":" Specify a key value at a given absolute point in time.\nThe parameter will then transition linearly between those points.\n\nLet's define an audio function that fades in to 1 and then fades out until it's 0 again.\n\n    import Duration\n    import Time\n    import Web\n\n\n    -- 1                ________\n    --                /         \\\n    -- 0 ____________/           \\_______\n    --    t ->    fade in     fade out\n    fadeInOut fadeInStartTime fadeOutEndTime audio =\n        Web.audioParameterAt 0\n            |> Web.audioParameterThrough fadeInStartTime 1\n            |> Web.audioParameterThrough (Duration.addTo fadeInStartTime Duration.second) 1\n            |> Web.audioParameterThrough (Duration.subtractFrom fadeOutEndTime Duration.second) 1\n            |> Web.audioParameterThrough fadeOutEndTime 0\n\n  - 🧩 `Duration` is from [ianmackenzie/elm-units](https://dark.elm.dmy.fr/packages/ianmackenzie/elm-units/latest/)\n  - 🧩 `Time` is from [elm/time](https://dark.elm.dmy.fr/packages/elm/time/latest/)\n\nYou do not have to worry about order.\n\n","type":"Time.Posix -> Basics.Float -> Web.AudioParameterTimeline -> Web.AudioParameterTimeline"},{"name":"audioPlay","comment":" An [`Interface`](Web#Interface) for playing [`Audio`](Web#Audio) created with [`audioFromSource`](#audioFromSource).\n\nTo play multiple audios:\n\n    [ audio0, audio1, audio2 ]\n        |> List.map Web.audioPlay\n        |> Web.interfaceBatch\n\n","type":"Web.Audio -> Web.Interface future_"},{"name":"audioSourceLoad","comment":" An [`Interface`](Web#Interface) for fetching audio data from a given url\nand returning an [`AudioSource`](Web#AudioSource) to use with [`audioFromSource`](#audioFromSource).\n","type":"String.String -> Web.Interface (Result.Result Web.AudioSourceLoadError Web.AudioSource)"},{"name":"audioSpeedScaleBy","comment":" Scale the playback rate by a given factor. This will also affect pitch.\n\nFor example, `Web.audioSpeedScaleBy 0.5` means playback will take twice as long and the pitch will be one octave lower, see [AudioBufferSourceNode.playbackRate](https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode/playbackRate)\n\nIn general, to pitch by semitones:\n\n    Web.audioSpeedScaleBy\n        (Web.audioParameterAt (2 ^ (semitones / 12)))\n\nNote: It would be possible to modify the signal to compensate for the pitch change,\nsee [Audio time stretching and pitch scaling](https://en.wikipedia.org/wiki/Audio_time_stretching_and_pitch_scaling).\nHelp appreciated!\n\n","type":"Web.AudioParameterTimeline -> Web.Audio -> Web.Audio"},{"name":"audioStereoPan","comment":" Change the stereo panning with a given a signed percentage [parameter](Web#AudioParameterTimeline).\n\n`Web.audioStereoPan -0.9` for example means that the sound is almost fully balanced towards the left speaker\n\n","type":"Web.AudioParameterTimeline -> Web.Audio -> Web.Audio"},{"name":"audioVolumeScaleBy","comment":" Scale how loud it is.\n1 preserves the current volume, 0.5 halves it, and 0 mutes it.\nIf the the volume is less than 0, 0 will be used instead.\n","type":"Web.AudioParameterTimeline -> Web.Audio -> Web.Audio"},{"name":"authorSet","comment":" An [`Interface`](Web#Interface) for adding or replacing the document's author metadata\n","type":"String.String -> Web.Interface future_"},{"name":"clipboardReplaceBy","comment":" An [`Interface`](Web#Interface) for setting the textual contents of the system clipboard.\n\nNote: uses [`navigator.clipboard.writeText`](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText)\n\n","type":"String.String -> Web.Interface future_"},{"name":"clipboardRequest","comment":" An [`Interface`](Web#Interface) for reading the textual contents of the system clipboard.\n\nNote: uses [`navigator.clipboard.readText`](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/readText)\n\n","type":"Web.Interface String.String"},{"name":"consoleError","comment":" An [`Interface`](Web#Interface) for printing a message that something failed with bad consequences, for example\n\n> ❌ decoding the selected file failed. Please report this bug at ...\n\nNote: uses [`console.error`](https://developer.mozilla.org/en-US/docs/Web/API/console/error_static)\n\n","type":"String.String -> Web.Interface future_"},{"name":"consoleLog","comment":" An [`Interface`](Web#Interface) for printing a message with general information\nlike if certain tasks have been successful\n\n> survey submitted and received successfully\n\nDepending on what minifying tools you use for your production build, these might get removed.\n\nNote: uses [`console.log`](https://developer.mozilla.org/en-US/docs/Web/API/console/log_static),\njust like [`Debug.log`](https://dark.elm.dmy.fr/packages/elm/core/latest/Debug#log)\n\n","type":"String.String -> Web.Interface future_"},{"name":"consoleWarn","comment":" An [`Interface`](Web#Interface) for printing a message that something didn't succeed but you could recover from, for example\n\n> ⚠️ Unknown device - there may be compatibility issues.\n\n> ⚠️ Recoverable upload failure, will retry. Error was: no status.\n\nNote: uses [`console.warn`](https://developer.mozilla.org/en-US/docs/Web/API/console/warn_static)\n\n","type":"String.String -> Web.Interface future_"},{"name":"descriptionSet","comment":" An [`Interface`](Web#Interface) for adding or replacing the document's description metadata\nwhich should be a short and accurate summary of the content of the page.\nSeveral browsers, like Firefox and Opera, use this as the default description of bookmarked pages.\n","type":"String.String -> Web.Interface future_"},{"name":"documentListenTo","comment":" An [`Interface`](Web#Interface) for detecting a specific [`document` event](https://developer.mozilla.org/en-US/docs/Web/API/Document#events)\nthat has no native [`Interface`](Web#Interface), like like scroll, scrollend, selectionchange or paste\n","type":"String.String -> Web.Interface Json.Decode.Value"},{"name":"domAttribute","comment":" A key-value attribute [`DomModifier`](#DomModifier)\n","type":"String.String -> String.String -> Web.DomModifier future_"},{"name":"domAttributeNamespaced","comment":" A namespaced key-value attribute [`DomModifier`](#DomModifier).\nFor example, you could define an SVG xlink href attribute as\n\n    attributeXlinkHref : String -> Web.DomModifier msg\n    attributeXlinkHref value =\n        Web.domAttributeNamespaced \"http://www.w3.org/1999/xlink\" \"xlink:href\" value\n\n","type":"String.String -> String.String -> String.String -> Web.DomModifier future_"},{"name":"domBoolProperty","comment":" A key-bool value DOM property [`DomModifier`](#DomModifier)\n","type":"String.String -> Basics.Bool -> Web.DomModifier future_"},{"name":"domElement","comment":" Create a DOM element with a given tag, [`DomModifier`](#DomModifier)s and sub-[node](Web#DomNode)s.\nFor example to get `<p>flying</p>`\n\n    Web.domElement \"p\"\n        []\n        [ Web.domText \"flying\" ]\n\nTo create an SVG element, use [`Web.svgElement`](Web#svgElement)\n\n","type":"String.String -> List.List (Web.DomModifier future) -> List.List (Web.DomNode future) -> Web.DomNode future"},{"name":"domElementNamespaced","comment":" Create a DOM element with a given namespace, tag, [`DomModifier`](#DomModifier)s and sub-[node](Web#DomNode)s.\nFor example, [`Web.svgElement`](Web#svgElement) is defined as\n\n    svgElement : String -> List (Modifier future) -> List (DomNode future) -> DomNode future\n    svgElement tag modifiers subs =\n        Web.domElementNamespaced \"http://www.w3.org/2000/svg\" tag modifiers subs\n\n","type":"String.String -> String.String -> List.List (Web.DomModifier future) -> List.List (Web.DomNode future) -> Web.DomNode future"},{"name":"domFutureMap","comment":" Wire events from this [DOM node](Web#DomNode) to a specific event, for example\n\n    buttonUi \"start\"\n        |> Web.domFutureMap (\\Clicked -> StartButtonClicked)\n\nwith\n\n    buttonUi : String -> Web.DomNode ButtonEvent\n    buttonUi label =\n        Web.domElement \"button\"\n            [ Web.domListenTo \"click\"\n                |> Web.domModifierFutureMap (\\_ -> Clicked)\n            ]\n            [ Web.domText label ]\n\n    type ButtonEvent\n        = Clicked\n\n","type":"(future -> mappedFuture) -> Web.DomNode future -> Web.DomNode mappedFuture"},{"name":"domListenTo","comment":" Listen for a specific DOM event on the [`Web.DomElement`](Web#DomElement).\nUse [`domModifierFutureMap`](#domModifierFutureMap) to wire this to a specific event.\n\nIf you want to override the browser's default behavior for that event,\nuse [`domListenToPreventingDefaultAction`](#domListenToPreventingDefaultAction)\n\n","type":"String.String -> Web.DomModifier Json.Decode.Value"},{"name":"domListenToPreventingDefaultAction","comment":" Like [`domListenTo`](#domListenTo) but [preventing the browser's default action](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault).\n\nThat's for example how elm's [`Browser.Events.onSubmit`](https://dark.elm.dmy.fr/packages/elm/html/latest/Html-Events#onSubmit)\nprevents the form from changing the page’s location:\n\n    submitListen : Web.DomModifier ()\n    submitListen =\n        Web.domListenToPreventingDefaultAction \"submit\"\n            |> Web.domModifierFutureMap (\\_ -> ())\n\n","type":"String.String -> Web.DomModifier Json.Decode.Value"},{"name":"domModifierBatch","comment":" Combine multiple [`DomModifier`](#DomModifier)s into one.\n","type":"List.List (Web.DomModifier future) -> Web.DomModifier future"},{"name":"domModifierFutureMap","comment":" Wire events from this [`DomModifier`](#DomModifier) to a specific event.\n\n    Web.domListenTo \"click\" |> Web.domModifierFutureMap (\\_ -> ButtonClicked)\n\n","type":"(future -> mappedFuture) -> Web.DomModifier future -> Web.DomModifier mappedFuture"},{"name":"domModifierNone","comment":" Doing nothing as a [`DomModifier`](#DomModifier). These two examples are equivalent:\n\n    Web.domModifierBatch\n        [ a, Web.domModifierNone, b ]\n\nand\n\n    Web.domModifierBatch\n        (List.filterMap identity\n            [ a |> Just, Nothing, b |> Just ]\n        )\n\n","type":"Web.DomModifier future_"},{"name":"domRender","comment":" An [`Interface`](Web#Interface) for displaying a given [DOM node](Web#DomNode)\n","type":"Web.DomNode future -> Web.Interface future"},{"name":"domScrollPositionRequest","comment":" Getting the current scroll position from the left and top.\n\nUse in combination with [`domScrollToPosition`](#domScrollToPosition)\nto implement saving and restoring scroll position even when users had navigated off a URL.\n\n","type":"Web.DomModifier { fromLeft : Basics.Float, fromTop : Basics.Float }"},{"name":"domScrollToPosition","comment":" Ensure a given initial scroll position in both directions.\nTo move to the edge in a direction, use [`domScrollToShow`](#domScrollToShow) instead.\n\nUnlike [`domStyle`](#domStyle)s,\nthis is just an initial configuration\nwhich can be changed by user actions.\nSo adding e.g. `scrollToPosition ...`\nwill scroll once the next render happens\nbut will not prevent users from scrolling away.\n\n","type":"{ fromLeft : Basics.Float, fromTop : Basics.Float } -> Web.DomModifier future_"},{"name":"domScrollToShow","comment":" Ensure a given initial [`DomElementVisibilityAlignment`](Web#DomElementVisibilityAlignment)\nin both directions.\n\nUnlike [`domStyle`](#domStyle)s,\nthis is just an initial configuration\nwhich can be changed by user actions.\nSo adding e.g. `scrollToShow { y = Web.DomElementStart, x = Web.DomElementStart }`\nwill scroll to the top left once the next render happens\nbut will not prevent users from scrolling away.\n\nNote: Uses [`Element.scrollIntoView`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView)\n\n","type":"{ x : Web.DomElementVisibilityAlignment, y : Web.DomElementVisibilityAlignment } -> Web.DomModifier future_"},{"name":"domStringProperty","comment":" A key-string value DOM property [`DomModifier`](#DomModifier)\n","type":"String.String -> String.String -> Web.DomModifier future_"},{"name":"domStyle","comment":" A key-value style [`DomModifier`](#DomModifier)\n","type":"String.String -> String.String -> Web.DomModifier future_"},{"name":"domText","comment":" Plain text [DOM node](#DomNode)\n","type":"String.String -> Web.DomNode future_"},{"name":"fileDownloadBytes","comment":" An [`Interface`](Web#Interface) for downloading a given file\nwith [`Bytes`](https://dark.elm.dmy.fr/packages/elm/bytes/latest/) as its content,\na given type and and a given default name.\n\nReplacement for [`File.Download.bytes`](https://dark.elm.dmy.fr/packages/elm/file/latest/File-Download#bytes)\n\n","type":"{ name : String.String, mimeType : String.String, content : Bytes.Bytes } -> Web.Interface future_"},{"name":"gamepadsChangeListen","comment":" An [`Interface`](Web#Interface) for detecting changes to which [gamepads](Web#Gamepad)\nare connected and in which ways buttons and axes are activated.\n\nThe given `Dict` keys uniquely identify each device for the whole session,\nso you can for example check for removed and added gamepads using `Dict.diff`\nor track one host device in the state.\n\nImplementation note:\nThe [web gamepad API](https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API) does not offer a listener to detect changes.\nSo instead, we poll every 14ms (a bit more often than 60 times/second)\nwhich is just a bit faster than Chrome's actual fetch rate (fetch rate is not part of the specification).\n\nWe want to avoid missed inputs before your next simulation tick,\nso we just had to guess a good interval number.\n<https://stackoverflow.com/a/51483510>\n\nIf you have issues with unresponsiveness, [open an issue](https://github.com/lue-bird/elm-state-interface-experimental/issues/new)\n\n","type":"Web.Interface (Dict.Dict Basics.Int Web.Gamepad)"},{"name":"gamepadsRequest","comment":" An [`Interface`](Web#Interface) for getting which [gamepads](Web#Gamepad)\nare connected and in which ways buttons and axes are activated.\n\nThe given `Dict` keys uniquely identify each device for the whole session.\n\n","type":"Web.Interface (Dict.Dict Basics.Int Web.Gamepad)"},{"name":"geoLocationChangeListen","comment":" An [`Interface`](Web#Interface) for detecting changes in the current [position of the device](Web#GeoLocation)\n","type":"Web.Interface Web.GeoLocation"},{"name":"geoLocationRequest","comment":" An [`Interface`](Web#Interface) for getting the current [position of the device](Web#GeoLocation)\n","type":"Web.Interface Web.GeoLocation"},{"name":"httpRequestSend","comment":" An [`Interface`](Web#Interface) for sending an HTTP request\nto a given url\n\n    exampleHttpPost : Web.Interface (Result Web.HttpError Bytes)\n    exampleHttpPost =\n        Web.httpRequestSend\n            { url = ...\n            , method = \"POST\"\n            , headers = [ { name = \"Content-Type\", value = \"application/json\" } ]\n            , body =\n                96\n                    |> Json.Encode.int\n                    |> Json.Encode.encode 0\n                    |> Bytes.Encode.encode\n                    |> Just\n            }\n\n    exampleHttpGet : Web.HttpRequest\n    exampleHttpGet =\n        Web.httpRequestSend\n            { url = ...\n            , method = \"GET\"\n            , headers = [ { name = \"X-Custom-Header\", value = \"ProcessThisImmediately\" } ]\n            , body = Nothing -- empty bytes with HEAD or GET can lead to an HttpError\n            }\n            |> Web.interfaceFutureMap\n                (\\responseOrError ->\n                    case responseOrError of\n                        Ok bytes ->\n                            case bytes |> Bytes.Decode.decode (Bytes.Decode.string (bytes |> Bytes.width)) of\n                                Just jsonString ->\n                                    case jsonString |> Json.Decode.decodeString ... of\n                                        Err jsonError ->\n                                            Err (jsonError |> Json.Decode.errorToString)\n\n                                        Ok parsed ->\n                                            Ok parsed\n\n                                Nothing ->\n                                    Err \"failed to decode bytes to a string\"\n\n                        Err _ ->\n                            Err \"HTTP response error\"\n                )\n\nUsually, if you have a request body,\nthe headers include `Content-Type` with a [MIME type](https://en.wikipedia.org/wiki/Media_type)\n\nThe response will either contain [`Bytes`](https://dark.elm.dmy.fr/packages/elm/bytes/latest/).\nor an [`HttpError`](Web#HttpError)\n\nIf you for some reason need a timeout,\nadd a [`Web.timeOnceAt`](Web#timeOnceAt) and remove the [`Web.httpRequestSend`](#httpRequestSend)\nonce it has passed.\n\nUses the [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)\n\n","type":"{ url : String.String, method : String.String, headers : List.List { name : String.String, value : String.String }, body : Maybe.Maybe Bytes.Bytes } -> Web.Interface (Result.Result Web.HttpError Bytes.Bytes)"},{"name":"interfaceBatch","comment":" Combine multiple [`Interface`](#Interface)s into one\n","type":"List.List (Web.Interface future) -> Web.Interface future"},{"name":"interfaceFutureMap","comment":" Take what the [`Interface`](#Interface) can come back with and return a different future value.\n\nIn practice, this is sometimes used like a kind of event-config pattern:\n\n    Web.timePosixRequest\n        |> Web.interfaceFutureMap (\\timeNow -> TimeReceived timeNow)\n\n    button \"show all entries\"\n        |> Web.domRender\n        |> Web.interfaceFutureMap (\\Pressed -> ShowAllEntriesButtonClicked)\n\nsometimes as a way to deal with all events (like `update` in The Elm Architecture)\n\n    ...\n        |> Web.interfaceFutureMap\n            (\\event ->\n                case event of\n                    MouseMovedTo newMousePoint ->\n                        { state | mousePoint = newMousePoint }\n\n                    CounterDecreaseClicked ->\n                        { state | counter = state.counter - 1 }\n\n                    CounterIncreaseClicked ->\n                        { state | counter = state.counter + 1 }\n            )\n\nand sometimes to nest events (like `Cmd.map/Task.map/Sub.map/...` in The Elm Architecture):\n\n    type Event\n        = DirectoryTreeViewEvent TreeUiEvent\n        | SortButtonClicked\n\n    type TreeUiEvent\n        = Expanded TreePath\n        | Collapsed TreePath\n\n    interface : State -> Interface State\n    interface state =\n        ...\n            [ treeUi ..\n                |> Web.interfaceFutureMap DirectoryTreeViewEvent\n            , ...\n            ]\n            |> Web.domRender\n\n    treeUi : ... -> Web.DomNode TreeUiEvent\n\nIn all these examples, you end up converting the narrow future representation of part of the interface\nto a broader representation for the parent interface\n\n","type":"(future -> mappedFuture) -> Web.Interface future -> Web.Interface mappedFuture"},{"name":"interfaceNone","comment":" Doing nothing as an [`Interface`](#Interface). These two examples are equivalent:\n\n    Web.interfaceBatch [ a, Web.interfaceNone, b ]\n\nand\n\n    Web.interfaceBatch\n        (List.filterMap identity\n            [ a |> Just, Nothing, b |> Just ]\n        )\n\n","type":"Web.Interface future_"},{"name":"keywordsSet","comment":" An [`Interface`](Web#Interface) for adding or replacing the document's keywords metadata\nwhich should consist of words relevant to the page's content\n","type":"List.List String.String -> Web.Interface future_"},{"name":"localStorageRemove","comment":" An [`Interface`](Web#Interface) for deleting the entry with the given key if it exists.\n\nNote: This will trigger an event for all other tabs of the same url origin\nthat can be listened to using [`localStorageRemoveOnADifferentTabListen`](#localStorageRemoveOnADifferentTabListen)\n\n","type":"String.String -> Web.Interface future_"},{"name":"localStorageRemoveOnADifferentTabListen","comment":" An [`Interface`](Web#Interface) for keeping an eye on\nwhen the local storage on a different tab with the same url origin is removed.\n","type":"String.String -> Web.Interface AppUrl.AppUrl"},{"name":"localStorageRequest","comment":" An [`Interface`](Web#Interface) for reading the value of the entry with the given key.\nComes back with `Nothing` if that key doesn't exist.\n\n    import Json.Decode\n    import Web\n\n    projectFromLocalStorageRequest : Web.Interface (Result String Project)\n    projectFromLocalStorageRequest =\n        Web.localStorageRequest \"project\"\n            |> Web.interfaceFutureMap\n                (\\savedProject ->\n                    case savedProject of\n                        Nothing ->\n                            \"nothing had been saved\" |> Err\n\n                        Just savedProjectJsonString ->\n                            savedProjectJsonString\n                                |> Json.Decode.decodeString projectJsonDecoder\n                                |> Result.mapError Json.Decode.errorToString\n                )\n\n","type":"String.String -> Web.Interface (Maybe.Maybe String.String)"},{"name":"localStorageSet","comment":" An [`Interface`](Web#Interface) for replacing the value of the entry with the given key\nor adding key and value as a new entry if the key doesn't exist.\n\n    import Json.Decode\n    import Web\n\n    projectSaveToLocalStorage : Project -> Web.Interface future_\n    projectSaveToLocalStorage =\n        \\project ->\n            Web.localStorageSet \"project\"\n                (project |> projectJsonEncode |> Json.Encode.encode 0)\n\nNote: This will trigger an event for all other tabs of the same url origin\nthat can be listened to using [`localStorageSetOnADifferentTabListen`](#localStorageSetOnADifferentTabListen)\n\n","type":"String.String -> String.String -> Web.Interface future_"},{"name":"localStorageSetOnADifferentTabListen","comment":" An [`Interface`](Web#Interface) for keeping an eye on\nwhen the local storage on a different tab with the same url origin is set.\n\nWhen the `oldValue` is `Nothing`, no entry with that key existed.\n\n","type":"String.String -> Web.Interface { appUrl : AppUrl.AppUrl, oldValue : Maybe.Maybe String.String, newValue : String.String }"},{"name":"navigateBack","comment":" An [`Interface`](Web#Interface) for going back a given number of pages.\n\nNote: You only manage the browser history that you created.\n\nReplacement for [`Browser.Navigation.back`](https://dark.elm.dmy.fr/packages/elm/browser/latest/Browser-Navigation#back)\n\n","type":"Basics.Int -> Web.Interface future_"},{"name":"navigateForward","comment":" An [`Interface`](Web#Interface) for going forward a given number of pages.\nIf there are no more pages in the future, this will do nothing.\n\nNote: You only manage the browser history that you created.\n\nReplacement for [`Browser.Navigation.forward`](https://dark.elm.dmy.fr/packages/elm/browser/latest/Browser-Navigation#forward)\n\n","type":"Basics.Int -> Web.Interface future_"},{"name":"navigateTo","comment":" An [`Interface`](Web#Interface) for leaving the current page and loading the given [URL](https://dark.elm.dmy.fr/packages/elm/url/latest/).\nThis always results in a page load, even if the provided URL is the same as the current one.\n\n    gotoElmWebsite : Web.Interface future_\n    gotoElmWebsite =\n        Web.navigateTo \"https://elm-lang.org/\"\n\nReplacement for [`Browser.Navigation.load`](https://dark.elm.dmy.fr/packages/elm/browser/latest/Browser-Navigation#load)\n\n","type":"String.String -> Web.Interface future_"},{"name":"navigationListen","comment":" If you used [`urlPush`](#urlPush) to update the URL with new history entries,\nwhen the user clicks ← or → buttons (or you call [`navigateForward`](#navigateForward) or [`navigateBack`](#navigateBack) yourself),\nthe URL will change but your UI will not.\n\n[`navigationListen`](#navigationListen) is an [`Interface`](Web#Interface) for detecting those URL changes and making ui changes as needed.\n\nWhen the app itself initiates a url change with [`urlPush`](#urlPush) or [`urlReplace`](#urlReplace), no such event is triggered.\n\nNote: This event is called [\"popstate\"](https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event) in js\n\n","type":"Web.Interface AppUrl.AppUrl"},{"name":"notificationAskForPermission","comment":" Ask the user to consent to receiving notifications, if they haven't already.\n\nFor security reasons, browsers require some kind of user interaction like a button click first.\nSo you could for example add a toggle to send notifications and ask only for permission\nwhen the toggle is set.\n\n","type":"Web.Interface future_"},{"name":"notificationShow","comment":" An [`Interface`](Web#Interface) for pushing a notification to the user.\nUsers can in the future [respond to it by clicking it](Web#NotificationClicked).\n\nTo not notify users multiple times for multiple messages of the same kind,\nchoose an `id` with a description for this kind\nso that the notification with the same kind gets silently replaced if it exists.\n\n    case messagesFromOpponent of\n        [] ->\n            Web.interfaceNone\n\n        [ onlyMessage ] ->\n            Web.notificationShow\n                { id = \"opponent messages\"\n                , message = \"Your opponent left a message\"\n                , details = \"\\\"\" ++ onlyMessage ++ \"\\\"\"\n                }\n\n        _ :: _ :: message2Up ->\n            Web.notificationShow\n                { id = \"opponent messages\"\n                , message =\n                    [ \"Your opponent left \"\n                    , 2 + (message2Up |> List.length) |> String.fromInt\n                    , \" messages\"\n                    ]\n                        |> String.join\n                , details = \"\"\n                }\n\nAnother example of a reminder shown in advance\n\n    let\n        untilMeeting : Duration\n        untilMeeting =\n            Duration.from meetingTime currentTime\n    in\n    if untilMeeting |> Quantity.isLessThan (Duration.minutes 10) then\n        Web.notificationShow\n            { id = \"time until meeting\"\n            , message =\n                [ \"the meeting starts in \"\n                , untilMeeting |> Duration.inMinutes |> Basics.ceiling |> String.fromInt)\n                , \" minutes\"\n                ]\n                    |> String.concat\n            , details = \"\"\n            }\n\n    else\n        Web.interfaceNone\n\nNote: If you haven't previously used [`notificationAskForPermission`](#notificationAskForPermission)\nit will request this permission now.\n\n","type":"{ message : String.String, details : String.String, id : String.String } -> Web.Interface Web.NotificationClicked"},{"name":"preferredLanguagesChangeListen","comment":" An [`Interface`](Web#Interface) for detecting changes to the languages the user prefers.\nEach described using language tags according to [RFC 5646: Tags for Identifying Languages (also known as BCP 47)](https://datatracker.ietf.org/doc/html/rfc5646).\nIn the returned list they are ordered by preference with the most preferred language first.\n\nNote: uses [`window.onlanguagechange`](https://developer.mozilla.org/en-US/docs/Web/API/Window/languagechange_event)\n\n","type":"Web.Interface (List.List String.String)"},{"name":"preferredLanguagesRequest","comment":" An [`Interface`](Web#Interface) for reading the languages the user prefers.\nEach described using language tags according to [RFC 5646: Tags for Identifying Languages (also known as BCP 47)](https://datatracker.ietf.org/doc/html/rfc5646).\nIn the returned list they are ordered by preference with the most preferred language first.\n\nNote: uses [`window.navigator.languages`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/languages)\n\n","type":"Web.Interface (List.List String.String)"},{"name":"program","comment":" Create a [`Program`](#Program):\n\n  - The state is everything the program knows (what The Elm Architecture calls model).\n    And it always starts with a given `initialState`\n\n  - The [`Interface`](#Interface) is the face to the outside world\n    and can be created using the helpers in [time](#time), [DOM](#dom), [HTTP](#http-client) etc.\n    The given `interface` function constructs these interfaces based on the current state\n\n  - Connections to and from js\n\n        port toJs : Json.Encode.Value -> Cmd event_\n\n        port fromJs : (Json.Encode.Value -> event) -> Sub event\n\n","type":"{ initialState : state, interface : state -> Web.Interface state, ports : { toJs : Json.Encode.Value -> Platform.Cmd.Cmd (Web.ProgramEvent state), fromJs : (Json.Encode.Value -> Web.ProgramEvent state) -> Platform.Sub.Sub (Web.ProgramEvent state) } } -> Web.Program state"},{"name":"programInit","comment":" The \"init\" part for an embedded program\n","type":"Web.ProgramConfig state -> ( Web.ProgramState state, Platform.Cmd.Cmd (Web.ProgramEvent state) )"},{"name":"programSubscriptions","comment":" The \"subscriptions\" part for an embedded program\n","type":"Web.ProgramConfig state -> Web.ProgramState state -> Platform.Sub.Sub (Web.ProgramEvent state)"},{"name":"programUpdate","comment":" The \"update\" part for an embedded program\n","type":"Web.ProgramConfig state -> Web.ProgramEvent state -> Web.ProgramState state -> ( Web.ProgramState state, Platform.Cmd.Cmd (Web.ProgramEvent state) )"},{"name":"randomUnsignedInt32sRequest","comment":" An [`Interface`](Web#Interface) for generating a given count of cryptographically sound unsigned 32-bit `Int`s.\nYou can use these in all kinds of packages that allow creating an initial seed\nfrom ints like [NoRedInk/elm-random-pcg-extended](https://dark.elm.dmy.fr/packages/NoRedInk/elm-random-pcg-extended/latest/Random-Pcg-Extended#initialSeed)\n\nNote: uses [`window.crypto.getRandomValues`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues)\n\n","type":"Basics.Int -> Web.Interface (List.List Basics.Int)"},{"name":"reload","comment":" An [`Interface`](Web#Interface) for reloading the current page.\nThis always results in a page load!\n\nNote: This may grab resources from the browser cache.\n\nReplacement for [`Browser.Navigation.reload`](https://dark.elm.dmy.fr/packages/elm/browser/latest/Browser-Navigation#reload)\n\n","type":"Web.Interface future_"},{"name":"socketListenAndMaybeSend","comment":" An [`Interface`](Web#Interface) for detecting when data has been sent from the server at a given address\nor the connection has changed, see [`Web.SocketEvent`](Web#SocketEvent).\n\nYou also have the ability to send data to the server whenever necessary.\nIt's common to pair this with [`Json.Encode.encode 0`](https://dark.elm.dmy.fr/packages/elm/json/latest/Json-Encode#encode)\nto send json.\n\n    case state.answer of\n        Nothing ->\n            Web.socketListenAndMaybeSend\n                { address = \"ws://127.0.0.1:9000\"\n                , dataToSend = Just \"Are you still on?\"\n                }\n                |> Web.interfaceFutureMap\n                    (\\socketEvent ->\n                        case socketEvent of\n                            Web.SocketDataReceived answer ->\n                                { state | answer = Just answer }\n\n                            Web.SocketOpened ->\n                                state\n\n                            Web.SocketClosed _ ->\n                                state\n                    )\n\n        Just _ ->\n            Web.domRender (Web.domText \"Just wanted to hear your voice, thx bye!\")\n\n\"Removing\" [`Web.socketListenAndMaybeSend`](#socketListenAndMaybeSend) from your interface will close the connection.\n\n","type":"{ address : String.String, dataToSend : Maybe.Maybe String.String } -> Web.Interface Web.SocketEvent"},{"name":"svgElement","comment":" Create an SVG [DOM node](#DomNode).\nwith a given tag, [`DomModifier`](Web#DomModifier)s and sub-nodes\n","type":"String.String -> List.List (Web.DomModifier future) -> List.List (Web.DomNode future) -> Web.DomNode future"},{"name":"timeOnceAt","comment":" An [`Interface`](Web#Interface) for getting a reminder\nonce a given [point in time](https://dark.elm.dmy.fr/packages/elm/time/latest/Time#Posix) has been reached.\n\nThis lets you for example wait until it's 15 minutes before the event,\ntimeout a request or schedule a certain action to a specific time.\n\n    import Web\n\n    type RequestState result\n        = NotAsked\n        | BeforeTimeout { start : Time.Posix }\n        | TimedOut\n        | GotResult result\n\n    { initialState = NotAsked\n    , interface =\n        \\state ->\n            [ case state of\n                NotAsked ->\n                    [ Web.timePosixRequest\n                        |> Web.interfaceFutureMap BeforeTimeout\n                    , ..request.. |> Web.futureMap GotResult\n                    ]\n\n                BeforeTimeout requestTime ->\n                    -- timeout after 10 seconds\n                    Web.timeOnceAt (Duration.addTo requestTime (Duration.seconds 10))\n                        |> Web.interfaceFutureMap (\\_ -> TimedOut)\n\n                TimedOut ->\n                    ...\n\n                GotResult result ->\n                    ...\n            ]\n    }\n\n  - 🧩 [`Duration` is from ianmackenzie/elm-units](https://dark.elm.dmy.fr/packages/ianmackenzie/elm-units/latest/Duration)\n\nYou can abstract this in various ways like adding a\n\n    withTimeout :\n        ..Request result..\n        -> Web.Interface (RequestState result)\n\nwhere the result can be put into the \"main state\" and therefore cased on.\n\n","type":"Time.Posix -> Web.Interface Time.Posix"},{"name":"timePeriodicallyListen","comment":" An [`Interface`](Web#Interface) for getting the current time\nevery time a given [`Duration`](https://dark.elm.dmy.fr/packages/ianmackenzie/elm-units/latest/Duration) has passed.\n\nNote: Do not use it for animations.\n[`Web.animationFrameListen`](Web#animationFrameListen)\nsyncs up with repaints and will end up being much smoother for any moving visuals.\n\n","type":"Duration.Duration -> Web.Interface Time.Posix"},{"name":"timePosixRequest","comment":" An [`Interface`](Web#Interface) for getting the current [POSIX time](https://dark.elm.dmy.fr/packages/elm/time/latest/Time#Posix).\n\nReplacement for [`elm/time`'s `Time.now`](https://dark.elm.dmy.fr/packages/elm/time/latest/Time#now).\n\n","type":"Web.Interface Time.Posix"},{"name":"timeZoneNameRequest","comment":" Intended for package authors.\nAn [`Interface`](Web#Interface) for using [`Intl.DateTimeFormat().resolvedOptions().timeZone`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/resolvedOptions#timezone)\nto get names like `Europe/Moscow` or `America/Havana`.\nFrom there you can look it up in any [IANA data](https://www.iana.org/time-zones) you loaded yourself.\n\nReplacement for [`elm/time`'s `Time.getZoneName`](https://package.elm-lang.org/packages/elm/time/latest/Time#getZoneName).\n\n","type":"Web.Interface String.String"},{"name":"timeZoneRequest","comment":" An [`Interface`](Web#Interface) for getting a [`Time.Zone`](https://dark.elm.dmy.fr/packages/elm/time/latest/Time#Zone)\nbased on the current UTC offset.\n\nReplacement for [`elm/time`'s `Time.here`](https://dark.elm.dmy.fr/packages/elm/time/latest/Time#here).\n\n","type":"Web.Interface Time.Zone"},{"name":"titleReplaceBy","comment":" An [`Interface`](Web#Interface) for setting the document's title\n","type":"String.String -> Web.Interface future_"},{"name":"urlPush","comment":" An [`Interface`](Web#Interface) for changing the [app-specific URL](https://dark.elm.dmy.fr/packages/lydell/elm-app-url/latest/)\nand adding a new entry to the browser history\nwithout triggering a page load.\n\nReplacement for [`Browser.Navigation.urlPush`](https://dark.elm.dmy.fr/packages/elm/browser/latest/Browser-Navigation#urlPush)\n\n","type":"AppUrl.AppUrl -> Web.Interface future_"},{"name":"urlReplace","comment":" An [`Interface`](Web#Interface) for changing the [app-specific URL](https://dark.elm.dmy.fr/packages/lydell/elm-app-url/latest/),\nwithout triggering a page load or adding a new entry to the browser history.\n\nThis can be useful if you have search box and you want the ?search=hats in the URL to match without adding a history entry for every single key stroke.\nImagine how annoying it would be to click back thirty times and still be on the same page!\n\nReplacement for [`Browser.Navigation.urlReplace`](https://dark.elm.dmy.fr/packages/elm/browser/latest/Browser-Navigation#urlReplace)\n\n","type":"AppUrl.AppUrl -> Web.Interface future_"},{"name":"urlRequest","comment":" An [`Interface`](Web#Interface) for getting the current page's [app-specific URL](https://dark.elm.dmy.fr/packages/lydell/elm-app-url/latest/).\nIs usually used while starting up the app.\n\nNote: Uses [`window.location.href`](https://developer.mozilla.org/en-US/docs/Web/API/Window/location)\n\n","type":"Web.Interface AppUrl.AppUrl"},{"name":"windowListenTo","comment":" An [`Interface`](Web#Interface) for detecting a specific [`window` event](https://developer.mozilla.org/en-US/docs/Web/API/Window#events)\n","type":"String.String -> Web.Interface Json.Decode.Value"},{"name":"windowSizeChangeListen","comment":" An [`Interface`](Web#Interface) for detecting changes to the inner window width and height\n","type":"Web.Interface { width : Basics.Int, height : Basics.Int }"},{"name":"windowSizeRequest","comment":" An [`Interface`](Web#Interface) for getting the inner window width and height in pixels,\nnot including toolbars/scrollbars\n","type":"Web.Interface { width : Basics.Int, height : Basics.Int }"},{"name":"windowVisibilityChangeListen","comment":" An [`Interface`](Web#Interface) for detecting changes to the [visibility to the user](Web#WindowVisibility)\n\nYou can use times where the page becomes hidden to for example pause a currently running game.\nThese times will also be the last reliable observation you can make before a user might close the page, so treat it as the likely end of the user's session\n\n","type":"Web.Interface Web.WindowVisibility"}],"binops":[]}]