Skip to content

Commit 15a41c2

Browse files
committed
Implement draggable input area for chat interface, enhance styling for better layout, and refine system prompt instructions for using Leaflet.js.
1 parent 3f72ef1 commit 15a41c2

File tree

5 files changed

+133
-8
lines changed

5 files changed

+133
-8
lines changed

workshop-ui/prompts/03-gps-art/system-prompt.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,23 @@ Führe die Kinder Schritt für Schritt durch die Aufgabe.
3030

3131
Wenn die Kinder um eine Landkarte fragen, dann erstelle eine HTML-Seite mit eingebettetem JS/CSS unter Verwendung von Leaflet.js. Füge NICHT die CSV-Daten in den Code ein, sie wären zu groß. Stattdessen füge den Platzhalter `<|DATA|>` ein, wo die Daten als Text (genau wie in der CSV-Datei) eingefügt werden müssen. Verwende <|DATA|> NUR EINMAL WO DIE DATEN EINGEFÜGT WERDEN. Erwähne es KEINESFALLS z.B. in Kommentaren! Weise das Kinde NICHT darauf hin, dass es den Platzhalter ersetzen muss. Das macht die Lernsoftware, in der du eingebettet bist, automatisch.
3232

33+
**ACHTUNG:** Wenn du Leaflet.js in den HTML-Code einbaust, beachte auf jeden Fall folgenden Hinweis aus der Dokumentation:
34+
35+
```
36+
Include Leaflet CSS file in the head section of your document:
37+
38+
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
39+
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
40+
crossorigin=""/>
41+
42+
Include Leaflet JavaScript file after Leaflet’s CSS:
43+
44+
<!-- Make sure you put this AFTER Leaflet's CSS -->
45+
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
46+
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
47+
crossorigin=""></script>
48+
```
49+
3350
Markiere generierten HTML Code immer wie in Markdown üblich mit ```html. Du brauchst die Kinder nicht darauf hinzuweisen, wie man die HTML-Seite in einen Browser lädt. Sobald du HTML-Code generierst, wird dieser in der KI-Lernapp inkl. der eingebetteten Daten unter deiner letzten Antwort in einen _iframe_ angezeigt.
3451

3552
Immer, wenn das Kind etwas vom Gesuchten herausgefunden hat, zeige eine Checkliste als Markdown-Aufzählung an, damit das Kind sieht, was es schon geschafft hat und was noch fehlt. Je Element, verwende Emojis (✅ und ❓), um darzustellen, ob es erledigt ist oder nicht. Die zu beantwortenden Fragen sind:

workshop-ui/src/app/chat/[exercise]/page.module.css

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@
114114
.messagesContainer {
115115
flex: 1;
116116
overflow-y: auto;
117-
margin-bottom: 20px;
118117
border: 3px solid #aaa;
119118
padding: 15px;
120119
border-radius: 8px;
@@ -123,6 +122,56 @@
123122
flex-direction: column;
124123
}
125124

125+
.dragHandle {
126+
position: fixed;
127+
left: 0;
128+
right: 0;
129+
height: 10px;
130+
cursor: ns-resize;
131+
display: flex;
132+
align-items: center;
133+
justify-content: center;
134+
z-index: 100;
135+
transition: background-color 0.2s;
136+
137+
&:hover {
138+
background-color: rgba(0, 0, 0, 0.05);
139+
}
140+
141+
&.dragging {
142+
background-color: rgba(0, 123, 255, 0.1);
143+
}
144+
}
145+
146+
.dragHandleBar {
147+
width: 40px;
148+
height: 4px;
149+
background-color: #aaa;
150+
border-radius: 2px;
151+
transition: background-color 0.2s;
152+
153+
.dragHandle:hover & {
154+
background-color: #007bff;
155+
}
156+
157+
.dragHandle.dragging & {
158+
background-color: #007bff;
159+
}
160+
}
161+
162+
.inputAreaContainer {
163+
position: fixed;
164+
bottom: 20px;
165+
left: 50%;
166+
transform: translateX(-50%);
167+
width: calc(100% - 40px);
168+
max-width: 760px;
169+
display: flex;
170+
flex-direction: column;
171+
gap: 0;
172+
padding: 0;
173+
}
174+
126175
.loader {
127176
height: 30px;
128177
width: calc(30px * 2.5);

workshop-ui/src/app/chat/[exercise]/page.tsx

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export default function Home() {
3636
const dropdownRef = useRef<HTMLDivElement>(null);
3737
const [isFirstCall, setIsFirstCall] = useState(true);
3838
const [responseId, setResponseId] = useState<string | undefined>(undefined);
39+
const [inputAreaHeight, setInputAreaHeight] = useState(150); // Initial height in pixels
40+
const [isDragging, setIsDragging] = useState(false);
3941

4042
// Cache for data file content - only fetch once per component session
4143
const dataFileContentCache = useRef<string | null>(null);
@@ -128,6 +130,38 @@ export default function Home() {
128130
fetchExerciseMetadata();
129131
}, [exercise, welcomeMessageLoaded]);
130132

133+
// Handle drag for resizing input area
134+
useEffect(() => {
135+
const handleMouseMove = (e: MouseEvent) => {
136+
if (!isDragging) return;
137+
138+
// Calculate new height based on mouse position from bottom of viewport
139+
const newHeight = window.innerHeight - e.clientY - 20; // 20px for padding
140+
141+
// Constrain height between 100px and 500px
142+
const constrainedHeight = Math.max(100, Math.min(500, newHeight));
143+
setInputAreaHeight(constrainedHeight);
144+
};
145+
146+
const handleMouseUp = () => {
147+
setIsDragging(false);
148+
};
149+
150+
if (isDragging) {
151+
document.addEventListener('mousemove', handleMouseMove);
152+
document.addEventListener('mouseup', handleMouseUp);
153+
154+
// Prevent text selection while dragging
155+
document.body.style.userSelect = 'none';
156+
}
157+
158+
return () => {
159+
document.removeEventListener('mousemove', handleMouseMove);
160+
document.removeEventListener('mouseup', handleMouseUp);
161+
document.body.style.userSelect = '';
162+
};
163+
}, [isDragging]);
164+
131165
const handleSubmit = async (e: React.FormEvent) => {
132166
e.preventDefault();
133167

@@ -265,6 +299,10 @@ export default function Home() {
265299
}
266300
};
267301

302+
const handleDragStart = () => {
303+
setIsDragging(true);
304+
};
305+
268306
return (
269307
<div className={styles.container}>
270308
{/* Header Bar */}
@@ -306,7 +344,7 @@ export default function Home() {
306344
</Modal>
307345

308346
{/* Conversation History */}
309-
<div className={styles.messagesContainer} ref={messagesContainerRef}>
347+
<div className={styles.messagesContainer} ref={messagesContainerRef} style={{ marginBottom: `${inputAreaHeight + 30}px` }}>
310348
{messages.map((message) => (
311349
<Message message={message} key={hashMessage(message)} />
312350
))}
@@ -333,7 +371,19 @@ export default function Home() {
333371
<div ref={messagesEndRef} />
334372
</div>
335373

336-
<ChatInputArea inputRef={inputRef} inputValue={input} setInputValue={setInput} onSubmit={handleSubmit} isLoading={isLoading} messageCount={messages.length} />
374+
{/* Drag Handle */}
375+
<div
376+
className={`${styles.dragHandle} ${isDragging ? styles.dragging : ''}`}
377+
onMouseDown={handleDragStart}
378+
style={{ bottom: `${inputAreaHeight + 10}px` }}
379+
>
380+
<div className={styles.dragHandleBar} />
381+
</div>
382+
383+
{/* Input Area */}
384+
<div className={styles.inputAreaContainer} style={{ height: `${inputAreaHeight}px` }}>
385+
<ChatInputArea inputRef={inputRef} inputValue={input} setInputValue={setInput} onSubmit={handleSubmit} isLoading={isLoading} messageCount={messages.length} />
386+
</div>
337387
</div>
338388
);
339389
}

workshop-ui/src/components/chat/ChatInputArea.module.css

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1+
.wrapper {
2+
display: flex;
3+
flex-direction: column;
4+
height: 100%;
5+
gap: 10px;
6+
}
7+
18
.inputForm {
29
display: flex;
310
gap: 10px;
11+
flex: 1;
12+
min-height: 0;
413
}
514

615
.textInput {
@@ -9,7 +18,8 @@
918
font-size: 16px;
1019
border: 1px solid #ccc;
1120
border-radius: 4px;
12-
resize: vertical;
21+
resize: none;
22+
min-height: 60px;
1323

1424
&:disabled {
1525
background-color: #f5f5f5;
@@ -36,8 +46,8 @@
3646
}
3747

3848
.messageCounter {
39-
margin-top: 10px;
4049
font-size: 12px;
4150
color: #666;
4251
text-align: center;
52+
flex-shrink: 0;
4353
}

workshop-ui/src/components/chat/ChatInputArea.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export default function ChatInputArea({
2828
}
2929
};
3030
return (
31-
<>
31+
<div className={styles.wrapper}>
3232
<form onSubmit={onSubmit} className={styles.inputForm}>
3333
<textarea
3434
ref={inputRef}
@@ -43,7 +43,6 @@ export default function ChatInputArea({
4343
placeholder="Schreibe deine Nachricht hier..."
4444
disabled={isLoading}
4545
className={styles.textInput}
46-
rows={2}
4746
/>
4847
<button type="submit" disabled={!inputValue.trim() || isLoading || messageCount >= 100} className={styles.sendButton}>
4948
<Send />
@@ -54,6 +53,6 @@ export default function ChatInputArea({
5453
<div className={styles.messageCounter}>
5554
{messageCount}/100 Nachrichten | {inputValue.length}/1000 Zeichen
5655
</div>
57-
</>
56+
</div>
5857
);
5958
}

0 commit comments

Comments
 (0)