init nix-features

This commit is contained in:
mtgmonkey 2025-06-02 12:44:29 +00:00
parent d04ddd4c32
commit b929e21f82
14 changed files with 1646 additions and 61 deletions

37
elm.json Normal file
View file

@ -0,0 +1,37 @@
{
"type": "application",
"source-directories": [
"src"
],
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"NoRedInk/elm-json-decode-pipeline": "1.0.1",
"bartavelle/json-helpers": "2.0.2",
"elm/browser": "1.0.2",
"elm/core": "1.0.5",
"elm/html": "1.0.0",
"elm/json": "1.1.3",
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"lobanov/elm-localstorage": "1.0.1",
"rtfeldman/elm-css": "18.0.0",
"rtfeldman/elm-iso8601-date-strings": "1.1.4"
},
"indirect": {
"elm/bytes": "1.0.8",
"elm/file": "1.0.5",
"elm/http": "2.0.0",
"elm/parser": "1.1.0",
"elm/random": "1.0.0",
"elm/virtual-dom": "1.0.3",
"lobanov/elm-taskport": "2.0.1",
"robinheghan/murmur3": "1.0.0",
"rtfeldman/elm-hex": "1.0.0"
}
},
"test-dependencies": {
"direct": {},
"indirect": {}
}
}

34
elmskell-frontend.nix Normal file
View file

@ -0,0 +1,34 @@
{
esbuild,
stdenv,
elmPackages,
...
}: let
elmConfig = elmPackages.fetchElmDeps {
elmPackages = import ./frontend/elm-srcs.nix;
elmVersion = "0.19.1";
registryDat = ./frontend/registry.dat;
};
in
stdenv.mkDerivation {
pname = "elmskell-backend";
version = "0.1.0";
src = ./frontend;
nativeBuildInputs = [
esbuild
elmPackages.elm
];
buildInputs = [
];
configurePhase = ''
${elmConfig}
'';
buildPhase = ''
elm make src/Main.elm --optimize --output=tmp.js
esbuild tmp.js --minify --target=es5 --outfile=main.js
'';
installPhase = ''
mkdir -p $out
cp main.js $out/main.js
'';
}

26
flake.lock generated Normal file
View file

@ -0,0 +1,26 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1748708770,
"narHash": "sha256-q8jG2HJWgooWa9H0iatZqBPF3bp0504e05MevFmnFLY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a59eb7800787c926045d51b70982ae285faa2346",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-25.05",
"type": "indirect"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

14
flake.nix Normal file
View file

@ -0,0 +1,14 @@
{
inputs = {
nixpkgs.url = "nixpkgs/nixos-25.05";
};
outputs = {nixpkgs, ...}: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in {
packages.${system} = {
default = pkgs.callPackage ./package.nix {};
frontend = pkgs.callPackage ./elmskell-frontend.nix {};
};
};
}

102
frontend/elm-srcs.nix Normal file
View file

