Migrate manual script to datastar foo

This commit is contained in:
Tristan D. 2025-03-20 19:24:21 +01:00
parent 75d19433be
commit 6f93cf35e0
Signed by: tristan
SSH key fingerprint: SHA256:9oFM1J63hYWJjCnLG6C0fxBS15rwNcWwdQNMOHYKJ/4
6 changed files with 73 additions and 31 deletions

34
Cargo.lock generated
View file

@ -201,6 +201,28 @@ dependencies = [
"syn 2.0.100", "syn 2.0.100",
] ]
[[package]]
name = "async-stream"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
dependencies = [
"async-stream-impl",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-stream-impl"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.88" version = "0.1.88"
@ -1232,11 +1254,13 @@ dependencies = [
name = "darm_test" name = "darm_test"
version = "0.1.1" version = "0.1.1"
dependencies = [ dependencies = [
"async-stream",
"axum 0.8.1", "axum 0.8.1",
"axum-controller", "axum-controller",
"axum-controller-macros", "axum-controller-macros",
"axum-typed-routing 0.2.0 (git+https://github.com/jvdwrf/axum-typed-routing)", "axum-typed-routing 0.2.0 (git+https://github.com/jvdwrf/axum-typed-routing)",
"datastar", "datastar",
"futures",
"hypertext", "hypertext",
"mime_guess", "mime_guess",
"rust-embed", "rust-embed",
@ -1269,9 +1293,15 @@ dependencies = [
[[package]] [[package]]
name = "datastar" name = "datastar"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/starfederation/datastar.git#db376dfbe4d100ea997c932f1301fbe72e08dfe8" source = "git+https://github.com/starfederation/datastar.git#90b8b1cf3506d528ab9af97c4dc3c9d7f9d42fc5"
dependencies = [ dependencies = [
"axum 0.8.1",
"futures-util", "futures-util",
"http-body",
"pin-project-lite",
"serde",
"serde_json",
"sync_wrapper",
] ]
[[package]] [[package]]
@ -3172,7 +3202,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"windows-targets 0.52.6", "windows-targets 0.48.5",
] ]
[[package]] [[package]]

View file

@ -10,15 +10,17 @@ version.workspace = true
edition.workspace = true edition.workspace = true
[dependencies] [dependencies]
async-stream = "0.3.6"
axum = { version = "0.8", features = ["http2"] } axum = { version = "0.8", features = ["http2"] }
axum-controller = { version = "0.2.0", path = "../../axum-controller/axum-controller" } axum-controller = { version = "0.2.0", path = "../../axum-controller/axum-controller" }
axum-controller-macros = { version = "0.2.0", path = "../../axum-controller/axum-controller-macros" } axum-controller-macros = { version = "0.2.0", path = "../../axum-controller/axum-controller-macros" }
axum-typed-routing = { git = "https://github.com/jvdwrf/axum-typed-routing", version = "0.2.0" } axum-typed-routing = { git = "https://github.com/jvdwrf/axum-typed-routing", version = "0.2.0" }
datastar = { git = "https://github.com/starfederation/datastar.git", version = "0.1.0" } datastar = { git = "https://github.com/starfederation/datastar.git", version = "0.1.0", features = ["axum"] }
futures = "0.3.31"
hypertext = { version = "0.6.0", features = ["axum"] } hypertext = { version = "0.6.0", features = ["axum"] }
mime_guess = "2.0.5" mime_guess = "2.0.5"
rust-embed = { version = "8.5.0", features = ["axum", "compression"] } rust-embed = { version = "8.5.0", features = ["axum", "compression"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.43", features = ["full", "tracing"] } tokio = { version = "1.43", features = ["full", "tracing"] }
tracing = "0.1.41" tracing = { version= "0.1.41", features = [ "max_level_debug", "release_max_level_info" ] }
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }

View file

@ -1 +1 @@
js/datastar-1-0-0-beta-7.js js/datastar-1-0-0-beta-9-3caff1580ebe0e7c.js

View file

@ -54,7 +54,7 @@ pub fn initialize_logger() {
INIT.call_once(|| { INIT.call_once(|| {
let env_filter = tracing_subscriber::EnvFilter::builder() let env_filter = tracing_subscriber::EnvFilter::builder()
.with_default_directive(tracing::level_filters::LevelFilter::INFO.into()) .with_default_directive(tracing::level_filters::LevelFilter::DEBUG.into())
.from_env_lossy(); .from_env_lossy();
tracing_subscriber::fmt() tracing_subscriber::fmt()

View file

@ -4,14 +4,12 @@ use crate::ui::html_elements;
pub fn main_page(body: impl Renderable) -> impl Renderable { pub fn main_page(body: impl Renderable) -> impl Renderable {
maud! { maud! {
!DOCTYPE
html lang="en" { html lang="en" {
head { head {
meta charset="UTF-8"; meta charset="UTF-8";
meta name="viewport" content="width=device-width, initial-scale=1.0"; meta name="viewport" content="width=device-width, initial-scale=1.0";
title { title { "LLM Chat App" }
"LLM Chat App"
}
script type="module" src="/dist/datastar.min.js" {} script type="module" src="/dist/datastar.min.js" {}
link rel="stylesheet" href="/dist/styles.min.css"; link rel="stylesheet" href="/dist/styles.min.css";
link rel="icon" href="/dist/favicon.ico"; link rel="icon" href="/dist/favicon.ico";

View file

@ -1,7 +1,14 @@
mod components; mod components;
use std::time::Duration;
use axum::{extract::State, response::IntoResponse}; use axum::{extract::State, response::IntoResponse};
use axum_controller::*; use axum_controller::*;
use datastar::{
prelude::{FragmentMergeMode, MergeFragments},
Sse,
};
use hypertext::{maud, GlobalAttributes, Renderable}; use hypertext::{maud, GlobalAttributes, Renderable};
use tokio;
use crate::{ui::components::*, AppState}; use crate::{ui::components::*, AppState};
@ -25,6 +32,29 @@ pub struct UiController {}
state = AppState state = AppState
)] )]
impl UiController { impl UiController {
#[route(GET "/msg")]
async fn message(State(_): State<AppState>) -> impl IntoResponse {
tokio::time::sleep(Duration::from_millis(500)).await;
let sse_stream: Sse<_> = Sse(async_stream::stream! {
yield MergeFragments::new(maud! { div #user-1 .mb-2 { strong { "You:" } "userInput" } }.render())
// .id("chat")
.selector("#chat")
.merge_mode(FragmentMergeMode::Append)
.into();
tokio::time::sleep(Duration::from_millis(500)).await;
yield MergeFragments::new(maud! { div #llm-1 class="mb-2" { strong { "LLM:" } "This is a mock response."} }.render())
// .id("chat")
.selector("#chat")
.merge_mode(FragmentMergeMode::Append)
.into();
});
sse_stream
}
#[route(GET "/")] #[route(GET "/")]
async fn index(State(_): State<AppState>) -> impl IntoResponse { async fn index(State(_): State<AppState>) -> impl IntoResponse {
main_page(maud! { main_page(maud! {
@ -32,36 +62,18 @@ impl UiController {
h1 class="text-2xl font-bold mb-4" { h1 class="text-2xl font-bold mb-4" {
"LLM Chat App" "LLM Chat App"
} }
div class="bg-white p-6 rounded-lg shadow-md" { div .bg-white .p-6 .rounded-lg .shadow-md {
div id="chat" class="mb-4" { div #chat .mb-4 {
// Chat messages will appear here // Chat messages will appear here
} }
form id="chat-form" { form id="chat-form" {
textarea id="user-input" class="w-full p-2 border rounded-lg mb-2" placeholder="Type your message..." {} textarea id="user-input" class="w-full p-2 border rounded-lg mb-2" placeholder="Type your message..." {}
button type="submit" class="bg-blue-500 text-white p-2 rounded-lg" { button type="button" data-on-click="@get('/msg')" class="bg-blue-500 text-white p-2 rounded-lg" {
"Send" "Send"
} }
} }
} }
} }
script {
(hypertext::Raw("
console.log(\"asd\");
document.getElementById('chat-form').addEventListener('submit', function(event) {
event.preventDefault();
const userInput = document.getElementById('user-input').value;
const chatContainer = document.getElementById('chat');
chatContainer.innerHTML += `<div class='mb-2'><strong>You:</strong> ${userInput}</div>`;
document.getElementById('user-input').value = '';
console.log(\"asd\");
// Mock response from LLM
setTimeout(() => {
chatContainer.innerHTML += `<div class='mb-2'><strong>LLM:</strong> This is a mock response.</div>`;
}, 1000);
});
"))
}
} }
) )
.render() .render()