feat: Replace TestaController with UiController for minimal llm chat UI using TailwindCSS

This commit is contained in:
Tristan D. 2025-03-17 22:45:12 +01:00
parent b2bd8bbaae
commit 259fac7c58
Signed by: tristan
SSH key fingerprint: SHA256:9oFM1J63hYWJjCnLG6C0fxBS15rwNcWwdQNMOHYKJ/4

View file

@ -25,12 +25,12 @@ mod html_elements {
} }
} }
struct TestaController {} struct UiController {}
#[controller( #[controller(
state = AppState state = AppState
)] )]
impl TestaController { impl UiController {
#[route(GET "/")] #[route(GET "/")]
async fn index(State(_): State<AppState>) -> impl IntoResponse { async fn index(State(_): State<AppState>) -> impl IntoResponse {
maud! { maud! {
@ -39,61 +39,51 @@ impl TestaController {
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 {
"My Website" "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="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css";
link rel="icon" href="/dist/favicon.ico"; link rel="icon" href="/dist/favicon.ico";
} }
body { body class="bg-gray-100" {
main { div class="container mx-auto p-4" {
h1 { h1 class="text-2xl font-bold mb-4" {
"Welcome to My Website" "LLM Chat App"
} }
div."m-1" { div class="bg-white p-6 rounded-lg shadow-md" {
"div" div id="chat" class="mb-4" {
} // Chat messages will appear here
div."m1" { }
"div" form id="chat-form" {
} textarea id="user-input" class="w-full p-2 border rounded-lg mb-2" placeholder="Type your message..." {}
div ."m1" { button type="submit" class="bg-blue-500 text-white p-2 rounded-lg" {
"m1" "Send"
} }
div ."m2" { }
"m2"
}
div ."m3" {
"m3"
} }
} }
script {
"
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 = '';
// Mock response from LLM
setTimeout(() => {
chatContainer.innerHTML += `<div class='mb-2'><strong>LLM:</strong> This is a mock response.</div>`;
}, 1000);
});
"
}
} }
} }
} }
.render() .render()
} }
#[allow(unused)]
#[route(GET "/item/:id?amount&offset")]
async fn item_handler(
id: u32,
amount: Option<u32>,
offset: Option<u32>,
State(state): State<AppState>,
// Json(json): Json<u32>,
) -> impl IntoResponse {
// todo!("handle request")
maud! {
h1 { "Item" }
div {
}
p {
(format!("{id:?} {amount:?} {offset:?} {state:?}"))
}
}
.render()
}
} }
struct DistController; struct DistController;
@ -205,7 +195,7 @@ async fn main() {
let listener = tokio::net::TcpListener::bind("0.0.0.0:8000").await.unwrap(); let listener = tokio::net::TcpListener::bind("0.0.0.0:8000").await.unwrap();
let router: axum::Router = axum::Router::new() let router: axum::Router = axum::Router::new()
.merge(TestaController::into_router(app_state.clone())) .merge(UiController::into_router(app_state.clone()))
.merge(DistController::into_router(app_state.clone())) .merge(DistController::into_router(app_state.clone()))
.fallback_service(get(handle_404)) .fallback_service(get(handle_404))
.method_not_allowed_fallback(handle_405) .method_not_allowed_fallback(handle_405)