mirror of
https://github.com/phfroidmont/scalive.git
synced 2025-12-24 21:26:58 +01:00
Initial commit
This commit is contained in:
commit
ed71df15f3
7 changed files with 218 additions and 0 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
||||||
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
.bsp
|
||||||
|
.direnv
|
||||||
|
.metals
|
||||||
|
.scala-build
|
||||||
|
secrets.env
|
||||||
|
out
|
||||||
2
.scalafmt.conf
Normal file
2
.scalafmt.conf
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
version = "3.7.17"
|
||||||
|
runner.dialect = scala3
|
||||||
6
build.mill
Normal file
6
build.mill
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
package build
|
||||||
|
import mill.*, scalalib.*
|
||||||
|
|
||||||
|
object core extends ScalaModule:
|
||||||
|
def scalaVersion = "3.7.2"
|
||||||
|
def scalacOptions = Seq("-Wunused:all")
|
||||||
94
core/src/main.scala
Normal file
94
core/src/main.scala
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
import scala.collection.immutable.ArraySeq
|
||||||
|
|
||||||
|
@main
|
||||||
|
def main =
|
||||||
|
val temlate = Template(TestLiveView.render)
|
||||||
|
println(temlate.init(MyModel("Initial title")))
|
||||||
|
println(temlate.update(MyModel("Updated title")))
|
||||||
|
|
||||||
|
trait LiveView[Model]:
|
||||||
|
val model = Dyn[Model, Model](identity)
|
||||||
|
def render: HtmlTag[Model]
|
||||||
|
|
||||||
|
final case class MyModel(title: String)
|
||||||
|
|
||||||
|
object TestLiveView extends LiveView[MyModel]:
|
||||||
|
def render: HtmlTag[MyModel] =
|
||||||
|
div(
|
||||||
|
div("some text"),
|
||||||
|
model(_.title)
|
||||||
|
)
|
||||||
|
|
||||||
|
class Dyn[I, O](f: I => O):
|
||||||
|
private var last: Option[O] = None
|
||||||
|
|
||||||
|
def apply[O2](f2: O => O2): Dyn[I, O2] = Dyn(f.andThen(f2))
|
||||||
|
|
||||||
|
def forceUpate(v: I): O =
|
||||||
|
val newValue = f(v)
|
||||||
|
last = Some(newValue)
|
||||||
|
newValue
|
||||||
|
|
||||||
|
def update(v: I): Option[O] =
|
||||||
|
val newValue = f(v)
|
||||||
|
last match
|
||||||
|
case Some(lastValue) if lastValue == newValue => None
|
||||||
|
case _ =>
|
||||||
|
last = Some(newValue)
|
||||||
|
last
|
||||||
|
|
||||||
|
enum Mod[T]:
|
||||||
|
case Tag(v: HtmlTag[T])
|
||||||
|
case Text(v: String)
|
||||||
|
case DynText(v: Dyn[T, String])
|
||||||
|
|
||||||
|
given [T]: Conversion[HtmlTag[T], Mod[T]] = Mod.Tag(_)
|
||||||
|
given [T]: Conversion[String, Mod[T]] = Mod.Text(_)
|
||||||
|
given [T]: Conversion[Dyn[T, String], Mod[T]] = Mod.DynText(_)
|
||||||
|
|
||||||
|
class Template[Model](
|
||||||
|
private val static: ArraySeq[String],
|
||||||
|
private val dynamic: ArraySeq[Dyn[Model, String]]
|
||||||
|
):
|
||||||
|
def init(model: Model): Template.InitialState =
|
||||||
|
Template.InitialState(
|
||||||
|
static,
|
||||||
|
dynamic.map(_.forceUpate(model))
|
||||||
|
)
|
||||||
|
def update(model: Model): Template.Diff =
|
||||||
|
Template.Diff(
|
||||||
|
dynamic.zipWithIndex.flatMap((dyn, i) => dyn.update(model).map(i -> _))
|
||||||
|
)
|
||||||
|
object Template:
|
||||||
|
final case class InitialState(static: Seq[String], dynamic: Seq[String])
|
||||||
|
final case class Diff(dynamic: Seq[(Int, String)])
|
||||||
|
def apply[Model](tag: HtmlTag[Model]) =
|
||||||
|
val (static, dynamic) = buildTag(tag)
|
||||||
|
new Template(static.to(ArraySeq), dynamic.to(ArraySeq))
|
||||||
|
|
||||||
|
def buildMod[Model](mod: Mod[Model]): (List[String], List[Dyn[Model, String]]) =
|
||||||
|
mod match
|
||||||
|
case Mod.Tag(v) => buildTag(v)
|
||||||
|
case Mod.Text(v) => (List(v), List.empty)
|
||||||
|
case Mod.DynText[Model](v) => (List.empty, List(v))
|
||||||
|
|
||||||
|
def buildTag[Model](
|
||||||
|
tag: HtmlTag[Model]
|
||||||
|
): (List[String], List[Dyn[Model, String]]) =
|
||||||
|
val modsBuilt: List[(List[String], List[Dyn[Model, String]])] =
|
||||||
|
tag.mods.map(buildMod)
|
||||||
|
val static =
|
||||||
|
List(s"<${tag.name}>") ++
|
||||||
|
modsBuilt.flatMap(_._1) ++
|
||||||
|
List(s"</${tag.name}>")
|
||||||
|
val dynamic = modsBuilt.flatMap(_._2)
|
||||||
|
(static, dynamic)
|
||||||
|
|
||||||
|
trait HtmlTag[Model]:
|
||||||
|
def name: String
|
||||||
|
def mods: List[Mod[Model]]
|
||||||
|
|
||||||
|
class Div[Model](val mods: List[Mod[Model]]) extends HtmlTag[Model]:
|
||||||
|
val name = "div"
|
||||||
|
|
||||||
|
def div[Model](mods: Mod[Model]*): Div[Model] = Div(mods.toList)
|
||||||
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1753939845,
|
||||||
|
"narHash": "sha256-K2ViRJfdVGE8tpJejs8Qpvvejks1+A4GQej/lBk5y7I=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "94def634a20494ee057c76998843c015909d6311",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
48
flake.nix
Normal file
48
flake.nix
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{ nixpkgs, flake-utils, ... }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
mill = pkgs.mill.overrideAttrs (old: rec {
|
||||||
|
version = "1.0.2";
|
||||||
|
src = pkgs.fetchurl {
|
||||||
|
url = "https://repo1.maven.org/maven2/com/lihaoyi/mill-dist-native-linux-amd64/${version}/mill-dist-native-linux-amd64-${version}.exe";
|
||||||
|
hash = "sha256-+jRVJDxpH9DONuar+1CqB0Yl6thAuTn7dJYqOEsebGU=";
|
||||||
|
};
|
||||||
|
buildInputs = [ pkgs.zlib ];
|
||||||
|
nativeBuildInputs = [
|
||||||
|
pkgs.makeWrapper
|
||||||
|
]
|
||||||
|
++ pkgs.lib.optional pkgs.stdenvNoCC.isLinux pkgs.autoPatchelfHook;
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
install -Dm 555 $src $out/bin/.mill-wrapped
|
||||||
|
# can't use wrapProgram because it sets --argv0
|
||||||
|
makeWrapper $out/bin/.mill-wrapped $out/bin/mill \
|
||||||
|
--prefix PATH : "${pkgs.jre}/bin" \
|
||||||
|
--set-default JAVA_HOME "${pkgs.jre}"
|
||||||
|
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
doInstallCheck = false;
|
||||||
|
});
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShell = pkgs.mkShell {
|
||||||
|
buildInputs = [
|
||||||
|
mill
|
||||||
|
pkgs.scalafmt
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue