Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ backend/dist/
frontend/demo-recordings/
frontend/test-results/
frontend/playwright-report/
frontend/playwright/.cache/
frontend/playwright/.cache/
.env
2 changes: 1 addition & 1 deletion backend/deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"dependencies": {
"@heroicons/react": "^2.2.0",
"dayjs": "^1.11.13",
"lucide-react": "^0.544.0",
"react": "^19.1.0",
"react-dom": "^19.1.1",
"react-router-dom": "^7.6.2"
Expand Down
6 changes: 2 additions & 4 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { Suspense, lazy } from "react";
import { ProjectSelector } from "./components/ProjectSelector";
import { ChatPage } from "./components/ChatPage";
import { SplitView } from "./components/SplitView";
import { SettingsProvider } from "./contexts/SettingsContext";
import { isDevelopment } from "./utils/environment";

Expand All @@ -19,8 +18,7 @@ function App() {
<SettingsProvider>
<Router>
<Routes>
<Route path="/" element={<ProjectSelector />} />
<Route path="/projects/*" element={<ChatPage />} />
<Route path="/" element={<SplitView />} />
{DemoPage && (
<Route
path="/demo"
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/components/ChatPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,15 @@ export function ChatPage() {
{/* Header */}
<div className="flex items-center justify-between mb-4 sm:mb-8 flex-shrink-0">
<div className="flex items-center gap-4">
{!isHistoryView && !isLoadedConversation && (
<button
onClick={handleBackToProjects}
className="p-2 rounded-lg bg-white/80 dark:bg-slate-800/80 border border-slate-200 dark:border-slate-700 hover:bg-white dark:hover:bg-slate-800 transition-all duration-200 backdrop-blur-sm shadow-sm hover:shadow-md"
aria-label="Back to project selection"
>
<ChevronLeftIcon className="w-5 h-5 text-slate-600 dark:text-slate-400" />
</button>
)}
{isHistoryView && (
<button
onClick={handleBackToChat}
Expand Down
44 changes: 29 additions & 15 deletions frontend/src/components/MessageComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ import {
isEditToolUseResult,
isBashToolUseResult,
} from "../utils/contentUtils";
import {
Settings,
Wrench,
CheckCircle,
ClipboardList,
Brain,
Clock,
CheckCheck,
RotateCcw,
Loader,
} from "lucide-react";

// ANSI escape sequence regex for cleaning hooks messages
const ANSI_REGEX = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
Expand Down Expand Up @@ -66,7 +77,7 @@ export function ChatMessageComponent({ message }: ChatMessageComponentProps) {
}`}
/>
</div>
<pre className="whitespace-pre-wrap text-sm font-mono leading-relaxed">
<pre className="whitespace-pre-wrap break-words overflow-x-auto text-sm font-mono leading-relaxed">
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The break-words and overflow-x-auto classes are redundant. break-words tells text to break anywhere to prevent overflow, while overflow-x-auto adds a scrollbar when content overflows horizontally. These work against each other.

If you want text to break, use only break-words. If you want a scrollbar for code/preformatted text that shouldn't break, use only overflow-x-auto. For most readable text in messages, break-words alone is appropriate.

Suggested change
<pre className="whitespace-pre-wrap break-words overflow-x-auto text-sm font-mono leading-relaxed">
<pre className="whitespace-pre-wrap break-words text-sm font-mono leading-relaxed">

Copilot uses AI. Check for mistakes.
{message.content}
</pre>
</MessageContainer>
Expand Down Expand Up @@ -127,7 +138,7 @@ export function SystemMessageComponent({
label={getLabel()}
details={details}
badge={"subtype" in message ? message.subtype : undefined}
icon={<span className="bg-blue-400 dark:bg-blue-500">⚙</span>}
icon={<Settings className="w-4 h-4 text-white" />}
colorScheme={{
header: "text-blue-800 dark:text-blue-300",
content: "text-blue-700 dark:text-blue-300",
Expand All @@ -150,7 +161,7 @@ export function ToolMessageComponent({ message }: ToolMessageComponentProps) {
>
<div className="text-xs font-semibold mb-2 opacity-90 text-emerald-700 dark:text-emerald-300 flex items-center gap-2">
<div className="w-4 h-4 bg-emerald-500 dark:bg-emerald-600 rounded-full flex items-center justify-center text-white text-xs">
🔧
<Wrench className="w-3 h-3" />
</div>
{message.content}
</div>
Expand Down Expand Up @@ -221,7 +232,7 @@ export function ToolResultMessageComponent({
label={message.toolName}
details={displayContent}
badge={message.toolName === "Edit" ? undefined : message.summary}
icon={<span className="bg-emerald-400 dark:bg-emerald-500">✓</span>}
icon={<CheckCircle className="w-4 h-4 text-white" />}
colorScheme={{
header: "text-emerald-800 dark:text-emerald-300",
content: "text-emerald-700 dark:text-emerald-300",
Expand Down Expand Up @@ -250,7 +261,7 @@ export function PlanMessageComponent({ message }: PlanMessageComponentProps) {
<div className="mb-3 flex items-center justify-between gap-4">
<div className="text-xs font-semibold opacity-90 text-blue-700 dark:text-blue-300 flex items-center gap-2">
<div className="w-4 h-4 bg-blue-500 dark:bg-blue-600 rounded-full flex items-center justify-center text-white text-xs">
📋
<ClipboardList className="w-3 h-3" />
</div>
Ready to code?
</div>
Expand All @@ -265,7 +276,7 @@ export function PlanMessageComponent({ message }: PlanMessageComponentProps) {
Here is Claude's plan:
</p>
<div className="bg-blue-100/50 dark:bg-blue-800/30 border border-blue-200 dark:border-blue-700 rounded-lg p-3">
<pre className="text-sm text-blue-900 dark:text-blue-100 whitespace-pre-wrap font-mono leading-relaxed">
<pre className="text-sm text-blue-900 dark:text-blue-100 whitespace-pre-wrap break-words overflow-x-auto font-mono leading-relaxed">
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same redundancy: break-words and overflow-x-auto conflict with each other.

Suggested change
<pre className="text-sm text-blue-900 dark:text-blue-100 whitespace-pre-wrap break-words overflow-x-auto font-mono leading-relaxed">
<pre className="text-sm text-blue-900 dark:text-blue-100 whitespace-pre-wrap overflow-x-auto font-mono leading-relaxed">

Copilot uses AI. Check for mistakes.
{message.plan}
</pre>
</div>
Expand All @@ -286,7 +297,7 @@ export function ThinkingMessageComponent({
label="Claude's Reasoning"
details={message.content}
badge="thinking"
icon={<span className="bg-purple-400 dark:bg-purple-500">💭</span>}
icon={<Brain className="w-4 h-4 text-white" />}
colorScheme={{
header: "text-purple-700 dark:text-purple-300",
content: "text-purple-600 dark:text-purple-400 italic",
Expand All @@ -306,12 +317,15 @@ export function TodoMessageComponent({ message }: TodoMessageComponentProps) {
const getStatusIcon = (status: TodoItem["status"]) => {
switch (status) {
case "completed":
return { icon: "✅", label: "Completed" };
return { icon: <CheckCheck className="w-4 h-4" />, label: "Completed" };
case "in_progress":
return { icon: "🔄", label: "In progress" };
return {
icon: <RotateCcw className="w-4 h-4" />,
label: "In progress",
};
case "pending":
default:
return { icon: "⏳", label: "Pending" };
return { icon: <Clock className="w-4 h-4" />, label: "Pending" };
}
};

Expand All @@ -338,7 +352,7 @@ export function TodoMessageComponent({ message }: TodoMessageComponentProps) {
className="w-4 h-4 bg-amber-500 dark:bg-amber-600 rounded-full flex items-center justify-center text-white text-xs"
aria-hidden="true"
>
📋
<ClipboardList className="w-3 h-3" />
</div>
Todo List Updated
</div>
Expand All @@ -353,12 +367,12 @@ export function TodoMessageComponent({ message }: TodoMessageComponentProps) {
const statusIcon = getStatusIcon(todo.status);
return (
<div key={index} className="flex items-start gap-2">
<span
className="text-sm flex-shrink-0 mt-0.5"
<div
className="flex-shrink-0 mt-0.5"
aria-label={statusIcon.label}
>
{statusIcon.icon}
</span>
</div>
<div className="flex-1 min-w-0">
<div className={`text-sm ${getStatusColor(todo.status)}`}>
{todo.content}
Expand Down Expand Up @@ -392,7 +406,7 @@ export function LoadingComponent() {
Claude
</div>
<div className="flex items-center gap-2 text-sm">
<div className="w-4 h-4 border-2 border-current border-t-transparent rounded-full animate-spin"></div>
<Loader className="w-4 h-4 animate-spin" />
<span className="animate-pulse">Thinking...</span>
</div>
</MessageContainer>
Expand Down
Loading