summaryrefslogtreecommitdiff
path: root/fig-frontend-client
diff options
context:
space:
mode:
Diffstat (limited to 'fig-frontend-client')
-rw-r--r--fig-frontend-client/.gitignore2
-rw-r--r--fig-frontend-client/.psc-ide-port1
-rw-r--r--fig-frontend-client/Makefile35
-rw-r--r--fig-frontend-client/assets/icon.pngbin0 -> 983 bytes
-rw-r--r--fig-frontend-client/assets/mrblue.pngbin0 -> 771 bytes
-rw-r--r--fig-frontend-client/config-deploy.m44
-rw-r--r--fig-frontend-client/config-test.m44
-rwxr-xr-xfig-frontend-client/deploy.sh3
-rw-r--r--fig-frontend-client/extension/background.js23
-rw-r--r--fig-frontend-client/extension/manifest.dhall30
-rw-r--r--fig-frontend-client/index-template.html (renamed from fig-frontend-client/index.html)4
-rw-r--r--fig-frontend-client/main.js266
-rw-r--r--fig-frontend-client/src/Config.js1
-rw-r--r--fig-frontend-client/src/Config.purs3
-rw-r--r--fig-frontend-client/src/Main.purs164
15 files changed, 301 insertions, 239 deletions
diff --git a/fig-frontend-client/.gitignore b/fig-frontend-client/.gitignore
new file mode 100644
index 0000000..b74c9c8
--- /dev/null
+++ b/fig-frontend-client/.gitignore
@@ -0,0 +1,2 @@
+dist/*
+main.js \ No newline at end of file
diff --git a/fig-frontend-client/.psc-ide-port b/fig-frontend-client/.psc-ide-port
deleted file mode 100644
index 48a9dfe..0000000
--- a/fig-frontend-client/.psc-ide-port
+++ /dev/null
@@ -1 +0,0 @@
-15776 \ No newline at end of file
diff --git a/fig-frontend-client/Makefile b/fig-frontend-client/Makefile
new file mode 100644
index 0000000..872bec7
--- /dev/null
+++ b/fig-frontend-client/Makefile
@@ -0,0 +1,35 @@
+.PHONY: all deploy extension
+
+all: dist/test/index.html dist/test/assets dist/test/main.js dist/test/main.css
+
+deploy: dist/deploy/index.html dist/deploy/assets dist/deploy/main.js dist/deploy/main.css
+ rsync -av dist/deploy/ "pub.colonq.computer:~/public_html/"
+
+dist:
+ mkdir -p dist/test
+ mkdir -p dist/deploy
+
+main.js: $(shell find src)
+ purs-nix bundle
+
+dist/%/assets: $(shell find assets) dist
+ rm -rf $@
+ mkdir -p $@
+ cp -r assets/* $@
+
+dist/%/main.js: main.js dist
+ cp $< $@
+
+dist/%/main.css: main.css dist
+ cp $< $@
+
+dist/%/index.html: config-%.m4 dist index-template.html
+ sh -c "m4 $< >$@"
+
+extension: dist/extension/assets dist/extension/manifest.json dist/extension/background.js dist/extension/main.js dist/extension/main.css
+
+dist/extension/manifest.json: extension/manifest.dhall
+ dhall-to-json <$< >$@
+
+dist/extension/%: extension/%
+ cp $< $@
diff --git a/fig-frontend-client/assets/icon.png b/fig-frontend-client/assets/icon.png
new file mode 100644
index 0000000..0431e3f
--- /dev/null
+++ b/fig-frontend-client/assets/icon.png
Binary files differ
diff --git a/fig-frontend-client/assets/mrblue.png b/fig-frontend-client/assets/mrblue.png
new file mode 100644
index 0000000..8cf2a0b
--- /dev/null
+++ b/fig-frontend-client/assets/mrblue.png
Binary files differ
diff --git a/fig-frontend-client/config-deploy.m4 b/fig-frontend-client/config-deploy.m4
new file mode 100644
index 0000000..504bb3e
--- /dev/null
+++ b/fig-frontend-client/config-deploy.m4
@@ -0,0 +1,4 @@
+define(`CONFIG_SUBST', `
+globalThis.apiServer = "https://api.colonq.computer/api";
+')
+include(`index-template.html')
diff --git a/fig-frontend-client/config-test.m4 b/fig-frontend-client/config-test.m4
new file mode 100644
index 0000000..f1e6c16
--- /dev/null
+++ b/fig-frontend-client/config-test.m4
@@ -0,0 +1,4 @@
+define(`CONFIG_SUBST', `
+globalThis.apiServer = "http://localhost:8000/api";
+')
+include(`index-template.html')
diff --git a/fig-frontend-client/deploy.sh b/fig-frontend-client/deploy.sh
deleted file mode 100755
index f68b833..0000000
--- a/fig-frontend-client/deploy.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-purs-nix bundle
-scp -r main.css main.js index.html assets/ pub.colonq.computer:~/public_html
diff --git a/fig-frontend-client/extension/background.js b/fig-frontend-client/extension/background.js
new file mode 100644
index 0000000..a0f4c91
--- /dev/null
+++ b/fig-frontend-client/extension/background.js
@@ -0,0 +1,23 @@
+/* Retrieve any previously set cookie and send to content script */
+
+function getActiveTab() {
+ return browser.tabs.query({active: true, currentWindow: true});
+}
+
+function cookieUpdate() {
+ getActiveTab().then((tabs) => {
+ // get any previously set cookie for the current tab
+ let gettingCookies = browser.cookies.get({
+ url: tabs[0].url,
+ name: "name"
+ });
+ gettingCookies.then((cookie) => {
+ browser.tabs.sendMessage(tabs[0].id, cookie.value);
+ });
+ });
+}
+
+// update when the tab is updated
+browser.tabs.onUpdated.addListener(cookieUpdate);
+// update when the tab is activated
+browser.tabs.onActivated.addListener(cookieUpdate);
diff --git a/fig-frontend-client/extension/manifest.dhall b/fig-frontend-client/extension/manifest.dhall
new file mode 100644
index 0000000..87c48f6
--- /dev/null
+++ b/fig-frontend-client/extension/manifest.dhall
@@ -0,0 +1,30 @@
+{ manifest_version = 2
+, name = "computerspotting"
+, version = "1.0"
+, description = "spot the computer"
+, icons =
+ [ { mapKey = "48", mapValue = "assets/mrgreen.png" }
+ ]
+, web_accessible_resources =
+ [ "mrgreen.png"
+ , "mrblue.png"
+ ]
+, permissions =
+ [ "webRequest"
+ , "webRequestBlocking"
+ , "tabs"
+ , "cookies"
+ , "*://*.twitch.tv/*"
+ , "*://api.colonq.computer/*"
+ ]
+, background =
+ { scripts = ["background.js"]
+ }
+, content_scripts =
+ [ { matches = ["*://*.twitch.tv/*"]
+ , js = ["main.js"]
+ , css = ["main.css"]
+ , run_at = "document_end"
+ }
+ ]
+} \ No newline at end of file
diff --git a/fig-frontend-client/index.html b/fig-frontend-client/index-template.html
index ef70156..1ab53d8 100644
--- a/fig-frontend-client/index.html
+++ b/fig-frontend-client/index-template.html
@@ -6,9 +6,13 @@
<title>LCOLONQ</title>
<link rel="icon" href="./assets/mrgreen.png">
<link rel="stylesheet" type="text/css" href="./main.css">
+ <script type="module">
+CONFIG_SUBST
+ </script>
<script type="module" src="./main.js"></script>
</head>
<body>
+ <!-- RINGBEARER -->
<div id="lcolonq-title">
LCOLONQ
<div id="lcolonq-subtitle">hi</div>
diff --git a/fig-frontend-client/main.js b/fig-frontend-client/main.js
index 5cf9335..70827fc 100644
--- a/fig-frontend-client/main.js
+++ b/fig-frontend-client/main.js
@@ -1,3 +1,6 @@
+// output/Config/foreign.js
+var apiServer = globalThis.apiServer;
+
// output/Control.Semigroupoid/index.js
var semigroupoidFn = {
compose: function(f) {
@@ -1465,50 +1468,6 @@ var unionWith = function(dictOrd) {
var union = function(dictOrd) {
return unionWith(dictOrd)($$const);
};
-var member = function(dictOrd) {
- var compare2 = compare(dictOrd);
- return function(k) {
- var go2 = function($copy_v) {
- var $tco_done = false;
- var $tco_result;
- function $tco_loop(v) {
- if (v instanceof Leaf) {
- $tco_done = true;
- return false;
- }
- ;
- if (v instanceof Node) {
- var v1 = compare2(k)(v.value2);
- if (v1 instanceof LT) {
- $copy_v = v.value4;
- return;
- }
- ;
- if (v1 instanceof GT) {
- $copy_v = v.value5;
- return;
- }
- ;
- if (v1 instanceof EQ) {
- $tco_done = true;
- return true;
- }
- ;
- throw new Error("Failed pattern match at Data.Map.Internal (line 457, column 7 - line 460, column 19): " + [v1.constructor.name]);
- }
- ;
- throw new Error("Failed pattern match at Data.Map.Internal (line 454, column 8 - line 460, column 19): " + [v.constructor.name]);
- }
- ;
- while (!$tco_done) {
- $tco_result = $tco_loop($copy_v);
- }
- ;
- return $tco_result;
- };
- return go2;
- };
-};
var lookup = function(dictOrd) {
var compare2 = compare(dictOrd);
return function(k) {
@@ -1798,20 +1757,6 @@ function setCanvasHeight(canvas) {
};
};
}
-function setFillStyle(ctx) {
- return function(style) {
- return function() {
- ctx.fillStyle = style;
- };
- };
-}
-function fillRect(ctx) {
- return function(r) {
- return function() {
- ctx.fillRect(r.x, r.y, r.width, r.height);
- };
- };
-}
function clearRect(ctx) {
return function(r) {
return function() {
@@ -1826,17 +1771,6 @@ function setFont(ctx) {
};
};
}
-function fillText(ctx) {
- return function(text5) {
- return function(x) {
- return function(y) {
- return function() {
- ctx.fillText(text5, x, y);
- };
- };
- };
- };
-}
// output/Graphics.Canvas/index.js
var applySecond2 = /* @__PURE__ */ applySecond(applyEffect);
@@ -1991,16 +1925,15 @@ var map1 = /* @__PURE__ */ map(functorArray);
var union2 = /* @__PURE__ */ union(ordRecord2);
var append3 = /* @__PURE__ */ append(semigroupArray);
var lookup2 = /* @__PURE__ */ lookup(ordRecord2);
-var member2 = /* @__PURE__ */ member(ordRecord2);
-var $$for2 = /* @__PURE__ */ $$for(applicativeEffect)(traversableArray);
+var when2 = /* @__PURE__ */ when(applicativeEffect);
var discard22 = /* @__PURE__ */ discard2(/* @__PURE__ */ bindStateT(monadEffect));
var monadStateStateT2 = /* @__PURE__ */ monadStateStateT(monadEffect);
var monadEffectState2 = /* @__PURE__ */ monadEffectState(monadEffectEffect);
var applicativeStateT2 = /* @__PURE__ */ applicativeStateT(monadEffect);
var pure1 = /* @__PURE__ */ pure(applicativeStateT2);
-var when2 = /* @__PURE__ */ when(applicativeStateT2);
+var when1 = /* @__PURE__ */ when(applicativeStateT2);
var void1 = /* @__PURE__ */ $$void(/* @__PURE__ */ functorStateT(functorEffect));
-var for1 = /* @__PURE__ */ $$for(applicativeStateT2)(traversableArray);
+var $$for2 = /* @__PURE__ */ $$for(applicativeStateT2)(traversableArray);
var GfxWhiteout = /* @__PURE__ */ function() {
function GfxWhiteout2() {
}
@@ -2046,36 +1979,37 @@ var EventMouseMove = /* @__PURE__ */ function() {
}();
var tick = function(dictMonadState) {
return modify_(dictMonadState)(function(st) {
- var $109 = {};
- for (var $110 in st) {
- if ({}.hasOwnProperty.call(st, $110)) {
- $109[$110] = st[$110];
+ var $110 = {};
+ for (var $111 in st) {
+ if ({}.hasOwnProperty.call(st, $111)) {
+ $110[$111] = st[$111];
}
;
}
;
- $109.tick = st.tick + 1 | 0;
- $109.transitions = function() {
+ $110.tick = st.tick + 1 | 0;
+ $110.redraw = false;
+ $110.transitions = function() {
var v1 = uncons(st.transitions);
if (v1 instanceof Nothing) {
return st.transitions;
}
;
if (v1 instanceof Just) {
- var $105 = isEmpty(v1.value0.head.cells);
- if ($105) {
+ var $106 = isEmpty(v1.value0.head.cells);
+ if ($106) {
return v1.value0.tail;
}
;
return st.transitions;
}
;
- throw new Error("Failed pattern match at Main (line 217, column 19 - line 219, column 79): " + [v1.constructor.name]);
+ throw new Error("Failed pattern match at Main (line 239, column 19 - line 241, column 79): " + [v1.constructor.name]);
}();
- $109.inverse = filter2(function(v1) {
+ $110.inverse = filter2(function(v1) {
return st.tick <= v1;
})(st.inverse);
- return $109;
+ return $110;
});
};
var tick1 = /* @__PURE__ */ tick(monadStateStateT2);
@@ -2092,7 +2026,7 @@ var pickRandom = function(cells2) {
return new Just(v.value0);
}
;
- throw new Error("Failed pattern match at Main (line 135, column 3 - line 137, column 28): " + [v.constructor.name]);
+ throw new Error("Failed pattern match at Main (line 139, column 3 - line 141, column 28): " + [v.constructor.name]);
};
};
var pickCell = function(dictMonadState) {
@@ -2117,36 +2051,37 @@ var pickCell = function(dictMonadState) {
;
if (v1 instanceof Just) {
return put2(function() {
- var $119 = {};
- for (var $120 in st) {
- if ({}.hasOwnProperty.call(st, $120)) {
- $119[$120] = st[$120];
+ var $120 = {};
+ for (var $121 in st) {
+ if ({}.hasOwnProperty.call(st, $121)) {
+ $120[$121] = st[$121];
}
;
}
;
- $119.cells = insert2(v1.value0.value0)(v1.value0.value1)(st.cells);
- $119.transitions = cons(function() {
- var $116 = {};
- for (var $117 in v.value0.head) {
- if ({}.hasOwnProperty.call(v.value0.head, $117)) {
- $116[$117] = v["value0"]["head"][$117];
+ $120.redraw = true;
+ $120.cells = insert2(v1.value0.value0)(v1.value0.value1)(st.cells);
+ $120.transitions = cons(function() {
+ var $117 = {};
+ for (var $118 in v.value0.head) {
+ if ({}.hasOwnProperty.call(v.value0.head, $118)) {
+ $117[$118] = v["value0"]["head"][$118];
}
;
}
;
- $116.cells = $$delete2(v1.value0.value0)(v.value0.head.cells);
- return $116;
+ $117.cells = $$delete2(v1.value0.value0)(v.value0.head.cells);
+ return $117;
}())(v.value0.tail);
- return $119;
+ return $120;
}());
}
;
- throw new Error("Failed pattern match at Main (line 249, column 49 - line 252, column 120): " + [v1.constructor.name]);
+ throw new Error("Failed pattern match at Main (line 271, column 49 - line 274, column 135): " + [v1.constructor.name]);
});
}
;
- throw new Error("Failed pattern match at Main (line 246, column 3 - line 252, column 120): " + [v.constructor.name]);
+ throw new Error("Failed pattern match at Main (line 268, column 3 - line 274, column 135): " + [v.constructor.name]);
});
};
};
@@ -2167,30 +2102,61 @@ var newContext = function __do() {
})();
var render = getContext2D(v.value0)();
setFont(render)("bold 0.8vw Iosevka Comfy")();
- var cellDim = toNumber(ceil2(width8 / 100));
+ var cellWidth = toNumber(ceil2(width8 / 200));
+ var cellHeight = cellWidth * 2;
return new Just({
window: w,
canvas: v.value0,
render,
width: width8,
height: height8,
- cellDim,
- widthCells: ceil2(100),
- heightCells: ceil2(height8 / cellDim),
+ cellHeight,
+ cellWidth,
+ widthCells: ceil2(200),
+ heightCells: ceil2(height8 / cellHeight),
events: [new EventGfx(GfxWhiteout.value)]
});
}
;
- throw new Error("Failed pattern match at Main (line 77, column 45 - line 97, column 10): " + [v.constructor.name]);
+ throw new Error("Failed pattern match at Main (line 79, column 45 - line 101, column 10): " + [v.constructor.name]);
};
var initialState = {
tick: 0,
cells: empty2,
inverse: empty2,
- transitions: []
+ transitions: [],
+ redraw: true
};
var gfxTransitions = function(ctx) {
return function(v) {
+ var linkRight = function(y) {
+ return function(fgc) {
+ return function(bgc) {
+ return function(str) {
+ return function(url) {
+ return fromFoldable2(mapWithIndex2(function(i) {
+ return function(c) {
+ return new Tuple({
+ x: ((ctx.widthCells - length2(str) | 0) - 8 | 0) + i | 0,
+ y
+ }, {
+ bg: bgc,
+ fg: fgc,
+ "char": singleton4(c),
+ click: function(st) {
+ return function __do3() {
+ $$void2(open(url)("_blank")("")(ctx.window))();
+ return st;
+ };
+ }
+ });
+ };
+ })(toCharArray(str)));
+ };
+ };
+ };
+ };
+ };
var link = function(y) {
return function(fgc) {
return function(bgc) {
@@ -2219,7 +2185,7 @@ var gfxTransitions = function(ctx) {
};
};
};
- var fg = unions2([link(0)("purple")("white")("twitch.tv/lcolonq")("https://twitch.tv/lcolonq"), link(1)("blue")("white")("twitter.com/lcolonq")("https://twitter.com/lcolonq")]);
+ var fg = unions2([link(0)("purple")("white")("twitch.tv/lcolonq")("https://twitch.tv/lcolonq"), link(1)("blue")("white")("twitter.com/lcolonq")("https://twitter.com/lcolonq"), link(ctx.heightCells - 1 | 0)("white")("black")("the previous one")("https://pub.colonq.computer/~llll/cgi-bin/ring?me=llll&offset=-1"), linkRight(ctx.heightCells - 1 | 0)("white")("black")("the next one")("https://pub.colonq.computer/~llll/cgi-bin/ring?me=llll&offset=1")]);
var bg = fromFoldable2(concat(flip(map1)(range2(0)(ctx.widthCells))(function(x) {
return flip(map1)(range2(0)(ctx.heightCells))(function(y) {
return new Tuple({
@@ -2239,7 +2205,7 @@ var gfxTransitions = function(ctx) {
return singleton4(v1.value0);
}
;
- throw new Error("Failed pattern match at Main (line 182, column 12 - line 184, column 39): " + [v1.constructor.name]);
+ throw new Error("Failed pattern match at Main (line 188, column 12 - line 190, column 39): " + [v1.constructor.name]);
}()
});
});
@@ -2275,7 +2241,8 @@ var pullEvents = function(dictMonadState) {
if (v instanceof Just) {
return discard3(liftEffect2(write({
canvas: ctx.canvas,
- cellDim: ctx.cellDim,
+ cellHeight: ctx.cellHeight,
+ cellWidth: ctx.cellWidth,
height: ctx.height,
heightCells: ctx.heightCells,
render: ctx.render,
@@ -2288,6 +2255,7 @@ var pullEvents = function(dictMonadState) {
return pure22({
cells: st.cells,
inverse: st.inverse,
+ redraw: st.redraw,
tick: st.tick,
transitions: append3(st.transitions)(gfxTransitions(ctx)(v.value0.head.value0))
});
@@ -2306,7 +2274,7 @@ var pullEvents = function(dictMonadState) {
return liftEffect2(v1.value0.click(st));
}
;
- throw new Error("Failed pattern match at Main (line 234, column 11 - line 236, column 53): " + [v1.constructor.name]);
+ throw new Error("Failed pattern match at Main (line 256, column 11 - line 258, column 53): " + [v1.constructor.name]);
}
;
if (v.value0.head instanceof EventMouseMove) {
@@ -2320,17 +2288,18 @@ var pullEvents = function(dictMonadState) {
})));
return pure22({
cells: st.cells,
+ redraw: st.redraw,
tick: st.tick,
transitions: st.transitions,
inverse: union2(inv)(st.inverse)
});
}
;
- throw new Error("Failed pattern match at Main (line 231, column 7 - line 240, column 53): " + [v.value0.head.constructor.name]);
+ throw new Error("Failed pattern match at Main (line 253, column 7 - line 262, column 53): " + [v.value0.head.constructor.name]);
});
}
;
- throw new Error("Failed pattern match at Main (line 227, column 10 - line 240, column 53): " + [v.constructor.name]);
+ throw new Error("Failed pattern match at Main (line 249, column 10 - line 262, column 53): " + [v.constructor.name]);
}())(function(st$prime) {
return put2(st$prime);
});
@@ -2340,69 +2309,16 @@ var pullEvents = function(dictMonadState) {
};
};
var pullEvents1 = /* @__PURE__ */ pullEvents(monadStateStateT2)(monadEffectState2);
-var drawCell = function(ctx) {
- return function(st) {
- return function(v) {
- return function(pos) {
- var x = toNumber(pos.x) * ctx.cellDim;
- var y = toNumber(pos.y) * ctx.cellDim;
- var fg = function() {
- var $150 = member2({
- x: pos.x,
- y: pos.y
- })(st.inverse);
- if ($150) {
- return v.bg;
- }
- ;
- return v.fg;
- }();
- var bg = function() {
- var $151 = member2({
- x: pos.x,
- y: pos.y
- })(st.inverse);
- if ($151) {
- return v.fg;
- }
- ;
- return v.bg;
- }();
- return function __do3() {
- setFillStyle(ctx.render)(bg)();
- fillRect(ctx.render)({
- x,
- y,
- width: ctx.cellDim,
- height: ctx.cellDim
- })();
- setFillStyle(ctx.render)(fg)();
- return fillText(ctx.render)(v["char"])(x + ctx.cellDim / 4)(y + ctx.cellDim / 1.4)();
- };
- };
- };
- };
-};
-var drawCells = function(ctx) {
- return function(st) {
- return function(cells2) {
- return $$void2($$for2(toUnfoldable2(cells2))(function(v) {
- return drawCell(ctx)(st)(v.value1)(v.value0);
- }));
- };
- };
-};
var loop2 = function(rc) {
return function(st) {
return function __do3() {
var ctx = read(rc)();
- clearRect(ctx.render)({
+ when2(st.redraw)(clearRect(ctx.render)({
x: 0,
y: 0,
width: ctx.width,
height: ctx.height
- })();
- drawCells(ctx)(st)(st.cells)();
+ }))();
var v = flip(runStateT)(st)(discard22(tick1)(function() {
return discard22(pullEvents1(rc))(function() {
var v2 = head(st.transitions);
@@ -2411,12 +2327,12 @@ var loop2 = function(rc) {
}
;
if (v2 instanceof Just) {
- return when2(rem(st.tick)(v2.value0.cadence) === 0)(void1(for1(range2(0)(v2.value0.speed))(function(v1) {
+ return when1(rem(st.tick)(v2.value0.cadence) === 0)(void1($$for2(range2(0)(v2.value0.speed))(function(v1) {
return pickCell1;
})));
}
;
- throw new Error("Failed pattern match at Main (line 264, column 5 - line 268, column 58): " + [v2.constructor.name]);
+ throw new Error("Failed pattern match at Main (line 287, column 5 - line 291, column 58): " + [v2.constructor.name]);
});
}))();
return $$void2(requestAnimationFrame(loop2(rc)(v.value1))(ctx.window))();
@@ -2424,6 +2340,7 @@ var loop2 = function(rc) {
};
};
var main = function __do2() {
+ log2(apiServer)();
var w = windowImpl();
(function __do3() {
var v = newContext();
@@ -2444,7 +2361,7 @@ var main = function __do2() {
return write(v1.value0)(rc)();
}
;
- throw new Error("Failed pattern match at Main (line 56, column 24 - line 58, column 41): " + [v1.constructor.name]);
+ throw new Error("Failed pattern match at Main (line 58, column 24 - line 60, column 41): " + [v1.constructor.name]);
};
})();
addEventListener("resize")(lresize)(false)(toEventTarget(w))();
@@ -2462,19 +2379,20 @@ var main = function __do2() {
var ctx = read(rc)();
return write({
canvas: ctx.canvas,
- cellDim: ctx.cellDim,
+ cellHeight: ctx.cellHeight,
+ cellWidth: ctx.cellWidth,
height: ctx.height,
heightCells: ctx.heightCells,
render: ctx.render,
width: ctx.width,
widthCells: ctx.widthCells,
window: ctx.window,
- events: cons(h(floor2(px / ctx.cellDim))(floor2(py / ctx.cellDim)))(ctx.events)
+ events: cons(h(floor2(px / ctx.cellWidth))(floor2(py / ctx.cellHeight)))(ctx.events)
})(rc)();
};
}
;
- throw new Error("Failed pattern match at Main (line 60, column 42 - line 66, column 116): " + [v1.constructor.name]);
+ throw new Error("Failed pattern match at Main (line 62, column 42 - line 68, column 121): " + [v1.constructor.name]);
});
};
var lmouseclick = lmouse(EventMouseClick.create)();
@@ -2484,7 +2402,7 @@ var main = function __do2() {
return loop2(rc)(initialState)();
}
;
- throw new Error("Failed pattern match at Main (line 51, column 18 - line 71, column 27): " + [v.constructor.name]);
+ throw new Error("Failed pattern match at Main (line 53, column 18 - line 73, column 27): " + [v.constructor.name]);
})();
return unit;
};
diff --git a/fig-frontend-client/src/Config.js b/fig-frontend-client/src/Config.js
new file mode 100644
index 0000000..11a9792
--- /dev/null
+++ b/fig-frontend-client/src/Config.js
@@ -0,0 +1 @@
+export const apiServer = globalThis.apiServer;
diff --git a/fig-frontend-client/src/Config.purs b/fig-frontend-client/src/Config.purs
new file mode 100644
index 0000000..b464803
--- /dev/null
+++ b/fig-frontend-client/src/Config.purs
@@ -0,0 +1,3 @@
+module Config where
+
+foreign import apiServer :: String
diff --git a/fig-frontend-client/src/Main.purs b/fig-frontend-client/src/Main.purs
index 723acd8..871ef8a 100644
--- a/fig-frontend-client/src/Main.purs
+++ b/fig-frontend-client/src/Main.purs
@@ -2,13 +2,11 @@ module Main where
import Prelude
+import Config as Config
import Control.Monad.State (class MonadState, get, modify_, put, runStateT)
-import Data.Array (concat, cons, head, length, mapWithIndex, range, uncons, zip, (!!))
-import Data.Int (ceil, floor, rem, round, toNumber)
-import Data.Map (Map, delete, empty, filter, fromFoldable, insert, isEmpty, lookup, member, toUnfoldable, union, unions)
+import Data.Array (concat, cons, delete, filter, foldM, foldr, fromFoldable, head, length, mapWithIndex, null, range, uncons, updateAt, (!!))
+import Data.Int (ceil, floor, quot, rem, toNumber)
import Data.Maybe (Maybe(..))
-import Data.Set (Set)
-import Data.Set as Set
import Data.String.CodeUnits (toCharArray)
import Data.String.CodeUnits as String
import Data.Traversable (for)
@@ -16,6 +14,7 @@ import Data.Tuple (Tuple(..))
import Effect (Effect)
import Effect.Class (class MonadEffect, liftEffect)
import Effect.Console (log)
+import Effect.Exception (throw)
import Effect.Random (randomInt)
import Effect.Ref (Ref, new, read, write)
import Graphics.Canvas (CanvasElement, Context2D, clearRect, fillRect, fillText, getCanvasElementById, getContext2D, setCanvasDimensions, setFillStyle, setFont)
@@ -32,12 +31,27 @@ type Context =
, render :: Context2D
, width :: Number
, height :: Number
- , cellDim :: Number
+ , cellWidth :: Number
+ , cellHeight :: Number
, widthCells :: Int
, heightCells :: Int
, events :: Array Event
}
+lookupPos :: forall m a t. MonadEffect m => Context -> {x :: Int, y :: Int | t} -> Array a -> m a
+lookupPos ctx pos a = do
+ let idx = pos.y * ctx.widthCells + pos.x
+ case a !! idx of
+ Nothing -> liftEffect $ throw "index out of bounds"
+ Just x -> pure x
+
+updatePos :: forall m a t. MonadEffect m => Context -> {x :: Int, y :: Int | t} -> a -> Array a -> m (Array a)
+updatePos ctx pos v a = do
+ let idx = pos.y * ctx.widthCells + pos.x
+ case updateAt idx v a of
+ Nothing -> liftEffect $ throw "index out of bounds"
+ Just a' -> pure a'
+
main :: Effect Unit
main = do
-- d <- toDocument <$> document w
@@ -47,6 +61,7 @@ main = do
-- l <- eventListener \_e ->
-- log "click"
-- addEventListener click l false $ toEventTarget e
+ log $ Config.apiServer
w <- window
newContext >>= case _ of
Nothing -> log "failed to find canvas"
@@ -63,7 +78,7 @@ main = do
let px = toNumber $ pageX me
let py = toNumber $ pageY me
ctx <- read rc
- write (ctx { events = cons (h (floor $ px / ctx.cellDim) (floor $ py / ctx.cellDim)) ctx.events }) rc
+ write (ctx { events = cons (h (floor $ px / ctx.cellWidth) (floor $ py / ctx.cellHeight)) ctx.events }) rc
lmouseclick <- lmouse EventMouseClick
addEventListener (EventType "click") lmouseclick false $ Window.toEventTarget w
lmousemove <- lmouse EventMouseMove
@@ -82,17 +97,19 @@ newContext = do
setCanvasDimensions canvas { width, height }
render <- getContext2D canvas
setFont render "bold 0.8vw Iosevka Comfy"
- let widthCells = 100.0
- let cellDim = toNumber $ ceil $ width / widthCells
+ let widthCells = 200.0
+ let cellWidth = toNumber $ ceil $ width / widthCells
+ let cellHeight = cellWidth * 2.0
pure $ Just
{ window: w
, canvas
, render
, width
, height
- , cellDim
+ , cellHeight
+ , cellWidth
, widthCells: ceil widthCells
- , heightCells: ceil $ height / cellDim
+ , heightCells: ceil $ height / cellHeight
, events: [EventGfx GfxWhiteout]
}
@@ -105,39 +122,43 @@ newtype Cell = Cell
drawCell :: forall t. Context -> State -> Cell -> { x :: Int, y :: Int | t } -> Effect Unit
drawCell ctx st (Cell c) pos = do
- let x = toNumber pos.x * ctx.cellDim
- let y = toNumber pos.y * ctx.cellDim
- let fg = if member { x: pos.x, y: pos.y } st.inverse then c.bg else c.fg
- let bg = if member { x: pos.x, y: pos.y } st.inverse then c.fg else c.bg
+ let x = toNumber pos.x * ctx.cellWidth
+ let y = toNumber pos.y * ctx.cellHeight
+ inv <- lookupPos ctx pos st.inverse
+ let fg = if inv /= 0 then c.bg else c.fg
+ let bg = if inv /= 0 then c.fg else c.bg
setFillStyle ctx.render bg
fillRect ctx.render
{ x
, y
- , width: ctx.cellDim
- , height: ctx.cellDim
+ , width: ctx.cellWidth
+ , height: ctx.cellHeight
}
setFillStyle ctx.render fg
- fillText ctx.render c.char (x + ctx.cellDim / 4.0) (y + ctx.cellDim / 1.4)
+ fillText ctx.render c.char (x + ctx.cellWidth / 4.0) (y + ctx.cellHeight / 1.4)
-type Cells = Map { x :: Int, y :: Int } Cell
-
-drawCells :: Context -> State -> Cells -> Effect Unit
+drawCells :: Context -> State -> Array Cell -> Effect Unit
drawCells ctx st cells = do
- void $ for (toUnfoldable cells :: Array _) \(Tuple pos c) -> do
- drawCell ctx st c pos
+ void $ for (range 0 ctx.widthCells) \x -> do
+ for (range 0 ctx.heightCells) \y -> do
+ let pos = { x, y }
+ c <- lookupPos ctx pos cells
+ drawCell ctx st c pos
-type Picker = Cells -> Effect (Maybe (Tuple { x :: Int, y :: Int} Cell))
+type Picker = Context -> Array Cell -> Effect (Maybe (Tuple { x :: Int, y :: Int} Cell))
pickRandom :: Picker
-pickRandom cells = do
- let arr = toUnfoldable cells
- idx <- randomInt 0 $ length arr - 1
- case arr !! idx of
+pickRandom ctx cells = do
+ idx <- randomInt 0 $ length cells - 1
+ case cells !! idx of
Nothing -> pure Nothing
- Just c -> pure $ Just c
+ Just c ->
+ pure
+ $ Just
+ $ Tuple { x: rem idx ctx.widthCells, y: quot idx ctx.widthCells } c
type Transition =
- { cells :: Cells
+ { cells :: Array Cell
, speed :: Int
, cadence :: Int
, picker :: Picker
@@ -145,17 +166,19 @@ type Transition =
type State =
{ tick :: Int
- , cells :: Cells
- , inverse :: Map { x :: Int, y :: Int } Int
+ , cells :: Array Cell
+ , inverse :: Array Int
, transitions :: Array Transition
+ , redraw :: Boolean
}
initialState :: State
initialState =
{ tick: 0
- , cells: empty
- , inverse: empty
+ , cells: []
+ , inverse: []
, transitions: []
+ , redraw: true
}
data Event
@@ -183,7 +206,7 @@ gfxTransitions ctx GfxWhiteout =
Nothing -> "?"
Just c -> String.singleton c
}
- link :: Int -> String -> String -> String -> String -> Cells
+ link :: Int -> String -> String -> String -> String -> Array Cell
link y fgc bgc str url =
fromFoldable $ mapWithIndex
(\i c ->
@@ -196,12 +219,28 @@ gfxTransitions ctx GfxWhiteout =
}
)
$ toCharArray str
- fg =
- unions
- [ link 0 "purple" "white" "twitch.tv/lcolonq" "https://twitch.tv/lcolonq"
- , link 1 "blue" "white" "twitter.com/lcolonq" "https://twitter.com/lcolonq"
- ]
- cells = union fg bg
+ linkRight :: Int -> String -> String -> String -> String -> Array Cell
+ linkRight y fgc bgc str url =
+ fromFoldable $ mapWithIndex
+ (\i c ->
+ Tuple { x: (ctx.widthCells - String.length str - 8) + i, y }
+ $ Cell
+ { bg: bgc, fg: fgc, char: String.singleton c
+ , click: \st -> do
+ void $ open url "_blank" "" ctx.window
+ pure st
+ }
+ )
+ $ toCharArray str
+ -- fg =
+ -- unions
+ -- [ link 0 "purple" "white" "twitch.tv/lcolonq" "https://twitch.tv/lcolonq"
+ -- , link 1 "blue" "white" "twitter.com/lcolonq" "https://twitter.com/lcolonq"
+ -- , link (ctx.heightCells - 1) "white" "black" "the previous one" "https://pub.colonq.computer/~llll/cgi-bin/ring?me=llll&offset=-1"
+ -- , linkRight (ctx.heightCells - 1) "white" "black" "the next one" "https://pub.colonq.computer/~llll/cgi-bin/ring?me=llll&offset=1"
+ -- ]
+ -- cells = union fg bg
+ cells = bg
in
[ { cells
, speed: 20
@@ -214,9 +253,10 @@ tick :: forall m. MonadState State m => m Unit
tick = modify_ \st ->
st
{ tick = st.tick + 1
+ , redraw = false
, transitions = case uncons st.transitions of
Nothing -> st.transitions
- Just { head, tail } -> if isEmpty head.cells then tail else st.transitions
+ Just { head, tail } -> if null head.cells then tail else st.transitions
, inverse = filter (st.tick <= _) st.inverse
}
@@ -231,39 +271,41 @@ pullEvents rc = do
case head of
EventGfx gfx -> pure $ st { transitions = st.transitions <> gfxTransitions ctx gfx }
EventMouseClick mx my ->
- case lookup { x: mx, y: my } st.cells of
+ lookupPos ctx { x: mx, y: my } st.cells >>= case _ of
Nothing -> pure st
Just (Cell c) -> liftEffect $ c.click st
EventMouseMove mx my -> do
- let inv = fromFoldable $ concat $ flip map (range (mx - 1) (mx + 1)) \x ->
- flip map (range (my - 1) (my + 1)) \y -> Tuple { x, y } (st.tick + 30)
- pure st { inverse = union inv st.inverse }
+ inv <- foldM (\inv pos -> updatePos ctx pos (st.tick + 30) inv) st.inverse $ map (\x -> map (\y -> { x, y }) (range (my - 1) (my + 1))) (range (mx - 1) (mx + 1))
+ pure st { inverse = inv }
put st'
-pickCell :: forall m. MonadState State m => MonadEffect m => m Unit
-pickCell = do
+pickCell :: forall m. MonadState State m => MonadEffect m => Context -> m Unit
+pickCell ctx = do
st <- get
case uncons st.transitions of
Nothing -> pure unit
Just { head: trans, tail } ->
liftEffect (trans.picker trans.cells) >>= case _ of
Nothing -> pure unit
- Just (Tuple pk pv) ->
- put st { cells = insert pk pv st.cells, transitions = cons (trans { cells = (delete pk trans.cells) }) tail }
+ Just (Tuple pk pv) -> do
+ cells <- updatePos ctx pk pv st.cells
+ put st { redraw = true, cells = cells, transitions = cons (trans { cells = (delete pk trans.cells) }) tail }
loop :: Ref Context -> State -> Effect Unit
loop rc st = do
ctx <- read rc
-- render
- clearRect ctx.render { x: 0.0, y: 0.0, width: ctx.width, height: ctx.height }
- drawCells ctx st st.cells
+ when st.redraw do
+ clearRect ctx.render { x: 0.0, y: 0.0, width: ctx.width, height: ctx.height }
+ drawCells ctx st st.cells
-- update
- Tuple _ st' <- flip runStateT st do
- tick
- pullEvents rc
- case head st.transitions of
- Nothing -> pure unit
- Just trans -> do
- when (rem st.tick trans.cadence == 0) do
- void $ for (range 0 trans.speed) \_ -> pickCell
- void $ requestAnimationFrame (loop rc st') ctx.window
+ -- Tuple _ st' <- flip runStateT st do
+ -- tick
+ -- pullEvents rc
+ -- case head st.transitions of
+ -- Nothing -> pure unit
+ -- Just trans -> do
+ -- when (rem st.tick trans.cadence == 0) do
+ -- void $ for (range 0 trans.speed) \_ -> pickCell
+ -- void $ requestAnimationFrame (loop rc st') ctx.window
+ void $ requestAnimationFrame (loop rc st) ctx.window