first commit

This commit is contained in:
Christian Lawson-Perfect 2025-02-09 20:37:04 +00:00
commit a9af55fb0b
9 changed files with 238 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
.make.*
app.js
elm-stuff/
error.txt

2
.watchmakerc Normal file
View file

@ -0,0 +1,2 @@
extensions:
- .elm

11
Makefile Normal file
View file

@ -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)"

25
elm.json Normal file
View file

@ -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": {}
}
}

23
index.html Normal file
View file

@ -0,0 +1,23 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Elm app by clp</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Elm app by clp</h1>
</header>
<main>
<p>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.</p>
<p>On balance of probabilities: I'm sorry I couldn't be bothered to make this work for you.</p>
</main>
<footer>Made by <a href="https://somethingorotherwhatever.com">clp</a></footer>
<script src="app.js"></script>
<script src="load-app.js" type="module"></script>
</body>
</html>

10
load-app.js Normal file
View file

@ -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();

21
show-error.mjs Normal file
View file

@ -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);

128
src/App.elm Normal file
View file

@ -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 ]
]
]
}

14
style.css Normal file
View file

@ -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%;
}