diff options
| author | LLLL Colonq <llll@colonq> | 2025-04-15 21:10:01 -0400 |
|---|---|---|
| committer | LLLL Colonq <llll@colonq> | 2025-04-15 21:10:01 -0400 |
| commit | b31fd7881f8da11dbdd1cc259907b535d96a47a4 (patch) | |
| tree | 1829fe570e53a0876990c6056da6f71880b65493 | |
| parent | bd1256504b6154c7d9b8feb0fefb6755b11586ee (diff) | |
Add gizmo page
| -rw-r--r-- | main.css | 36 | ||||
| -rw-r--r-- | src/Main.purs | 2 | ||||
| -rw-r--r-- | src/Main/Gizmo.purs | 34 | ||||
| -rw-r--r-- | src/UI.js | 26 | ||||
| -rw-r--r-- | src/UI.purs | 12 | ||||
| -rw-r--r-- | src/Utils.purs | 12 | ||||
| -rw-r--r-- | templates/api/gizmo.html | 26 |
7 files changed, 148 insertions, 0 deletions
@@ -571,3 +571,39 @@ a.lcolonq-button-link :active { #lcolonq-throwshade-current { margin: 0.5rem; } + +/* gizmo */ +#lcolonq-gizmo-body { + width: 100vw; + height: 100vh; +} +#lcolonq-gizmo { + width: 100%; + height: 100%; +} +#lcolonq-gizmo-top { + position: absolute; + opacity: 0; + transition: opacity 0.2s ease-in-out; + right: 0px; + top: 0px; + color: white; + background-color: black; + padding-top: 1rem; + padding-left: 1rem; + padding-right: 1rem; + padding-bottom: 1rem; +} +#lcolonq-gizmo-top:hover { + opacity: 1; +} +#lcolonq-gizmo-bottom { + background-color: white; + width: 100%; + height: 100%; +} +#lcolonq-gizmo-contents { + border: none; + width: 100%; + height: 100%; +} diff --git a/src/Main.purs b/src/Main.purs index 34ca80b..ab98c40 100644 --- a/src/Main.purs +++ b/src/Main.purs @@ -9,6 +9,7 @@ import Main.API as API import Main.Auth as Auth import Main.Button as Button import Main.Extension as Extension +import Main.Gizmo as Gizmo import Main.Greencircle as Greencircle import Main.Menu as Menu import Main.OBS as OBS @@ -28,4 +29,5 @@ main = case Config.mode of "auth" -> Auth.main "greencircle" -> Greencircle.main "throwshade" -> Throwshade.main + "gizmo" -> Gizmo.main _ -> throw $ "unknown mode: " <> Config.mode diff --git a/src/Main/Gizmo.purs b/src/Main/Gizmo.purs new file mode 100644 index 0000000..e728e66 --- /dev/null +++ b/src/Main/Gizmo.purs @@ -0,0 +1,34 @@ +module Main.Gizmo where + +import Prelude + +import Config as Config +import Data.Foldable (for_) +import Data.String (Pattern(..), split) +import Data.String as String +import Effect (Effect) +import Effect.Aff (launchAff_) +import Effect.Class (class MonadEffect, liftEffect) +import Effect.Console (log) +import Fetch (fetch) +import UI (addOption, onInput, startBufferRefresh) +import Utils (byId, getSelectValue, setIFrameSrc) + +loadBuffer :: forall m. MonadEffect m => String -> m Unit +loadBuffer nm = do + contents <- byId "lcolonq-gizmo-contents" + setIFrameSrc (Config.apiServer <> "/gizmo?buf=" <> nm) contents + +main :: Effect Unit +main = launchAff_ do + liftEffect $ log "hello from gizmo" + select <- byId "lcolonq-gizmo-select" + onInput select \d -> loadBuffer d + { text: text } <- fetch (Config.apiServer <> "/gizmo/list") {} + glist <- text + for_ (split (Pattern "\n") glist) \buf -> do + when (not $ String.null buf) $ addOption buf select + initial <- getSelectValue =<< byId "lcolonq-gizmo-select" + loadBuffer initial + contents <- byId "lcolonq-gizmo-contents" + startBufferRefresh contents @@ -36,3 +36,29 @@ export const _submitShader = (url) => (shader) => () => { body: data, }); }; + +export const _addOption = (nm) => (el) => () => { + const opt = document.createElement("option"); + opt.value = nm + opt.innerHTML = nm; + el.appendChild(opt); +}; + +export const _onInput = (el) => (f) => () => { + el.addEventListener("input", (ev) => { + f(ev.target.value)(); + }); +}; + +export const _startBufferRefresh = (url) => (iframe) => () => { + const socket = new WebSocket(url); + socket.addEventListener("open", (ev) => { + console.log("connected"); + }); + const select = document.getElementById("lcolonq-gizmo-select"); + socket.addEventListener("message", async (ev) => { + if (select.value == ev.data) { + iframe.src = iframe.src; + } + }); +}; diff --git a/src/UI.purs b/src/UI.purs index d5913b7..645c346 100644 --- a/src/UI.purs +++ b/src/UI.purs @@ -37,3 +37,15 @@ setShader s = liftEffect $ _setShader s foreign import _submitShader :: String -> String -> Effect Unit submitShader :: forall m. MonadEffect m => String -> m Unit submitShader el = liftEffect $ _submitShader (Config.secureApiServer <> "/redeem") el + +foreign import _addOption :: String -> DOM.El.Element -> Effect Unit +addOption :: forall m. MonadEffect m => String -> DOM.El.Element -> m Unit +addOption o el = liftEffect $ _addOption o el + +foreign import _onInput :: DOM.El.Element -> (String -> Effect Unit) -> Effect Unit +onInput :: forall m. MonadEffect m => DOM.El.Element -> (String -> Effect Unit) -> m Unit +onInput el f = liftEffect $ _onInput el f + +foreign import _startBufferRefresh :: String -> DOM.El.Element -> Effect Unit +startBufferRefresh :: forall m. MonadEffect m => DOM.El.Element -> m Unit +startBufferRefresh el = liftEffect $ _startBufferRefresh (Config.apiServer <> "/gizmo/events") el diff --git a/src/Utils.purs b/src/Utils.purs index 7be7be8..cee8f70 100644 --- a/src/Utils.purs +++ b/src/Utils.purs @@ -26,7 +26,9 @@ import Web.Event.EventTarget as Ev.Tar import Web.HTML as HTML import Web.HTML.HTMLDocument as HTML.Doc import Web.HTML.HTMLInputElement as HTML.Input +import Web.HTML.HTMLSelectElement as HTML.Select import Web.HTML.HTMLTextAreaElement as HTML.TextArea +import Web.HTML.HTMLIFrameElement as HTML.IFrame import Web.HTML.Window as HTML.Win maybeToArray :: forall a. Maybe a -> Array a @@ -102,6 +104,11 @@ getValue e = case HTML.Input.fromElement e of Just inp -> liftEffect $ HTML.Input.value inp Nothing -> liftEffect $ throw "element is not an input" +getSelectValue :: forall m. MonadEffect m => DOM.Element -> m String +getSelectValue e = case HTML.Select.fromElement e of + Just inp -> liftEffect $ HTML.Select.value inp + Nothing -> liftEffect $ throw "element is not a select" + addClass :: forall m. MonadEffect m => String -> DOM.Element -> m Unit addClass c e = do cl <- liftEffect $ DOM.El.classList e @@ -120,6 +127,11 @@ toggleClass c e = do _ <- liftEffect $ DOM.DTL.toggle cl c pure unit +setIFrameSrc :: forall m. MonadEffect m => String -> DOM.Element -> m Unit +setIFrameSrc src e = case HTML.IFrame.fromElement e of + Just iframe -> liftEffect $ HTML.IFrame.setSrc src iframe + Nothing -> liftEffect $ throw "element is not an iframe" + checkAuth :: AuthInfo -> Aff String checkAuth auth = do { text: resp } <- diff --git a/templates/api/gizmo.html b/templates/api/gizmo.html new file mode 100644 index 0000000..665f44f --- /dev/null +++ b/templates/api/gizmo.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> + <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> + <title>LCOLONQ Gizmo Extravaganza FES</title> + <link rel="icon" href="./assets/mrgreen.png"> + <link rel="stylesheet" type="text/css" href="./main.css"> + <script type="module"> +CONFIG_SUBST + globalThis.mode = "gizmo"; + </script> + <script type="module" src="./main.js"></script> + </head> + <body id="lcolonq-gizmo-body"> + <div id="lcolonq-gizmo"> + <div id="lcolonq-gizmo-top"> + gizmo selection + <select name="gizmo" id="lcolonq-gizmo-select"></select> + </div> + <div id="lcolonq-gizmo-bottom"> + <iframe id="lcolonq-gizmo-contents"></iframe> + </div> + </div> + </body> +</html> |