@ -0,0 +1,102 @@
{
"NoRedInk/elm-json-decode-pipeline" = {
sha256 = "1k241pjz1wj5rqv95f1j86msa0s0p6w4v8n0jn26aw6cbadw363b";
version = "1.0.1";
};
"bartavelle/json-helpers" = {
sha256 = "0k96qra2nq1j4j4ahfl98dkpkc6f2831mq5d5xxg27mp31qwq5nn";
version = "2.0.2";
};
"elm/browser" = {
sha256 = "0nagb9ajacxbbg985r4k9h0jadqpp0gp84nm94kcgbr5sf8i9x13";
version = "1.0.2";
};
"elm/core" = {
sha256 = "19w0iisdd66ywjayyga4kv2p1v9rxzqjaxhckp8ni6n8i0fb2dvf";
version = "1.0.5";
};
"elm/html" = {
sha256 = "1n3gpzmpqqdsldys4ipgyl1zacn0kbpc3g4v3hdpiyfjlgh8bf3k";
version = "1.0.0";
};
"elm/json" = {
sha256 = "0kjwrz195z84kwywaxhhlnpl3p251qlbm5iz6byd6jky2crmyqyh";
version = "1.1.3";
};
"elm/time" = {
sha256 = "0vch7i86vn0x8b850w1p69vplll1bnbkp8s383z7pinyg94cm2z1";
version = "1.0.0";
};
"elm/url" = {
sha256 = "0av8x5syid40sgpl5vd7pry2rq0q4pga28b4yykn9gd9v12rs3l4";
version = "1.0.0";
};
"lobanov/elm-localstorage" = {
sha256 = "00n3hk6gzrg637n4vfwk7arbj78bppqjn0mcnl7yp2ypva971dia";
version = "1.0.1";
};
"rtfeldman/elm-css" = {
sha256 = "1gwhgqwclc7clk1ns4qqzyn7b4wvcmccg9qavvb8m694qmwlkzjy";
version = "18.0.0";
};
"rtfeldman/elm-iso8601-date-strings" = {
sha256 = "1ah491kgyicgvy1c9myylqvhzb7ya9kgmn0hcsv23ymvqgaf6b1a";
version = "1.1.4";
};
"elm/bytes" = {
sha256 = "02ywbf52akvxclpxwj9n04jydajcbsbcbsnjs53yjc5lwck3abwj";
version = "1.0.8";
};
"elm/file" = {
sha256 = "1rljcb41dl97myidyjih2yliyzddkr2m7n74x7gg46rcw4jl0ny8";
version = "1.0.5";
};
"elm/http" = {
sha256 = "008bs76mnp48b4dw8qwjj4fyvzbxvlrl4xpa2qh1gg2kfwyw56v1";
version = "2.0.0";
};
"elm/parser" = {
sha256 = "0a3cxrvbm7mwg9ykynhp7vjid58zsw03r63qxipxp3z09qks7512";
version = "1.1.0";
};
"elm/random" = {
sha256 = "138n2455wdjwa657w6sjq18wx2r0k60ibpc4frhbqr50sncxrfdl";
version = "1.0.0";
};
"elm/virtual-dom" = {
sha256 = "1yvb8px2z62xd578ag2q0r5hd1vkz9y7dfkx05355iiy1d7jwq4v";
version = "1.0.3";
};
"lobanov/elm-taskport" = {
sha256 = "16vf3k80sicmhnr1k2nmdc9713ips7nyaxzjzkb6w8mi5w4qsmc0";
version = "2.0.1";
};
"robinheghan/murmur3" = {
sha256 = "15asmgr2zqh7rkywrg5647rpdqkpzxk02v5qc6ndj60jza3gsmjk";
version = "1.0.0";
};
"rtfeldman/elm-hex" = {
sha256 = "1y0aa16asvwdqmgbskh5iba6psp43lkcjjw9mgzj3gsrg33lp00d";
version = "1.0.0";
};
}

BIN
frontend/registry.dat Normal file

Binary file not shown.

View file

@ -1,32 +0,0 @@
produce: produce-elm haskell
echo "REMEMBER to RESTART elmskell.service TO UPDATE SITE IN PLACE"
echo "REMEMBER to RUN sudo iptables-apply -t 60 /etc/iptables/iptables.rules IF YOU HAVEN'T SINCE RESTART"
types: haskell
rm -rf frontend/src/ElmskellTypes/Generated
stack exec ~/.local/bin/hs-server-exe
compile: elm haskell
run: produce exec
exec:
stack exec ~/.local/bin/hs-server-exe
haskell:
cd backend && stack install
elm:
cd frontend && elm make src/Main.elm --output=../assets/js/main.js
rm -rf frontend/elm-stuff
produce-elm:
cd frontend && elm make src/Main.elm --optimize --output=tmp.js
mv frontend/tmp.js assets/js/tmp.js
rm -rf frontend/elm-stuff
rm assets/js/main.js
esbuild assets/js/tmp.js --minify --target=es5 --outfile=assets/js/main.js
rm assets/js/tmp.js
format-elm:
elm-format frontend/src/Main.elm --yes

