Skip to content

Commit 1a80cea

Browse files
authored
Merge branch 'adrianliechti:main' into main
2 parents ffd27be + 94a3cc7 commit 1a80cea

10 files changed

Lines changed: 278 additions & 295 deletions

File tree

package-lock.json

Lines changed: 240 additions & 210 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"react-syntax-highlighter": "^15.6.1",
3131
"rehype-raw": "^7.0.0",
3232
"rehype-sanitize": "^6.0.0",
33+
"remark-breaks": "^4.0.0",
3334
"remark-gfm": "^4.0.1",
3435
"wavtools": "^0.1.5",
3536
"zod": "^3.25.76"

src/components/ChatInput.tsx

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import {
1919
import { getConfig } from "../config";
2020
import { useChat } from "../hooks/useChat";
2121
import { useRepositories } from "../hooks/useRepositories";
22-
import { useTextPaste } from "../hooks/useTextPaste";
2322
import { useTranscription } from "../hooks/useTranscription";
2423
import { useDropZone } from "../hooks/useDropZone";
2524
import { useSettings } from "../hooks/useSettings";
@@ -47,9 +46,6 @@ export function ChatInput() {
4746
const contentEditableRef = useRef<HTMLDivElement>(null);
4847
const containerRef = useRef<HTMLDivElement>(null);
4948

50-
// Custom hook for plain text paste handling
51-
const handlePaste = useTextPaste(contentEditableRef, setContent);
52-
5349
// Generate static random placeholder text for new chats, updates when profile name changes
5450
const randomPlaceholder = useMemo(() => {
5551
const personalizedVariations = [
@@ -476,19 +472,20 @@ export function ChatInput() {
476472
suppressContentEditableWarning={true}
477473
onInput={(e) => {
478474
const target = e.target as HTMLDivElement;
475+
476+
const input = target.innerText || target.textContent || '';
477+
setContent(input);
479478

480-
// Simple approach: use innerText which preserves line breaks
481-
const newContent = target.innerText || '';
482-
483-
setContent(newContent);
484-
485-
// Hide prompt suggestions when user starts typing
486-
if (newContent.trim() && showPromptSuggestions) {
479+
if (input.trim() && showPromptSuggestions) {
487480
setShowPromptSuggestions(false);
488481
}
489482
}}
490483
onKeyDown={handleKeyDown}
491-
onPaste={handlePaste}
484+
onPaste={(e) => {
485+
e.preventDefault();
486+
const text = e.clipboardData.getData('text/plain');
487+
document.execCommand('insertText', false, text);
488+
}}
492489
/>
493490

494491
{/* CSS-animated placeholder */}
@@ -508,7 +505,7 @@ export function ChatInput() {
508505
</div>
509506

510507
{/* Controls */}
511-
<div className="flex items-center justify-between p-3 pt-0 pb-6 md:pb-3">
508+
<div className="flex items-center justify-between p-3 pt-0 pb-8 md:pb-3">
512509
<div className="flex items-center gap-2">
513510
<Button
514511
type="button"

src/components/ChatMessage.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import { getConfig } from "../config";
88

99
type ChatMessageProps = {
1010
message: Message;
11+
isLast?: boolean;
1112
};
1213

13-
export function ChatMessage({ message }: ChatMessageProps) {
14+
export function ChatMessage({ message, ...props }: ChatMessageProps) {
1415
const isUser = message.role === Role.User;
1516

1617
const config = getConfig();
@@ -78,10 +79,12 @@ export function ChatMessage({ message }: ChatMessageProps) {
7879
)}
7980

8081
{!isUser && (
81-
<div className="flex justify-between items-center mt-2 chat-message-actions">
82+
<div className={`flex justify-between items-center mt-2 ${
83+
props.isLast ? 'chat-message-actions !opacity-100' : 'chat-message-actions opacity-0'
84+
}`}>
8285
<div className="flex items-center gap-2">
83-
<CopyButton text={message.content} />
84-
{enableTTS && <PlayButton text={message.content} />}
86+
<CopyButton text={message.content} size={4} />
87+
{enableTTS && <PlayButton text={message.content} size={4} />}
8588
</div>
8689
</div>
8790
)}

src/components/CopyButton.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ import { useState } from 'react';
22
import { Copy as CopyIcon, CopyCheck as CopyCheckIcon } from "lucide-react";
33
import { Button } from "@headlessui/react";
44

5-
export const CopyButton = ({ text }: { text: string }) => {
5+
type CopyButtonProps = {
6+
text: string;
7+
size?: number;
8+
};
9+
10+
export const CopyButton = ({ text, ...props }: CopyButtonProps) => {
611
const [copied, setCopied] = useState(false);
712

813
const handleCopy = async () => {
@@ -15,9 +20,8 @@ export const CopyButton = ({ text }: { text: string }) => {
1520
}
1621
};
1722

18-
const buttonClasses = "text-neutral-400 hover:text-neutral-600 dark:text-neutral-400 dark:hover:text-neutral-300 transition-colors opacity-60 hover:opacity-100";
19-
20-
const iconSize = "h-3 w-3";
23+
const buttonClasses = "text-neutral-400 hover:text-neutral-600 dark:text-neutral-400 dark:hover:text-neutral-300 transition-colors opacity-60 hover:opacity-100 p-1";
24+
const iconSize = `h-${props.size ?? 3} w-${props.size ?? 3}`;
2125

2226
return (
2327
<Button

src/components/Markdown.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { memo } from 'react';
22
import ReactMarkdown, { type Components } from 'react-markdown';
33
import remarkGfm from 'remark-gfm';
4+
import remarkBreaks from 'remark-breaks';
45
import rehypeRaw from 'rehype-raw';
56
import rehypeSanitize from 'rehype-sanitize';
67
import { MermaidRenderer } from './MermaidRenderer';
@@ -214,7 +215,7 @@ const components: Partial<Components> = {
214215
},
215216
};
216217

217-
const remarkPlugins = [remarkGfm];
218+
const remarkPlugins = [remarkGfm, remarkBreaks];
218219
const rehypePlugins = [rehypeRaw, rehypeSanitize];
219220

220221
const NonMemoizedMarkdown = ({ children }: { children: string }) => {

src/components/PlayButton.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import { Client } from '../lib/client';
66
type PlayButtonProps = {
77
text: string;
88
voice?: string;
9+
size?: number;
910
};
1011

11-
export function PlayButton({ text }: PlayButtonProps) {
12+
export function PlayButton({ text, ...props }: PlayButtonProps) {
1213
const [isPlaying, setIsPlaying] = useState(false);
1314
const [isLoading, setIsLoading] = useState(false);
1415

@@ -31,8 +32,8 @@ export function PlayButton({ text }: PlayButtonProps) {
3132
}
3233
};
3334

34-
const buttonClasses = "text-neutral-400 hover:text-neutral-600 dark:text-neutral-400 dark:hover:text-neutral-300 transition-colors opacity-60 hover:opacity-100 disabled:opacity-30";
35-
const iconSize = "h-3 w-3";
35+
const buttonClasses = "text-neutral-400 hover:text-neutral-600 dark:text-neutral-400 dark:hover:text-neutral-300 transition-colors opacity-60 hover:opacity-100 disabled:opacity-30 p-1";
36+
const iconSize = `h-${props.size ?? 3} w-${props.size ?? 3}`;
3637

3738
return (
3839
<Button

src/hooks/useTextPaste.ts

Lines changed: 0 additions & 30 deletions
This file was deleted.

src/index.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ body {
132132
}
133133

134134
.chat-message-actions {
135-
@apply opacity-0 transition-opacity duration-200;
135+
@apply transition-opacity duration-200;
136136
}
137137

138138
.group:hover .chat-message-actions {

src/pages/ChatPage.tsx

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,6 @@ export function ChatPage() {
7272
dependencies: [chat, messages],
7373
});
7474

75-
// Animation state for chat input
76-
const [isAnimating, setIsAnimating] = useState(false);
77-
const prevMessagesCountRef = useRef(0);
78-
7975
// Set up navigation actions (only once on mount)
8076
useEffect(() => {
8177
setRightActions(
@@ -161,22 +157,6 @@ export function ChatPage() {
161157
prevMessagesLengthRef.current = messages.length;
162158
}, [messages.length, enableAutoScroll]);
163159

164-
// Handle animation when first message is added
165-
useEffect(() => {
166-
if (prevMessagesCountRef.current === 0 && messages.length > 0) {
167-
setIsAnimating(true);
168-
// Reset animation state after animation completes
169-
const animationTimer = setTimeout(() => {
170-
setIsAnimating(false);
171-
}, 600); // Match the CSS transition duration
172-
173-
return () => {
174-
clearTimeout(animationTimer);
175-
};
176-
}
177-
prevMessagesCountRef.current = messages.length;
178-
}, [messages.length]);
179-
180160
return (
181161
<div className="h-full w-full flex overflow-hidden relative">
182162
{messages.length === 0 && <BackgroundImage />}
@@ -242,7 +222,7 @@ export function ChatPage() {
242222
})()}
243223

244224
{messages.map((message, idx) => (
245-
<ChatMessage key={idx} message={message} />
225+
<ChatMessage key={idx} message={message} isLast={idx === messages.length - 1} />
246226
))}
247227

248228
{/* sentinel for scrollIntoView */}
@@ -254,14 +234,10 @@ export function ChatPage() {
254234

255235
{/* Chat Input - hidden during voice mode */}
256236
{!isVoiceMode && (
257-
<footer className={`absolute left-0 right-0 bg-transparent md:pb-4 pb-0 px-3 pointer-events-none transition-all duration-600 ease-out z-20 ${
258-
messages.length === 0 ? 'bottom-0 md:bottom-1/3 md:transform md:translate-y-1/2' : 'bottom-0'
259-
} ${isAnimating ? 'transition-all duration-600 ease-out' : ''}`}>
260-
<div className={`relative pointer-events-auto ${
261-
layoutMode === 'wide' ? 'max-w-full md:max-w-[80vw] mx-auto' : 'max-content-width'
262-
} ${messages.length === 0 ? 'max-w-4xl' : ''} ${
263-
showRepositoryDrawer ? 'md:mr-80 md:pr-4' : ''
264-
}`}>
237+
<footer className={`fixed bottom-0 left-0 right-0 md:px-3 md:pb-4 pointer-events-none z-20 transition-all duration-600 ease-out ${
238+
messages.length === 0 ? 'md:bottom-1/3 md:transform md:translate-y-1/2' : ''
239+
}`}>
240+
<div className="relative pointer-events-auto md:max-w-4xl mx-auto">
265241
<ChatInput />
266242
</div>
267243
</footer>

0 commit comments

Comments
 (0)