commit a9af55fb0b1d7e3b4d3b5b1c535e6b0f97d597d0 Author: Christian Lawson-Perfect Date: Sun Feb 9 20:37:04 2025 +0000 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..77b0fdd --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.make.* +app.js +elm-stuff/ +error.txt \ No newline at end of file diff --git a/.watchmakerc b/.watchmakerc new file mode 100644 index 0000000..285f521 --- /dev/null +++ b/.watchmakerc @@ -0,0 +1,2 @@ +extensions: + - .elm \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..092ea03 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +DIRNAME=$(notdir $(CURDIR)) + +ELMS=$(wildcard src/*.elm) + +app.js: src/App.elm $(ELMS) + -elm make $< --output=$@ 2> error.txt + @cat error.txt + +upload: app.js index.html style.css + rsync -avz . clpland:~/domains/somethingorotherwhatever.com/html/$(DIRNAME) + @echo "Uploaded to https://somethingorotherwhatever.com/$(DIRNAME)" \ No newline at end of file diff --git a/elm.json b/elm.json new file mode 100644 index 0000000..ff2b954 --- /dev/null +++ b/elm.json @@ -0,0 +1,25 @@ +{ + "type": "application", + "source-directories": [ + "src" + ], + "elm-version": "0.19.1", + "dependencies": { + "direct": { + "elm/browser": "1.0.2", + "elm/core": "1.0.5", + "elm/html": "1.0.0", + "elm/svg": "1.0.1", + "elm/time": "1.0.0" + }, + "indirect": { + "elm/json": "1.1.3", + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.3" + } + }, + "test-dependencies": { + "direct": {}, + "indirect": {} + } +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..ad818c7 --- /dev/null +++ b/index.html @@ -0,0 +1,23 @@ + + + + + + Elm app by clp + + + + +
+

Elm app by clp

+
+
+

This is an app which will either load succesfully, and you'll wonder whether you saw this text at all, or fail ignominiously, showing you only this text.

+

On balance of probabilities: I'm sorry I couldn't be bothered to make this work for you.

+
+ + + + + + diff --git a/load-app.js b/load-app.js new file mode 100644 index 0000000..ff737aa --- /dev/null +++ b/load-app.js @@ -0,0 +1,10 @@ +import show_error from './show-error.mjs'; +async function init_app() { + const compilation_error = await show_error; + if(compilation_error) { + return; + } + const app = Elm.App.init({node: document.body, flags: {}}); +} + +init_app(); diff --git a/show-error.mjs b/show-error.mjs new file mode 100644 index 0000000..0c9d53e --- /dev/null +++ b/show-error.mjs @@ -0,0 +1,21 @@ +export default fetch('/error.txt').then(r=>{ + if(r.ok) { + return r.text(); + } else { + throw(''); + } +}).then(text => { + if(!text) { + return false; + } + document.body.innerHTML = ''; + const error_show = document.createElement('pre'); + error_show.setAttribute('id','build-error'); + error_show.style.background = 'black'; + error_show.style.color = 'white'; + error_show.style.padding = '1em'; + error_show.style['font-size'] = '16px'; + error_show.textContent = text; + document.body.appendChild(error_show); + return true; +}).catch(e => false); diff --git a/src/App.elm b/src/App.elm new file mode 100644 index 0000000..14e7d63 --- /dev/null +++ b/src/App.elm @@ -0,0 +1,128 @@ +module App exposing (..) + +import Browser +import Html as H exposing (Html) +import Html.Attributes as HA +import Html.Events as HE +import Svg +import Svg.Attributes as SA +import Time exposing (Posix) + +main = Browser.document + { init = init + , update = update + , subscriptions = subscriptions + , view = view + } + +type alias Model = { time: Posix } + +init_model = { time = Time.millisToPosix 0 } + +type Msg + = Tick Posix + +init : () -> (Model, Cmd msg) +init _ = (init_model, Cmd.none) + +update msg model = case msg of + Tick time -> ({ model | time = time }, Cmd.none) + +subscriptions model = Time.every 50 Tick + +ff = String.fromFloat + +view model = + let + time = model.time + zone = Time.utc + now = (toFloat <| Time.posixToMillis time) / 1000 + + num_arms = 10 + + r_for n = 1 - 0.9*n/num_arms + period_for n = 10 ^ ((toFloat num_arms) - n) + + arm n = + let + period = period_for n + an = (now / period - 0.25) * 2 * pi + r = r_for n + in + Svg.line + [ SA.x1 "0" + , SA.y1 "0" + , SA.x2 <| ff <| (*) r <| cos an + , SA.y2 <| ff <| (*) r <| sin an + , SA.strokeWidth "0.01" + , SA.stroke "black" +-- , SA.strokeDasharray <| (ff dash) ++ " " ++ (ff dash) + ] + [] + arms = List.map (toFloat >> arm) (List.range 0 num_arms) + + tick ii = + let + i = toFloat ii + an = 2 * pi * (i / 10 - 0.25) + r = 1 + in + Svg.line + [ SA.x1 "0" + , SA.y1 "0" + , SA.x2 <| ff <| (*) r <| cos an + , SA.y2 <| ff <| (*) r <| sin an + , SA.strokeWidth "0.01" + , SA.stroke "#eee" + ] + [] + + inner_circle n = + let + fn = toFloat n + period = period_for fn + r = (r_for fn) + num_ticks = (floor period) + in + Svg.g + [] + [ Svg.circle + [ SA.r <| ff r + , SA.strokeWidth "0.002" + , SA.stroke "gray" + , SA.fill "none" + ] + [] + , Svg.text_ + [ SA.x "0" + , SA.y <| ff (-r) + , SA.fontSize "0.03" + , SA.textAnchor "middle" + ] + [ Svg.text <| ff <| period] + ] + + circles = List.map (inner_circle) (List.range 0 num_arms) + in + { + title = "Unix epoch clock", + body = + [ Svg.svg + [ HA.attribute "viewBox" "-1.2 -1.2 2.4 2.6"] + [ Svg.g [] (List.map tick (List.range 0 9)) + , Svg.g [] circles + , Svg.g [] arms + , Svg.text_ + [ SA.x "0" + , SA.y "1.1" + , SA.textAnchor "middle" + , SA.dominantBaseline "central" + , SA.fontSize "0.1" + , SA.stroke "white" + , SA.strokeWidth "0.02" + , SA.style "paint-order: stroke fill" + ] + [ Svg.text <| String.fromInt <| floor <| (\n -> (toFloat n)/1000) <| Time.posixToMillis model.time ] + ] + ] + } \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..9a92585 --- /dev/null +++ b/style.css @@ -0,0 +1,14 @@ +body { + margin: 0; + width: 100svw; + height: 100svh; + display: grid; + justify-content: center; + align-content: center; + grid-template: 1fr / 1fr; +} + +svg { + width: 100%; + max-height: 100%; +} \ No newline at end of file