Skip to content

Commit fbd069f

Browse files
committed
copilot: #file:chats.html を LINE ぽいモダンな UI とエフェクトに書き換えて
1 parent 08889e6 commit fbd069f

File tree

1 file changed

+293
-16
lines changed

1 file changed

+293
-16
lines changed

template_fastapi/templates/chats.html

Lines changed: 293 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,310 @@
22
<html>
33
<head>
44
<title>Chat</title>
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<style>
7+
* {
8+
margin: 0;
9+
padding: 0;
10+
box-sizing: border-box;
11+
}
12+
13+
body {
14+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
15+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
16+
height: 100vh;
17+
display: flex;
18+
flex-direction: column;
19+
}
20+
21+
.chat-header {
22+
background: rgba(255, 255, 255, 0.95);
23+
padding: 1rem;
24+
text-align: center;
25+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
26+
backdrop-filter: blur(10px);
27+
}
28+
29+
.chat-header h1 {
30+
color: #333;
31+
font-size: 1.5rem;
32+
margin-bottom: 0.5rem;
33+
}
34+
35+
.user-id {
36+
color: #666;
37+
font-size: 0.9rem;
38+
}
39+
40+
.chat-container {
41+
flex: 1;
42+
overflow-y: auto;
43+
padding: 1rem;
44+
display: flex;
45+
flex-direction: column;
46+
gap: 1rem;
47+
}
48+
49+
.message {
50+
display: flex;
51+
align-items: flex-end;
52+
animation: fadeInUp 0.3s ease-out;
53+
}
54+
55+
.message.own {
56+
justify-content: flex-end;
57+
}
58+
59+
.message.other {
60+
justify-content: flex-start;
61+
}
62+
63+
.message-bubble {
64+
max-width: 70%;
65+
padding: 0.75rem 1rem;
66+
border-radius: 1.5rem;
67+
position: relative;
68+
word-wrap: break-word;
69+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
70+
}
71+
72+
.message.own .message-bubble {
73+
background: #007AFF;
74+
color: white;
75+
border-bottom-right-radius: 0.5rem;
76+
}
77+
78+
.message.other .message-bubble {
79+
background: #E9E9EB;
80+
color: #333;
81+
border-bottom-left-radius: 0.5rem;
82+
}
83+
84+
.message-time {
85+
font-size: 0.7rem;
86+
color: rgba(255, 255, 255, 0.7);
87+
margin: 0.25rem 0.5rem;
88+
}
89+
90+
.message.other .message-time {
91+
color: #999;
92+
}
93+
94+
.input-container {
95+
background: rgba(255, 255, 255, 0.95);
96+
padding: 1rem;
97+
display: flex;
98+
align-items: center;
99+
gap: 0.5rem;
100+
backdrop-filter: blur(10px);
101+
border-top: 1px solid rgba(0, 0, 0, 0.1);
102+
}
103+
104+
.message-input {
105+
flex: 1;
106+
padding: 0.75rem 1rem;
107+
border: 2px solid #E9E9EB;
108+
border-radius: 2rem;
109+
font-size: 1rem;
110+
outline: none;
111+
transition: all 0.3s ease;
112+
}
113+
114+
.message-input:focus {
115+
border-color: #007AFF;
116+
box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.1);
117+
}
118+
119+
.send-button {
120+
width: 3rem;
121+
height: 3rem;
122+
background: #007AFF;
123+
border: none;
124+
border-radius: 50%;
125+
color: white;
126+
font-size: 1.2rem;
127+
cursor: pointer;
128+
transition: all 0.3s ease;
129+
display: flex;
130+
align-items: center;
131+
justify-content: center;
132+
}
133+
134+
.send-button:hover {
135+
background: #0056CC;
136+
transform: scale(1.05);
137+
}
138+
139+
.send-button:active {
140+
transform: scale(0.95);
141+
}
142+
143+
.send-button.sending {
144+
animation: pulse 1s infinite;
145+
}
146+
147+
@keyframes fadeInUp {
148+
from {
149+
opacity: 0;
150+
transform: translateY(20px);
151+
}
152+
to {
153+
opacity: 1;
154+
transform: translateY(0);
155+
}
156+
}
157+
158+
@keyframes pulse {
159+
0% {
160+
transform: scale(1);
161+
}
162+
50% {
163+
transform: scale(1.1);
164+
}
165+
100% {
166+
transform: scale(1);
167+
}
168+
}
169+
170+
.typing-indicator {
171+
display: flex;
172+
align-items: center;
173+
gap: 0.5rem;
174+
padding: 0.75rem 1rem;
175+
background: #E9E9EB;
176+
border-radius: 1.5rem;
177+
border-bottom-left-radius: 0.5rem;
178+
max-width: 70%;
179+
animation: fadeInUp 0.3s ease-out;
180+
}
181+
182+
.typing-dot {
183+
width: 0.5rem;
184+
height: 0.5rem;
185+
background: #999;
186+
border-radius: 50%;
187+
animation: typing 1.5s infinite;
188+
}
189+
190+
.typing-dot:nth-child(2) {
191+
animation-delay: 0.2s;
192+
}
193+
194+
.typing-dot:nth-child(3) {
195+
animation-delay: 0.4s;
196+
}
197+
198+
@keyframes typing {
199+
0%, 60%, 100% {
200+
transform: translateY(0);
201+
opacity: 0.5;
202+
}
203+
30% {
204+
transform: translateY(-10px);
205+
opacity: 1;
206+
}
207+
}
208+
209+
@media (max-width: 768px) {
210+
.message-bubble {
211+
max-width: 85%;
212+
}
213+
214+
.chat-header h1 {
215+
font-size: 1.2rem;
216+
}
217+
}
218+
</style>
5219
</head>
6220
<body>
7-
<h1>WebSocket Chat</h1>
8-
<h2>Your ID: <span id="ws-id"></span></h2>
9-
<form action="" onsubmit="sendMessage(event)">
10-
<input type="text" id="messageText" autocomplete="off"/>
11-
<button>Send</button>
221+
<div class="chat-header">
222+
<h1>💬 WebSocket Chat</h1>
223+
<div class="user-id">Your ID: <span id="ws-id"></span></div>
224+
</div>
225+
226+
<div class="chat-container" id="chat-container">
227+
</div>
228+
229+
<form class="input-container" onsubmit="sendMessage(event)">
230+
<input type="text" id="messageText" class="message-input" placeholder="メッセージを入力..." autocomplete="off"/>
231+
<button type="submit" class="send-button" id="send-button">
232+
<span></span>
233+
</button>
12234
</form>
13-
<ul id='messages'>
14-
</ul>
235+
15236
<script>
16237
var client_id = Date.now()
17238
document.querySelector("#ws-id").textContent = client_id;
18239
var ws = new WebSocket(`{{ websocket_url }}/ws/${client_id}`);
240+
var chatContainer = document.getElementById('chat-container');
241+
var sendButton = document.getElementById('send-button');
242+
19243
ws.onmessage = function(event) {
20-
var messages = document.getElementById('messages')
21-
var message = document.createElement('li')
22-
var content = document.createTextNode(event.data)
23-
message.appendChild(content)
24-
messages.appendChild(message)
244+
addMessage(event.data, false);
25245
};
246+
247+
function addMessage(text, isOwn) {
248+
var messageDiv = document.createElement('div');
249+
messageDiv.className = `message ${isOwn ? 'own' : 'other'}`;
250+
251+
var bubble = document.createElement('div');
252+
bubble.className = 'message-bubble';
253+
bubble.textContent = text;
254+
255+
var time = document.createElement('div');
256+
time.className = 'message-time';
257+
time.textContent = new Date().toLocaleTimeString('ja-JP', {
258+
hour: '2-digit',
259+
minute: '2-digit'
260+
});
261+
262+
messageDiv.appendChild(bubble);
263+
messageDiv.appendChild(time);
264+
chatContainer.appendChild(messageDiv);
265+
266+
// スクロールを一番下に
267+
chatContainer.scrollTop = chatContainer.scrollHeight;
268+
}
269+
26270
function sendMessage(event) {
27-
var input = document.getElementById("messageText")
28-
ws.send(input.value)
29-
input.value = ''
30-
event.preventDefault()
271+
event.preventDefault();
272+
var input = document.getElementById("messageText");
273+
var message = input.value.trim();
274+
275+
if (message) {
276+
// 送信エフェクト
277+
sendButton.classList.add('sending');
278+
279+
// 自分のメッセージを即座に表示
280+
addMessage(message, true);
281+
282+
// WebSocketで送信
283+
ws.send(message);
284+
285+
// 入力フィールドをクリア
286+
input.value = '';
287+
288+
// 送信エフェクトを解除
289+
setTimeout(() => {
290+
sendButton.classList.remove('sending');
291+
}, 500);
292+
}
31293
}
294+
295+
// Enterキーでの送信
296+
document.getElementById("messageText").addEventListener('keypress', function(e) {
297+
if (e.key === 'Enter' && !e.shiftKey) {
298+
e.preventDefault();
299+
sendMessage(e);
300+
}
301+
});
302+
303+
// 接続時のウェルカムメッセージ
304+
ws.onopen = function() {
305+
setTimeout(() => {
306+
addMessage("チャットに接続しました! 🎉", false);
307+
}, 500);
308+
};
32309
</script>
33310
</body>
34311
</html>

0 commit comments

Comments
 (0)