53
package.nix Normal file
View file

@ -0,0 +1,53 @@
{
esbuild,
elmPackages,
haskellPackages,
stdenv,
...
}: let
ghcExeOptions = "-Wall -Wcompact -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N";
ghcPackages = p: [
p.aeson
p.blaze-html
p.blaze-markup
p.directory
p.elm-street
p.http-types
p.scotty
p.text
p.wai-extra
p.warp
];
elmConfig = elmPackages.fetchElmDeps {
elmPackages = import ./frontend/elm-srcs.nix;
elmVersion = "0.19.1";
registryDat = ./frontend/registry.dat;
};
in
stdenv.mkDerivation {
pname = "elmskell";
version = "0.1.0";
src = ./src;
nativeBuildInputs = [
esbuild
(haskellPackages.ghcWithPackages ghcPackages)
elmPackages.elm
];
buildInputs = [
];
configurePhase = ''
${elmConfig}
'';
buildPhase = ''
ghc -v ${ghcExeOptions} ./Main.hs -o ./main
elm make ./Main.elm --optimize --output=tmp.js
esbuild ./tmp.js --minify --target=es5 --outfile=main.js
'';
installPhase = ''
mkdir -p $out/bin
mkdir -p $out/src
cp ./main $out/bin/elmskell
cp ./main.js $out/src/main.js
cp ./init.js $out/src/init.js
'';
}

1
result Symbolic link
View file

@ -0,0 +1 @@
/nix/store/afnd25aqbkrr55z46pld1yg1fdnv893w-elmskell-0.1.0

View file

@ -1,27 +0,0 @@
{pkgs ? import <nixpkgs> {}}: let
elmInputs = [
pkgs.elmPackages.elm
pkgs.elmPackages.elm-format
pkgs.esbuild
];
haskellInputs = [
pkgs.stack
];
buildTools = [
pkgs.just
];
cliTools = [
pkgs.httpie
pkgs.ungoogled-chromium
];
in
pkgs.mkShell {
buildInputs = [
elmInputs
haskellInputs
];
packages = [
buildTools
cliTools
];
}

98
src/ElmskellTypes.hs Normal file
View file

@ -0,0 +1,98 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE TypeApplications #-}
module ElmskellTypes (generateElmskellTypes) where
import Data.Aeson (ToJSON (..), FromJSON (..))
import Elm
import GHC.Generics
data Command
= ClearCommand
| ColorsCommand
| CookiesCommand
| DebugCommand
| FontCommand
| HelloCommand
| HelpCommand
| PromptCommand
| ThemeCommand
| TodoCommand
deriving (Generic)
deriving (Elm, ToJSON, FromJSON) via ElmStreet Command
data CookiesKept = CookiesKept
{ cookiesKeptKeepFont :: Bool
, cookiesKeptKeepPrompt :: Bool
, cookiesKeptKeepTheme :: Bool
} deriving (Generic)
deriving (Elm, ToJSON, FromJSON) via ElmStreet CookiesKept
data CoreColor
= Red
| Green
| Yellow
| Blue
| Magenta
| Cyan
| BrightRed
| BrightGreen
| BrightYellow
| BrightBlue
| BrightMagenta
| BrightCyan
deriving (Generic)
deriving (Elm, ToJSON, FromJSON) via ElmStreet CoreColor
data Font = Font
{ fontFontSize :: Float
} deriving (Generic)
deriving (Elm, ToJSON, FromJSON) via ElmStreet Font
data Input = Input
{ inputCommand :: Command
, inputArgs :: [String]
}
deriving (Generic)
deriving (Elm, ToJSON, FromJSON) via ElmStreet Input
data Prompt = Prompt
{ promptPrompt :: String
} deriving (Generic)
deriving (Elm, ToJSON, FromJSON) via ElmStreet Prompt
data Theme
= Pit
| Dim
| Sky
| Sun
deriving (Generic)
deriving (Elm, ToJSON, FromJSON) via ElmStreet Theme
data ThemeColor
= Background
| Foreground
| Cursor
| Black
| White
| BrightBlack
| BrightWhite
deriving (Generic)
deriving (Elm, ToJSON, FromJSON) via ElmStreet ThemeColor
type Types =
'[Command
, CookiesKept
, CoreColor
, Font
, Input
, Prompt
, Theme
, ThemeColor
]
generateElmskellTypes :: IO ()
generateElmskellTypes = generateElm @Types $ defaultSettings "frontend/src" ["ElmskellTypes", "Generated"]

