Skip to content

Commit 775537a

Browse files
committed
Better scroll style
1 parent c9a3077 commit 775537a

File tree

3 files changed

+120
-116
lines changed

3 files changed

+120
-116
lines changed

chat/src/components/ChatInterface.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ export default function ChatInterface() {
231231
</div>
232232
</header>
233233

234-
<main className="flex flex-1 flex-col w-full max-w-4xl mx-auto overflow-auto pb-4 px-2">
234+
<main className="flex flex-1 flex-col w-full overflow-auto">
235235
{(serverStatus === "offline" || serverStatus === "unknown") && (
236236
<div className="bg-yellow-100 border-y border-yellow-400 text-yellow-800 px-4 py-3 flex items-center justify-between font-medium">
237237
<div className="flex items-center">

chat/src/components/MessageInput.tsx

Lines changed: 90 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -131,100 +131,102 @@ export default function MessageInput({
131131

132132
return (
133133
<Tabs value={inputMode} onValueChange={setInputMode}>
134-
<form
135-
onSubmit={handleSubmit}
136-
className="rounded-lg border text-base shadow-sm placeholder:text-muted-foreground focus-within:outline-none focus-within:ring-1 focus-within:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm"
137-
>
138-
<div className="flex flex-col">
139-
<div className="flex">
140-
{inputMode === "control" && !disabled ? (
141-
<div
142-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
143-
ref={textareaRef as any}
144-
tabIndex={0}
145-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
146-
onKeyDown={handleKeyDown as any}
147-
onFocus={() => setControlAreaFocused(true)}
148-
onBlur={() => setControlAreaFocused(false)}
149-
className="cursor-text p-4 h-20 text-muted-foreground flex items-center justify-center w-full outline-none"
150-
>
151-
{controlAreaFocused
152-
? "Press any key to send to terminal (arrows, Ctrl+C, Ctrl+R, etc.)"
153-
: "Click or focus this area to send keystrokes to terminal"}
154-
</div>
155-
) : (
156-
<textarea
157-
autoFocus
158-
ref={textareaRef}
159-
value={message}
160-
onChange={(e) => setMessage(e.target.value)}
161-
onKeyDown={handleKeyDown}
162-
placeholder={"Type a message..."}
163-
className="resize-none w-full text-sm outline-none p-4 h-20"
164-
/>
165-
)}
166-
</div>
167-
168-
<div className="flex items-center justify-between p-4">
169-
<div className="flex items-center gap-3">
170-
<TabsList>
171-
<TabsTrigger
172-
value="text"
173-
onClick={() => {
174-
textareaRef.current?.focus();
175-
}}
176-
>
177-
Text
178-
</TabsTrigger>
179-
<TabsTrigger
180-
value="control"
181-
onClick={() => {
182-
textareaRef.current?.focus();
183-
}}
134+
<div className="max-w-4xl mx-auto w-full p-4 pt-0">
135+
<form
136+
onSubmit={handleSubmit}
137+
className="rounded-lg border text-base shadow-sm placeholder:text-muted-foreground focus-within:outline-none focus-within:ring-1 focus-within:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm"
138+
>
139+
<div className="flex flex-col">
140+
<div className="flex">
141+
{inputMode === "control" && !disabled ? (
142+
<div
143+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
144+
ref={textareaRef as any}
145+
tabIndex={0}
146+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
147+
onKeyDown={handleKeyDown as any}
148+
onFocus={() => setControlAreaFocused(true)}
149+
onBlur={() => setControlAreaFocused(false)}
150+
className="cursor-text p-4 h-20 text-muted-foreground flex items-center justify-center w-full outline-none"
184151
>
185-
Control
186-
</TabsTrigger>
187-
</TabsList>
188-
189-
<span className="text-xs text-muted-foreground">
190-
{inputMode === "text" ? (
191-
<>
192-
Switch to <span className="font-medium">Control</span> mode
193-
to send raw keystrokes (↑,↓,Tab,Ctrl+C,Ctrl+R) directly to
194-
the terminal
195-
</>
196-
) : (
197-
<>Control mode - keystrokes sent directly to terminal</>
198-
)}
199-
</span>
152+
{controlAreaFocused
153+
? "Press any key to send to terminal (arrows, Ctrl+C, Ctrl+R, etc.)"
154+
: "Click or focus this area to send keystrokes to terminal"}
155+
</div>
156+
) : (
157+
<textarea
158+
autoFocus
159+
ref={textareaRef}
160+
value={message}
161+
onChange={(e) => setMessage(e.target.value)}
162+
onKeyDown={handleKeyDown}
163+
placeholder={"Type a message..."}
164+
className="resize-none w-full text-sm outline-none p-4 h-20"
165+
/>
166+
)}
200167
</div>
201168

202-
{inputMode === "text" && (
203-
<Button
204-
type="submit"
205-
disabled={disabled || !message.trim()}
206-
size="icon"
207-
className="rounded-full"
208-
>
209-
<SendIcon />
210-
</Button>
211-
)}
212-
213-
{inputMode === "control" && !disabled && (
214-
<div className="flex items-center gap-1">
215-
{sentChars.map((char) => (
216-
<span
217-
key={char.id}
218-
className="size-9 rounded border font-mono font-medium text-xs flex items-center justify-center animate-pulse"
169+
<div className="flex items-center justify-between p-4">
170+
<div className="flex items-center gap-3">
171+
<TabsList>
172+
<TabsTrigger
173+
value="text"
174+
onClick={() => {
175+
textareaRef.current?.focus();
176+
}}
219177
>
220-
{char.char}
221-
</span>
222-
))}
178+
Text
179+
</TabsTrigger>
180+
<TabsTrigger
181+
value="control"
182+
onClick={() => {
183+
textareaRef.current?.focus();
184+
}}
185+
>
186+
Control
187+
</TabsTrigger>
188+
</TabsList>
189+
190+
<span className="text-xs text-muted-foreground">
191+
{inputMode === "text" ? (
192+
<>
193+
Switch to <span className="font-medium">Control</span>{" "}
194+
mode to send raw keystrokes (↑,↓,Tab,Ctrl+C,Ctrl+R)
195+
directly to the terminal
196+
</>
197+
) : (
198+
<>Control mode - keystrokes sent directly to terminal</>
199+
)}
200+
</span>
223201
</div>
224-
)}
202+
203+
{inputMode === "text" && (
204+
<Button
205+
type="submit"
206+
disabled={disabled || !message.trim()}
207+
size="icon"
208+
className="rounded-full"
209+
>
210+
<SendIcon />
211+
</Button>
212+
)}
213+
214+
{inputMode === "control" && !disabled && (
215+
<div className="flex items-center gap-1">
216+
{sentChars.map((char) => (
217+
<span
218+
key={char.id}
219+
className="size-9 rounded border font-mono font-medium text-xs flex items-center justify-center animate-pulse"
220+
>
221+
{char.char}
222+
</span>
223+
))}
224+
</div>
225+
)}
226+
</div>
225227
</div>
226-
</div>
227-
</form>
228+
</form>
229+
</div>
228230
</Tabs>
229231
);
230232
}

chat/src/components/MessageList.tsx

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -85,42 +85,44 @@ export default function MessageList({
8585
);
8686

8787
return (
88-
<div className="flex-1 overflow-y-auto py-4 flex flex-col gap-4">
89-
{messages.map((message) => (
90-
<div
91-
key={message.id}
92-
className={`${message.role === "user" ? "text-right" : ""}`}
93-
>
88+
<div className="overflow-y-auto">
89+
<div className="p-4 flex flex-col gap-4 max-w-4xl mx-auto">
90+
{messages.map((message) => (
9491
<div
95-
className={`inline-block rounded-lg ${
96-
message.role === "user"
97-
? "bg-accent-foreground rounded-lg max-w-[90%] p-4 text-accent"
98-
: "max-w-[90%]"
99-
}`}
92+
key={message.id}
93+
className={`${message.role === "user" ? "text-right" : ""}`}
10094
>
10195
<div
102-
className={`whitespace-pre-wrap break-words text-left text-sm ${
103-
message.role === "user" ? "" : "font-mono"
96+
className={`inline-block rounded-lg ${
97+
message.role === "user"
98+
? "bg-accent-foreground rounded-lg max-w-[90%] p-4 text-accent"
99+
: "max-w-[90%]"
104100
}`}
105101
>
106-
{message.role !== "user" && message.content === "" ? (
107-
<LoadingDots />
108-
) : (
109-
message.content
110-
)}
102+
<div
103+
className={`whitespace-pre-wrap break-words text-left text-sm ${
104+
message.role === "user" ? "" : "font-mono"
105+
}`}
106+
>
107+
{message.role !== "user" && message.content === "" ? (
108+
<LoadingDots />
109+
) : (
110+
message.content
111+
)}
112+
</div>
111113
</div>
112114
</div>
113-
</div>
114-
))}
115+
))}
115116

116-
{/* Loading indicator for message being sent */}
117-
{loading && (
118-
<div className="w-fit self-end">
119-
<LoadingDots />
120-
</div>
121-
)}
117+
{/* Loading indicator for message being sent */}
118+
{loading && (
119+
<div className="w-fit self-end">
120+
<LoadingDots />
121+
</div>
122+
)}
122123

123-
<div ref={messagesEndRef} />
124+
<div ref={messagesEndRef} />
125+
</div>
124126
</div>
125127
);
126128
}

0 commit comments

Comments
 (0)