Skip to content

Commit 037bb9c

Browse files
authored
Even more UI updates (#280)
1 parent 289a493 commit 037bb9c

29 files changed

+3212
-1966
lines changed

components/frontend/package-lock.json

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

components/frontend/src/app/globals.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,9 @@
121121
@apply bg-background text-foreground;
122122
}
123123
}
124+
125+
/* Thin scrollbar styling */
126+
.scrollbar-thin {
127+
scrollbar-width: thin;
128+
scrollbar-color: #d1d5db #f3f4f6;
129+
}

components/frontend/src/app/layout.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import "./globals.css";
44
import { Navigation } from "@/components/navigation";
55
import { QueryProvider } from "@/components/providers/query-provider";
66
import { Toaster } from "@/components/ui/toaster";
7-
import { VersionFooter } from "@/components/version-footer";
87
import { env } from "@/lib/env";
98

109
const inter = Inter({ subsets: ["latin"] });
@@ -31,7 +30,6 @@ export default function RootLayout({
3130
<QueryProvider>
3231
<Navigation feedbackUrl={feedbackUrl} />
3332
<main className="flex-1 bg-background overflow-auto">{children}</main>
34-
<VersionFooter />
3533
<Toaster />
3634
</QueryProvider>
3735
</body>
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
"use client";
2+
3+
import { Folder, NotepadText, Download, FolderSync, Loader2 } from "lucide-react";
4+
import { AccordionItem, AccordionTrigger, AccordionContent } from "@/components/ui/accordion";
5+
import { Button } from "@/components/ui/button";
6+
import { FileTree, type FileTreeNode } from "@/components/file-tree";
7+
8+
type WorkspaceFile = {
9+
name: string;
10+
path: string;
11+
isDir: boolean;
12+
size?: number;
13+
};
14+
15+
type ArtifactsAccordionProps = {
16+
files: WorkspaceFile[];
17+
currentSubPath: string;
18+
viewingFile: { path: string; content: string } | null;
19+
isLoadingFile: boolean;
20+
onFileOrFolderSelect: (node: FileTreeNode) => void;
21+
onRefresh: () => void;
22+
onDownloadFile: () => void;
23+
onNavigateBack: () => void;
24+
};
25+
26+
export function ArtifactsAccordion({
27+
files,
28+
currentSubPath,
29+
viewingFile,
30+
isLoadingFile,
31+
onFileOrFolderSelect,
32+
onRefresh,
33+
onDownloadFile,
34+
onNavigateBack,
35+
}: ArtifactsAccordionProps) {
36+
return (
37+
<AccordionItem value="artifacts" className="border rounded-lg px-3 bg-white">
38+
<AccordionTrigger className="text-base font-semibold hover:no-underline py-3">
39+
<div className="flex items-center gap-2">
40+
<NotepadText className="h-4 w-4" />
41+
<span>Artifacts</span>
42+
</div>
43+
</AccordionTrigger>
44+
<AccordionContent className="pt-2 pb-3">
45+
<div className="space-y-3">
46+
<p className="text-sm text-muted-foreground">
47+
Artifacts created by the AI will be added here.
48+
</p>
49+
50+
{/* File Browser for Artifacts */}
51+
<div className="border rounded-lg overflow-hidden">
52+
{/* Header with breadcrumbs and actions */}
53+
<div className="px-2 py-1.5 border-b flex items-center justify-between bg-muted/30">
54+
<div className="flex items-center gap-1 text-xs text-muted-foreground min-w-0 flex-1">
55+
{/* Back button when in subfolder or viewing file */}
56+
{(currentSubPath || viewingFile) && (
57+
<Button
58+
variant="ghost"
59+
size="sm"
60+
onClick={onNavigateBack}
61+
className="h-6 px-1.5 mr-1"
62+
>
63+
← Back
64+
</Button>
65+
)}
66+
67+
{/* Breadcrumb path */}
68+
<Folder className="inline h-3 w-3 mr-1 flex-shrink-0" />
69+
<code className="bg-muted px-1 py-0.5 rounded text-xs truncate">
70+
artifacts
71+
{currentSubPath && `/${currentSubPath}`}
72+
{viewingFile && `/${viewingFile.path}`}
73+
</code>
74+
</div>
75+
76+
{/* Action buttons */}
77+
{viewingFile ? (
78+
/* Download button when viewing file */
79+
<Button
80+
variant="ghost"
81+
size="sm"
82+
onClick={onDownloadFile}
83+
className="h-6 px-2 flex-shrink-0"
84+
title="Download file"
85+
>
86+
<Download className="h-3 w-3" />
87+
</Button>
88+
) : (
89+
/* Refresh button when not viewing file */
90+
<Button
91+
variant="ghost"
92+
size="sm"
93+
onClick={onRefresh}
94+
className="h-6 px-2 flex-shrink-0"
95+
>
96+
<FolderSync className="h-3 w-3" />
97+
</Button>
98+
)}
99+
</div>
100+
101+
{/* Content area */}
102+
<div className="p-2 max-h-64 overflow-y-auto">
103+
{isLoadingFile ? (
104+
<div className="flex items-center justify-center py-8">
105+
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
106+
</div>
107+
) : viewingFile ? (
108+
/* File content view */
109+
<div className="text-xs">
110+
<pre className="bg-muted/50 p-2 rounded overflow-x-auto">
111+
<code>{viewingFile.content}</code>
112+
</pre>
113+
</div>
114+
) : files.length === 0 ? (
115+
/* Empty state */
116+
<div className="text-center py-4 text-sm text-muted-foreground">
117+
<NotepadText className="h-8 w-8 mx-auto mb-2 opacity-30" />
118+
<p>No artifacts yet</p>
119+
<p className="text-xs mt-1">AI-generated artifacts will appear here</p>
120+
</div>
121+
) : (
122+
/* File tree */
123+
<FileTree
124+
nodes={files.map((item): FileTreeNode => ({
125+
name: item.name,
126+
path: item.path,
127+
type: item.isDir ? 'folder' : 'file',
128+
sizeKb: item.size ? item.size / 1024 : undefined,
129+
}))}
130+
onSelect={onFileOrFolderSelect}
131+
/>
132+
)}
133+
</div>
134+
</div>
135+
</div>
136+
</AccordionContent>
137+
</AccordionItem>
138+
);
139+
}
140+
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import { GitBranch, X, Link, Loader2 } from "lucide-react";
5+
import { AccordionItem, AccordionTrigger, AccordionContent } from "@/components/ui/accordion";
6+
import { Badge } from "@/components/ui/badge";
7+
import { Button } from "@/components/ui/button";
8+
9+
type Repository = {
10+
input: {
11+
url: string;
12+
branch?: string;
13+
};
14+
};
15+
16+
type RepositoriesAccordionProps = {
17+
repositories?: Repository[];
18+
onAddRepository: () => void;
19+
onRemoveRepository: (repoName: string) => void;
20+
};
21+
22+
export function RepositoriesAccordion({
23+
repositories = [],
24+
onAddRepository,
25+
onRemoveRepository,
26+
}: RepositoriesAccordionProps) {
27+
const [removingRepo, setRemovingRepo] = useState<string | null>(null);
28+
29+
const handleRemove = async (repoName: string) => {
30+
if (confirm(`Remove repository ${repoName}?`)) {
31+
setRemovingRepo(repoName);
32+
try {
33+
await onRemoveRepository(repoName);
34+
} finally {
35+
setRemovingRepo(null);
36+
}
37+
}
38+
};
39+
40+
return (
41+
<AccordionItem value="context" className="border rounded-lg px-3 bg-white">
42+
<AccordionTrigger className="text-base font-semibold hover:no-underline py-3">
43+
<div className="flex items-center gap-2">
44+
<Link className="h-4 w-4" />
45+
<span>Context</span>
46+
{repositories.length > 0 && (
47+
<Badge variant="secondary" className="ml-auto mr-2">
48+
{repositories.length}
49+
</Badge>
50+
)}
51+
</div>
52+
</AccordionTrigger>
53+
<AccordionContent className="pt-2 pb-3">
54+
<div className="space-y-3">
55+
<p className="text-sm text-muted-foreground">
56+
Add additional context to improve AI responses.
57+
</p>
58+
59+
{/* Repository List */}
60+
{repositories.length === 0 ? (
61+
<div className="text-center py-6">
62+
<div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-gray-100 mb-2">
63+
<Link className="h-5 w-5 text-gray-400" />
64+
</div>
65+
<p className="text-sm text-muted-foreground mb-3">No context added yet</p>
66+
<Button size="sm" variant="outline" onClick={onAddRepository}>
67+
<Link className="mr-2 h-3 w-3" />
68+
Add Context
69+
</Button>
70+
</div>
71+
) : (
72+
<div className="space-y-2">
73+
{repositories.map((repo, idx) => {
74+
const repoName = repo.input.url.split('/').pop()?.replace('.git', '') || `repo-${idx}`;
75+
const isRemoving = removingRepo === repoName;
76+
77+
return (
78+
<div key={idx} className="flex items-center gap-2 p-2 border rounded bg-muted/30 hover:bg-muted/50 transition-colors">
79+
<GitBranch className="h-4 w-4 text-muted-foreground flex-shrink-0" />
80+
<div className="flex-1 min-w-0">
81+
<div className="text-sm font-medium truncate">{repoName}</div>
82+
<div className="text-xs text-muted-foreground truncate">{repo.input.url}</div>
83+
</div>
84+
<Button
85+
variant="ghost"
86+
size="sm"
87+
className="h-7 w-7 p-0 flex-shrink-0"
88+
onClick={() => handleRemove(repoName)}
89+
disabled={isRemoving}
90+
>
91+
{isRemoving ? (
92+
<Loader2 className="h-3 w-3 animate-spin" />
93+
) : (
94+
<X className="h-3 w-3" />
95+
)}
96+
</Button>
97+
</div>
98+
);
99+
})}
100+
<Button onClick={onAddRepository} variant="outline" className="w-full" size="sm">
101+
<Link className="mr-2 h-3 w-3" />
102+
Add Context
103+
</Button>
104+
</div>
105+
)}
106+
</div>
107+
</AccordionContent>
108+
</AccordionItem>
109+
);
110+
}
111+

0 commit comments

Comments
 (0)