View file

@ -1 +0,0 @@
../frontend/src/Main.elm

1173
src/Main.elm Normal file

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@
../backend/app/Main.hs

108
src/Main.hs Normal file
View file

@ -0,0 +1,108 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module Main (main) where
import ElmskellTypes
import Data.Text.Lazy (Text)
import Network.Wai.Handler.Warp (Port)
import Network.Wai.Middleware.RequestLogger (logStdoutDev)
import Text.Blaze ((!))
import Text.Blaze.Html.Renderer.Text (renderHtml)
import Network.HTTP.Types
import Network.Wai.Middleware.Gzip
import Web.Scotty as S
import qualified Control.Exception as E
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes as A
-- HTML
index :: H.Html -> Text
index compiledElmApp = renderHtml $ do
H.docTypeHtml $ do
H.head $ do
H.title "TESTING Scotty+Elm"
H.meta ! A.charset "utf-8"
H.style "body{margin:0px;}"
(H.body ! A.id "body") $ do
embedJs compiledElmApp
-- CONF
port :: Port
port = 8080
adminContact :: String
adminContact = "[Matrix] @mtgmonkey:calitabby.net"
compiledElmAppFile :: AssetPath
compiledElmAppFile = "/js/main.js"
boilerplateJsFile :: AssetPath
boilerplateJsFile = "/js/init.js"
assetsFolder :: FilePath
assetsFolder = "/home/mtgmonkey/elmskell/assets"
-- MAIN
main :: IO ()
main = do
generateElmskellTypes
compiledElmAppOrExc <- E.try $ readFile $ assetsFolder ++ compiledElmAppFile :: IO (Either E.IOException String)
let compiledElmApp = case compiledElmAppOrExc of
Left e -> serverErrorReadFile e
Right contents -> H.toHtml $ contents
boilerplateJsOrExc <- E.try $ readFile $ assetsFolder ++ boilerplateJsFile :: IO (Either E.IOException String)
let boilerplateJs = case boilerplateJsOrExc of
Left e -> serverErrorReadFile e
Right contents -> H.toHtml $ contents
let anyRoute = regex "^.*$"
scotty port $ do
middleware $ gzip $ def { gzipFiles = GzipCompress }
middleware logStdoutDev
-- GET requests
get "/" $ do
shortCache
status ok200
S.html $ index $ do
compiledElmApp
boilerplateJs
get "/favicon.ico/" $ do
shortCache
status notFound404
S.html $ "you want a favi-<i>what</i>now!?"
-- ERR
notFound $ do
noCache
status methodNotAllowed405
S.text "Verb disallowed; OR, route doesn't exist :("
-- FUNC
serverErrorReadFile :: E.IOException -> Js
serverErrorReadFile e = H.toHtml $ "document.getElementById('body').innerHTML='Server-side error occurred: "
++ (show e)
++ "; report this to a site administrator: "
++ adminContact
++ "';"
shortCache :: ActionM ()
shortCache = addHeader "Cache-Control" "max-age=21600"
noCache :: ActionM ()
noCache = addHeader "Cache-Control" "no-cache"
embedJs :: Js -> H.Html
embedJs js = H.script $ js
-- TYPES
type AssetPath = FilePath
type Js = H.Html