diff options
Diffstat (limited to 'fig-frontend-client')
| -rw-r--r-- | fig-frontend-client/index-template.html | 2 | ||||
| -rw-r--r-- | fig-frontend-client/main.css | 14 | ||||
| -rw-r--r-- | fig-frontend-client/main.js | 144 | ||||
| -rw-r--r-- | fig-frontend-client/src/Main.purs | 16 | ||||
| -rw-r--r-- | fig-frontend-client/src/Model.js | 108 | ||||
| -rw-r--r-- | fig-frontend-client/src/Model.purs | 10 |
6 files changed, 266 insertions, 28 deletions
diff --git a/fig-frontend-client/index-template.html b/fig-frontend-client/index-template.html index f306205..f793ddd 100644 --- a/fig-frontend-client/index-template.html +++ b/fig-frontend-client/index-template.html @@ -13,6 +13,7 @@ CONFIG_SUBST </head> <body> <!-- RINGBEARER --> + <canvas id="lcolonq-canvas"></canvas> <div id="lcolonq-title"> <span class="lcolonq-letter" id="lcolonq-letter-0">L</span><span class="lcolonq-letter" id="lcolonq-letter-1">C</span><span class="lcolonq-letter" id="lcolonq-letter-2">O</span><span class="lcolonq-letter" id="lcolonq-letter-3">L</span><span class="lcolonq-letter" id="lcolonq-letter-4">O</span><span class="lcolonq-letter" id="lcolonq-letter-5">N</span><span class="lcolonq-letter" id="lcolonq-letter-6">Q</span> <div id="lcolonq-subtitle"> @@ -35,6 +36,5 @@ CONFIG_SUBST <audio id="lcolonq-audio-voice-4" src="assets/voice/4.wav"></audio> <audio id="lcolonq-audio-voice-5" src="assets/voice/5.wav"></audio> <audio id="lcolonq-audio-voice-6" src="assets/voice/6.wav"></audio> - <!-- <canvas id="lcolonq-canvas"></canvas> --> </body> </html> diff --git a/fig-frontend-client/main.css b/fig-frontend-client/main.css index 22368c3..3305e19 100644 --- a/fig-frontend-client/main.css +++ b/fig-frontend-client/main.css @@ -50,6 +50,8 @@ body { #lcolonq-subtitle { font-size: 5vw; margin-top: -5vw; + margin-right: 12vw; + text-align: right; } #lcolonq-header { @@ -98,16 +100,10 @@ body { #lcolonq-canvas { position: absolute; - top: 0px; - bottom: 0px; + bottom: 2rem; left: 0px; - right: 0px; - width: 100%; - height: 100%; -} - -button { - color: red; + width: 832px; + height: 832px; } .right { diff --git a/fig-frontend-client/main.js b/fig-frontend-client/main.js index 38024ce..67e726e 100644 --- a/fig-frontend-client/main.js +++ b/fig-frontend-client/main.js @@ -2679,6 +2679,112 @@ var fetch2 = function() { }; }; +// output/Model/foreign.js +var canvas = document.getElementById("lcolonq-canvas"); +var socket = new WebSocket("wss://colonq.computer/bullfrog/api/channel/listen/model"); +var currentFrame = null; +socket.addEventListener("open", (ev) => { + console.log("connected"); +}); +async function decompress(blob3) { + let ds = new DecompressionStream("gzip"); + let stream = blob3.stream(); + let out = await new Response(stream.pipeThrough(ds)); + return out.arrayBuffer(); +} +function readCell(dv, base) { + let cell = {}; + let o = base; + if (dv.getUint8(o) == 0) { + return [{ type: "bg" }, o + 1]; + } else { + cell.type = "fg"; + cell.custom = dv.getUint8(o + 1); + cell.r = dv.getUint8(o + 2); + cell.g = dv.getUint8(o + 3); + cell.b = dv.getUint8(o + 4); + cell.g0 = dv.getUint32(o + 5); + if (dv.getUint8(o + 9) == 0) { + return [cell, o + 10]; + } else { + cell.g1 = dv.getUint32(o + 10); + return [cell, o + 14]; + } + } +} +function readKeyframe(dv, base) { + let ret = []; + let o = base; + for (let idx = 0; idx < 64 * 64; ++idx) { + let res = readCell(dv, o); + ret.push(res[0]); + o = res[1]; + } + currentFrame = ret; +} +function readDiff(dv, base) { + if (currentFrame) { + let len = dv.getUint32(base); + let o = base + 4; + for (let idx = 0; idx < len; ++idx) { + let x = dv.getUint8(o); + let y = dv.getUint8(o + 1); + let c = readCell(dv, o + 2); + currentFrame[x + y * 64] = c[0]; + o = c[1]; + } + } +} +function readPacket(dv) { + if (dv.getUint8(0) == 0) { + readKeyframe(dv, 1); + } else { + readDiff(dv, 1); + } +} +function renderCellCanvas(ctx2, x, y, c) { + if (c) { + let msg = c.g1 ? String.fromCodePoint(c.g0, c.g1) : String.fromCodePoint(c.g0); + if (msg.trim().length) { + ctx2.fillStyle = "black"; + ctx2.fillRect(13 * y, 13 * x, 13, 13); + ctx2.fillStyle = `rgba(${c.r}, ${c.g}, ${c.b}, 1.0)`; + ctx2.fillText(msg, 13 * y, 13 * x + 10); + } + } +} +function renderCanvas() { + if (canvas.width != canvas.clientWidth) { + canvas.width = canvas.clientWidth; + } + if (canvas.height != canvas.clientHeight) { + canvas.height = canvas.clientHeight; + } + if (currentFrame) { + let ctx2 = canvas.getContext("2d"); + ctx2.clearRect(0, 0, canvas.width, canvas.height); + ctx2.font = "12px Iosevka Comfy"; + for (let y = 0; y < 64; ++y) { + for (let x = 0; x < 64; ++x) { + renderCellCanvas(ctx2, x, y, currentFrame[x * 64 + y]); + } + } + } +} +var _startModel = () => { + socket.addEventListener("message", async (ev) => { + let arr = await decompress(ev.data); + let view = new DataView(arr); + readPacket(view); + renderCanvas(); + }); +}; + +// output/Model/index.js +var startModel = function(dictMonadEffect) { + return liftEffect(dictMonadEffect)(_startModel); +}; + // output/Web.DOM.Document/foreign.js var getEffProp = function(name15) { return function(doc) { @@ -2819,9 +2925,10 @@ function document2(window2) { // output/Main/index.js var map6 = /* @__PURE__ */ map(functorEffect); -var discard2 = /* @__PURE__ */ discard(discardUnit)(bindAff); var bind4 = /* @__PURE__ */ bind(bindAff); var fetch3 = /* @__PURE__ */ fetch2()()(/* @__PURE__ */ toCoreRequestOptionsRowRo()()(toCoreRequestOptionsHelpe)); +var discard2 = /* @__PURE__ */ discard(discardUnit)(bindAff); +var startModel2 = /* @__PURE__ */ startModel(monadEffectAff); var for_2 = /* @__PURE__ */ for_(applicativeAff)(foldableArray); var show2 = /* @__PURE__ */ show(showInt); var playVoice2 = /* @__PURE__ */ playVoice(monadEffectEffect); @@ -2872,20 +2979,29 @@ var byId = function(dictMonadEffect) { }; }; var byId1 = /* @__PURE__ */ byId(monadEffectAff); +var updateSubtitle = /* @__PURE__ */ bind4(/* @__PURE__ */ byId1("lcolonq-subtitle"))(function(subtitle) { + return bind4(fetch3(apiServer + "/catchphrase")({}))(function(v) { + return bind4(v.text)(setText1(subtitle)); + }); +}); var main = /* @__PURE__ */ launchAff_(/* @__PURE__ */ discard2(/* @__PURE__ */ liftEffect(monadEffectAff)(/* @__PURE__ */ log("hi")))(function() { - return bind4(byId1("lcolonq-marquee"))(function(marq) { - return bind4(fetch3(apiServer + "/motd")({}))(function(v) { - return discard2(bind4(v.text)(setText1(marq)))(function() { - return bind4(byId1("lcolonq-subtitle"))(function(subtitle) { - return bind4(fetch3(apiServer + "/catchphrase")({}))(function(v1) { - return discard2(bind4(v1.text)(setText1(subtitle)))(function() { - return for_2(range2(0)(6))(function(i) { - return bind4(byId1("lcolonq-letter-" + show2(i)))(function(letter) { - return discard2(listen1(letter)("click")(function(_ev) { - return playVoice2(true)(i); - }))(function() { - return listen1(letter)("mouseover")(function(_ev) { - return playVoice2(false)(i); + return discard2(startModel2)(function() { + return bind4(byId1("lcolonq-marquee"))(function(marq) { + return bind4(fetch3(apiServer + "/motd")({}))(function(v) { + return discard2(bind4(v.text)(setText1(marq)))(function() { + return discard2(updateSubtitle)(function() { + return bind4(byId1("lcolonq-subtitle"))(function(subtitle) { + return discard2(listen1(subtitle)("click")(function(_ev) { + return launchAff_(updateSubtitle); + }))(function() { + return for_2(range2(0)(6))(function(i) { + return bind4(byId1("lcolonq-letter-" + show2(i)))(function(letter) { + return discard2(listen1(letter)("click")(function(_ev) { + return playVoice2(true)(i); + }))(function() { + return listen1(letter)("mouseover")(function(_ev) { + return playVoice2(false)(i); + }); }); }); }); diff --git a/fig-frontend-client/src/Main.purs b/fig-frontend-client/src/Main.purs index 2a73c4f..cb06b86 100644 --- a/fig-frontend-client/src/Main.purs +++ b/fig-frontend-client/src/Main.purs @@ -2,9 +2,8 @@ module Main where import Prelude -import Config as Config import Audio as Audio - +import Config as Config import Data.Array as Array import Data.Maybe (Maybe(..)) import Data.Traversable (for, for_) @@ -14,6 +13,7 @@ import Effect.Class (class MonadEffect, liftEffect) import Effect.Console (log) import Effect.Exception (throw) import Fetch (fetch) +import Model (startModel) import Web.DOM as DOM import Web.DOM.Document as DOM.Doc import Web.DOM.Element as DOM.El @@ -41,16 +41,24 @@ listen e ev f = do setText :: forall m. MonadEffect m => DOM.Element -> String -> m Unit setText e s = liftEffect $ DOM.Node.setTextContent s $ DOM.El.toNode e +updateSubtitle :: Aff Unit +updateSubtitle = do + subtitle <- byId "lcolonq-subtitle" + { text: catchphrase } <- fetch (Config.apiServer <> "/catchphrase") {} + catchphrase >>= setText subtitle + main :: Effect Unit main = launchAff_ do liftEffect $ log "hi" + startModel marq <- byId "lcolonq-marquee" { text: motd } <- fetch (Config.apiServer <> "/motd") {} motd >>= setText marq + updateSubtitle subtitle <- byId "lcolonq-subtitle" - { text: catchphrase } <- fetch (Config.apiServer <> "/catchphrase") {} - catchphrase >>= setText subtitle + listen subtitle "click" \_ev -> do + launchAff_ updateSubtitle for_ (Array.range 0 6) \i -> do letter <- byId $ "lcolonq-letter-" <> show i diff --git a/fig-frontend-client/src/Model.js b/fig-frontend-client/src/Model.js new file mode 100644 index 0000000..299c645 --- /dev/null +++ b/fig-frontend-client/src/Model.js @@ -0,0 +1,108 @@ +let canvas = document.getElementById("lcolonq-canvas"); +let socket = new WebSocket("wss://colonq.computer/bullfrog/api/channel/listen/model"); +let currentFrame = null; + +socket.addEventListener("open", (ev) => { + console.log("connected"); +}); + +async function decompress(blob) { + let ds = new DecompressionStream("gzip"); + let stream = blob.stream(); + let out = await new Response(stream.pipeThrough(ds)); + return out.arrayBuffer(); +} + +function readCell(dv, base) { + let cell = {}; + let o = base; + if (dv.getUint8(o) == 0) { + return [{type: "bg"}, o + 1]; + } else { + cell.type = "fg"; + cell.custom = dv.getUint8(o + 1); + cell.r = dv.getUint8(o + 2); + cell.g = dv.getUint8(o + 3); + cell.b = dv.getUint8(o + 4); + cell.g0 = dv.getUint32(o + 5); + if (dv.getUint8(o + 9) == 0) { + return [cell, o + 10]; + } else { + cell.g1 = dv.getUint32(o + 10); + return [cell, o + 14]; + } + } +} + +function readKeyframe(dv, base) { + let ret = []; + let o = base; + for (let idx = 0; idx < (64 * 64); ++idx) { + let res = readCell(dv, o); + ret.push(res[0]); + o = res[1]; + } + currentFrame = ret; +} + +function readDiff(dv, base) { + if (currentFrame) { + let len = dv.getUint32(base); + let o = base + 4; + for (let idx = 0; idx < len; ++idx) { + let x = dv.getUint8(o); + let y = dv.getUint8(o + 1); + let c = readCell(dv, o + 2); + currentFrame[x + (y * 64)] = c[0]; + o = c[1]; + } + } +} + +function readPacket(dv) { + if (dv.getUint8(0) == 0) { + readKeyframe(dv, 1); + } else { + readDiff(dv, 1); + } +} + +function renderCellCanvas(ctx, x, y, c) { + if (c) { + let msg = c.g1 ? String.fromCodePoint(c.g0, c.g1) : String.fromCodePoint(c.g0); + if (msg.trim().length) { + ctx.fillStyle = "black"; + ctx.fillRect(13 * y, 13 * x, 13, 13); + ctx.fillStyle = `rgba(${c.r}, ${c.g}, ${c.b}, 1.0)`; + ctx.fillText(msg, 13 * y, 13 * x + 10); + } + } +} + +function renderCanvas() { + if (canvas.width != canvas.clientWidth) { + canvas.width = canvas.clientWidth; + } + if (canvas.height != canvas.clientHeight) { + canvas.height = canvas.clientHeight; + } + if (currentFrame) { + let ctx = canvas.getContext("2d"); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.font = "12px Iosevka Comfy"; + for (let y = 0; y < 64; ++y) { + for (let x = 0; x < 64; ++x) { + renderCellCanvas(ctx, x, y, currentFrame[(x * 64) + y]); + } + } + } +} + +export const _startModel = () => { + socket.addEventListener("message", async (ev) => { + let arr = await decompress(ev.data); + let view = new DataView(arr); + readPacket(view); + renderCanvas(); + }); +}; diff --git a/fig-frontend-client/src/Model.purs b/fig-frontend-client/src/Model.purs new file mode 100644 index 0000000..a9f2888 --- /dev/null +++ b/fig-frontend-client/src/Model.purs @@ -0,0 +1,10 @@ +module Model where + +import Prelude + +import Effect (Effect) +import Effect.Class (class MonadEffect, liftEffect) + +foreign import _startModel :: Effect Unit +startModel :: forall m. MonadEffect m => m Unit +startModel = liftEffect _startModel |
