Skip to content

Commit 9d5a33c

Browse files
authored
[rust] add streaming ui (vercel#1320)
### Description Add streaming UI to axum example ### Demo URL <!-- Provide a URL to a live deployment where we can test your PR. If a demo isn't possible feel free to omit this section. --> ### Type of Change - [ ] New Example - [ ] Example updates (Bug fixes, new features, etc.) - [ ] Other (changes to the codebase, but not to examples) ### New Example Checklist - [ ] 🛫 `npm run new-example` was used to create the example - [ ] 📚 The template wasn't used but I carefuly read the [Adding a new example](https://github.com/vercel/examples#adding-a-new-example) steps and implemented them in the example - [ ] 📱 Is it responsive? Are mobile and tablets considered?
1 parent 68af8d4 commit 9d5a33c

1 file changed

Lines changed: 131 additions & 2 deletions

File tree

rust/axum/api/axum.rs

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use axum::extract::Json;
22
use axum::http::Uri;
3-
use axum::response::IntoResponse;
3+
use axum::response::{Html, IntoResponse};
44
use axum::{
55
Router,
66
routing::{get, post},
@@ -13,7 +13,136 @@ use vercel_runtime::Error;
1313
use vercel_runtime::axum::{VercelLayer, stream_response};
1414

1515
async fn home() -> impl IntoResponse {
16-
"Hello from Axum on Vercel"
16+
let html = r#"
17+
<!DOCTYPE html>
18+
<html lang="en">
19+
<head>
20+
<meta charset="UTF-8">
21+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
22+
<title>Vercel Axum</title>
23+
<style>
24+
* {
25+
margin: 0;
26+
padding: 0;
27+
box-sizing: border-box;
28+
}
29+
body {
30+
background-color: #000000;
31+
color: #ffffff;
32+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Inter', sans-serif;
33+
min-height: 100vh;
34+
display: flex;
35+
flex-direction: column;
36+
align-items: center;
37+
justify-content: center;
38+
padding: 2rem;
39+
}
40+
.container {
41+
width: 100%;
42+
max-width: 600px;
43+
}
44+
h1 {
45+
font-size: 2.25rem;
46+
font-weight: 500;
47+
margin-bottom: 2rem;
48+
text-align: left;
49+
letter-spacing: -0.025em;
50+
}
51+
button {
52+
background-color: #171717;
53+
color: #ffffff;
54+
border: 1px solid #333333;
55+
padding: 8px 16px;
56+
font-size: 0.875rem;
57+
font-weight: 500;
58+
border-radius: 4px;
59+
cursor: pointer;
60+
transition: all 0.15s ease;
61+
margin-bottom: 1.5rem;
62+
font-family: inherit;
63+
}
64+
button:hover {
65+
background-color: #262626;
66+
border-color: #404040;
67+
}
68+
button:disabled {
69+
background-color: #0a0a0a;
70+
color: #666666;
71+
border-color: #262626;
72+
cursor: not-allowed;
73+
}
74+
#stream-container {
75+
background-color: #0a0a0a;
76+
border: 1px solid #262626;
77+
border-radius: 4px;
78+
padding: 1rem;
79+
margin-top: 1rem;
80+
min-height: 200px;
81+
display: block;
82+
}
83+
#stream-content {
84+
white-space: pre-wrap;
85+
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
86+
font-size: 0.8rem;
87+
line-height: 1.5;
88+
color: #e5e5e5;
89+
}
90+
.loading {
91+
color: #888888;
92+
font-style: italic;
93+
}
94+
</style>
95+
</head>
96+
<body>
97+
<div class="container">
98+
<h1>Vercel Axum <a href="https://github.com/vercel/examples/tree/main/rust/axum" style="color: #60a5fa; text-decoration: none; font-size: 0.875rem; margin-left: 1rem;">View on GitHub</a></h1>
99+
<button id="stream-btn">Start streaming</button>
100+
<div id="stream-container">
101+
<div id="stream-content"></div>
102+
</div>
103+
</div>
104+
<script>
105+
const streamBtn = document.getElementById('stream-btn');
106+
const streamContainer = document.getElementById('stream-container');
107+
const streamContent = document.getElementById('stream-content');
108+
let isStreaming = false;
109+
streamBtn.addEventListener('click', async () => {
110+
if (isStreaming) return;
111+
isStreaming = true;
112+
streamBtn.textContent = 'Streaming...';
113+
streamBtn.disabled = true;
114+
streamContent.innerHTML = '';
115+
streamContent.className = 'loading';
116+
streamContent.textContent = 'Starting stream...';
117+
try {
118+
const response = await fetch('/stream');
119+
const reader = response.body.getReader();
120+
const decoder = new TextDecoder();
121+
let firstChunk = true;
122+
while (true) {
123+
const { done, value } = await reader.read();
124+
if (done) break;
125+
const chunk = decoder.decode(value);
126+
if (firstChunk) {
127+
streamContent.className = '';
128+
streamContent.textContent = '';
129+
firstChunk = false;
130+
}
131+
streamContent.textContent += chunk;
132+
}
133+
} catch (error) {
134+
streamContent.textContent = 'Error: ' + error.message;
135+
} finally {
136+
isStreaming = false;
137+
streamBtn.textContent = 'Start streaming';
138+
streamBtn.disabled = false;
139+
}
140+
});
141+
</script>
142+
</body>
143+
</html>"#;
144+
145+
Html(html)
17146
}
18147

19148
async fn stream_example() -> impl IntoResponse {

0 commit comments

Comments
 (0)