Skip to content

Commit 95c4979

Browse files
committed
fix: Remove title case transformation from knowledge base names and improve UI layout
- Remove .title() transformation from knowledge base names in API endpoints - Add textTransform: none to knowledge base name column in grid - Improve source chunks page layout with proper overflow handling - Enhance chunk card UI with badges, better spacing, and copy feedback - Add pagination controls with first/last page buttons and page number input - Preserve original chunk indices when filtering - Fix whit
1 parent d1dd8cf commit 95c4979

4 files changed

Lines changed: 167 additions & 85 deletions

File tree

src/backend/base/langflow/api/v1/knowledge_bases.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ async def list_knowledge_bases(current_user: CurrentActiveUser) -> list[Knowledg
331331

332332
kb_info = KnowledgeBaseInfo(
333333
id=kb_dir.name,
334-
name=kb_dir.name.replace("_", " ").replace("-", " ").title(),
334+
name=kb_dir.name.replace("_", " ").replace("-", " "),
335335
embedding_provider=metadata["embedding_provider"],
336336
embedding_model=metadata["embedding_model"],
337337
size=size,
@@ -376,7 +376,7 @@ async def get_knowledge_base(kb_name: str, current_user: CurrentActiveUser) -> K
376376

377377
return KnowledgeBaseInfo(
378378
id=kb_name,
379-
name=kb_name.replace("_", " ").replace("-", " ").title(),
379+
name=kb_name.replace("_", " ").replace("-", " "),
380380
embedding_provider=metadata["embedding_provider"],
381381
embedding_model=metadata["embedding_model"],
382382
size=size,

src/frontend/src/pages/MainPage/pages/filesPage/config/knowledgeBaseColumns.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export const createKnowledgeBaseColumns = (
3535
checkboxSelection: true,
3636
editable: false,
3737
cellClass: baseCellClass,
38+
cellStyle: { textTransform: "none" },
3839
},
3940
{
4041
headerName: "Size",

src/frontend/src/pages/MainPage/pages/knowledgePage/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export const KnowledgePage = () => {
9898
<div className="flex flex-1 flex-col justify-start px-5 pt-10">
9999
<div className="flex h-full flex-col justify-start">
100100
<div
101-
className="flex items-center pb-8 text-xl font-semibold"
101+
className="flex items-center pb-4 text-xl font-semibold"
102102
data-testid="mainpage_title"
103103
>
104104
<div className="h-7 w-10 transition-all group-data-[open=true]/sidebar-wrapper:md:w-0 lg:hidden">

src/frontend/src/pages/MainPage/pages/knowledgePage/sourceChunksPage/index.tsx

Lines changed: 163 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useEffect, useState } from "react";
22
import { useParams } from "react-router-dom";
33
import ForwardedIconComponent from "@/components/common/genericIconComponent";
4+
import { Badge } from "@/components/ui/badge";
45
import { Button } from "@/components/ui/button";
56
import { Input } from "@/components/ui/input";
67
import Loading from "@/components/ui/loading";
@@ -21,51 +22,80 @@ interface ChunkCardProps {
2122

2223
const ChunkCard = ({ chunk, index, onCopy }: ChunkCardProps) => {
2324
const [isExpanded, setIsExpanded] = useState(false);
25+
const [isCopied, setIsCopied] = useState(false);
2426
const shouldTruncate = chunk.content.length > TRUNCATE_LENGTH;
2527
const displayContent =
2628
shouldTruncate && !isExpanded
2729
? chunk.content.slice(0, TRUNCATE_LENGTH) + "..."
2830
: chunk.content;
2931

32+
const handleCopy = (e: React.MouseEvent) => {
33+
e.stopPropagation();
34+
onCopy(chunk.content);
35+
setIsCopied(true);
36+
setTimeout(() => setIsCopied(false), 2000);
37+
};
38+
3039
return (
3140
<div
3241
className={cn(
33-
"cursor-pointer rounded-lg border border-border bg-background p-4 transition-all duration-200",
34-
isExpanded && "ring-1 ring-ring",
42+
"cursor-pointer rounded-lg border border-muted bg-muted p-3 transition-all duration-200",
3543
)}
3644
onClick={() => shouldTruncate && setIsExpanded(!isExpanded)}
3745
>
38-
<div className="mb-3 flex items-center justify-between">
46+
<div className="mb-2 flex items-center justify-between">
3947
<div className="flex items-center gap-3">
40-
<span className="font-medium">Chunk {index}</span>
41-
<span className="text-sm text-muted-foreground">
48+
<span className="text-sm font-medium">Chunk {index}</span>
49+
<Badge
50+
variant="secondary"
51+
size="sq"
52+
className="text-xs text-muted-foreground"
53+
>
4254
{chunk.char_count} chars
43-
</span>
55+
</Badge>
4456
<Button
4557
variant="ghost"
4658
size="icon"
47-
className="h-6 w-6"
48-
onClick={(e) => {
49-
e.stopPropagation();
50-
onCopy(chunk.content);
51-
}}
59+
className={cn(
60+
"group h-6 w-6 transition-colors",
61+
isCopied && "text-accent-emerald-foreground",
62+
)}
63+
onClick={handleCopy}
5264
>
5365
<ForwardedIconComponent
54-
name="Copy"
55-
className="h-3.5 w-3.5 text-muted-foreground"
66+
name={isCopied ? "Check" : "Copy"}
67+
className={cn(
68+
"h-3.5 w-3.5 transition-colors",
69+
isCopied
70+
? "text-accent-emerald-foreground"
71+
: "text-muted-foreground group-hover:text-foreground",
72+
)}
5673
/>
5774
</Button>
5875
</div>
59-
{shouldTruncate && (
60-
<ForwardedIconComponent
61-
name={isExpanded ? "ChevronUp" : "ChevronDown"}
62-
className="h-4 w-4 text-muted-foreground transition-transform duration-200"
63-
/>
64-
)}
76+
<div className="flex items-center gap-3">
77+
{/* TODO: Add score when semantic search is implemented
78+
<Badge
79+
variant="secondary"
80+
size="sq"
81+
className="text-xs text-muted-foreground"
82+
>
83+
{chunk?.score ?? "N/A"} score
84+
</Badge>
85+
*/}
86+
<div className="w-4">
87+
{shouldTruncate && (
88+
<ForwardedIconComponent
89+
name={isExpanded ? "ChevronUp" : "ChevronDown"}
90+
className="h-4 w-4 text-muted-foreground transition-transform duration-200"
91+
/>
92+
)}
93+
</div>
94+
</div>
6595
</div>
6696
<p
6797
className={cn(
68-
"text-sm leading-relaxed text-muted-foreground transition-all duration-200",
98+
"text-sm leading-relaxed text-muted-foreground transition-all duration-200 whitespace-pre-wrap break-words",
6999
!isExpanded && shouldTruncate && "line-clamp-4",
70100
)}
71101
>
@@ -95,9 +125,14 @@ export const SourceChunksPage = () => {
95125
navigator.clipboard.writeText(content);
96126
};
97127

98-
const filteredChunks = (chunks || []).filter((chunk) =>
99-
chunk.content.toLowerCase().includes(searchText.toLowerCase()),
100-
);
128+
const filteredChunks = (chunks || [])
129+
.map((chunk, originalIndex) => ({
130+
...chunk,
131+
originalIndex: originalIndex + 1,
132+
}))
133+
.filter((chunk) =>
134+
chunk.content.toLowerCase().includes(searchText.toLowerCase()),
135+
);
101136

102137
const totalPages = Math.ceil(filteredChunks.length / CHUNKS_PER_PAGE);
103138
const startIndex = (currentPage - 1) * CHUNKS_PER_PAGE;
@@ -112,12 +147,12 @@ export const SourceChunksPage = () => {
112147

113148
return (
114149
<div className="flex h-full w-full" data-testid="source-chunks-wrapper">
115-
<div className="flex h-full w-full flex-col overflow-y-auto">
116-
<div className="flex h-full w-full flex-col xl:container">
117-
<div className="flex flex-1 flex-col justify-start px-5 pt-10">
118-
<div className="flex h-full flex-col justify-start">
150+
<div className="flex h-full w-full flex-col overflow-hidden">
151+
<div className="flex h-full w-full flex-col overflow-hidden xl:container">
152+
<div className="flex h-full flex-col px-5 pt-10">
153+
<div className="flex h-full flex-col overflow-hidden">
119154
<div
120-
className="flex items-center pb-8 text-xl font-semibold"
155+
className="flex shrink-0 items-center pb-4 text-base h-[44px] font-semibold"
121156
data-testid="mainpage_title"
122157
>
123158
<div className="h-7 w-10 transition-all group-data-[open=true]/sidebar-wrapper:md:w-0 lg:hidden">
@@ -141,11 +176,11 @@ export const SourceChunksPage = () => {
141176
className="h-4 w-4"
142177
/>
143178
</Button>
144-
{sourceId}
179+
<span style={{ textTransform: "none" }}>{sourceId}</span>
145180
</div>
146181

147-
<div className="flex h-full flex-col">
148-
<div className="pb-6">
182+
<div className="flex flex-1 flex-col overflow-hidden">
183+
<div className="shrink-0 pb-4 xl:w-[600px]">
149184
<Input
150185
icon="Search"
151186
type="text"
@@ -169,64 +204,110 @@ export const SourceChunksPage = () => {
169204
No chunks found
170205
</div>
171206
) : (
172-
<>
173-
<div className="flex flex-col gap-3">
174-
{paginatedChunks.map((chunk, index) => (
175-
<ChunkCard
176-
key={chunk.id}
177-
chunk={chunk}
178-
index={startIndex + index + 1}
179-
onCopy={handleCopyChunk}
180-
/>
181-
))}
207+
<div className="flex flex-1 flex-col overflow-hidden">
208+
<div className="flex-1 overflow-y-auto">
209+
<div className="flex flex-col gap-3">
210+
{paginatedChunks.map((chunk) => (
211+
<ChunkCard
212+
key={chunk.id}
213+
chunk={chunk}
214+
index={chunk.originalIndex}
215+
onCopy={handleCopyChunk}
216+
/>
217+
))}
218+
</div>
182219
</div>
183220

184221
{totalPages > 1 && (
185-
<div className="flex items-center justify-between border-t border-border py-4">
186-
<span className="text-sm text-muted-foreground">
187-
Showing {startIndex + 1}-
188-
{Math.min(
189-
startIndex + CHUNKS_PER_PAGE,
190-
filteredChunks.length,
191-
)}{" "}
192-
of {filteredChunks.length} chunks
193-
</span>
194-
<div className="flex items-center gap-2">
195-
<Button
196-
variant="outline"
197-
size="sm"
198-
onClick={() =>
199-
setCurrentPage((p) => Math.max(1, p - 1))
200-
}
201-
disabled={currentPage === 1}
202-
>
203-
<ForwardedIconComponent
204-
name="ChevronLeft"
205-
className="h-4 w-4"
206-
/>
207-
Previous
208-
</Button>
209-
<span className="px-2 text-sm">
210-
Page {currentPage} of {totalPages}
222+
<div className="shrink-0 pb-4 pt-3">
223+
<div className="flex items-center justify-between">
224+
<span className="text-sm text-muted-foreground">
225+
Showing {startIndex + 1}-
226+
{Math.min(
227+
startIndex + CHUNKS_PER_PAGE,
228+
filteredChunks.length,
229+
)}{" "}
230+
of {filteredChunks.length} chunks
211231
</span>
212-
<Button
213-
variant="outline"
214-
size="sm"
215-
onClick={() =>
216-
setCurrentPage((p) => Math.min(totalPages, p + 1))
217-
}
218-
disabled={currentPage === totalPages}
219-
>
220-
Next
221-
<ForwardedIconComponent
222-
name="ChevronRight"
223-
className="h-4 w-4"
224-
/>
225-
</Button>
232+
<div className="flex items-center gap-2">
233+
<Button
234+
variant="outline"
235+
size="icon"
236+
className="h-8 w-8"
237+
onClick={() => setCurrentPage(1)}
238+
disabled={currentPage === 1}
239+
>
240+
<ForwardedIconComponent
241+
name="ChevronsLeft"
242+
className="h-4 w-4"
243+
/>
244+
</Button>
245+
<Button
246+
variant="outline"
247+
size="icon"
248+
className="h-8 w-8"
249+
onClick={() =>
250+
setCurrentPage((p) => Math.max(1, p - 1))
251+
}
252+
disabled={currentPage === 1}
253+
>
254+
<ForwardedIconComponent
255+
name="ChevronLeft"
256+
className="h-4 w-4"
257+
/>
258+
</Button>
259+
<div className="flex items-center gap-1.5 px-2 text-sm">
260+
<span>Page</span>
261+
<input
262+
type="number"
263+
min={1}
264+
max={totalPages}
265+
value={currentPage}
266+
onChange={(e) => {
267+
const value = parseInt(e.target.value, 10);
268+
if (!isNaN(value)) {
269+
setCurrentPage(
270+
Math.max(1, Math.min(totalPages, value)),
271+
);
272+
}
273+
}}
274+
className="h-7 w-16 rounded border border-input bg-background px-2 text-center text-sm focus:outline-none focus:ring-1 focus:ring-ring"
275+
/>
276+
<span>of {totalPages}</span>
277+
</div>
278+
<Button
279+
variant="outline"
280+
size="icon"
281+
className="h-8 w-8"
282+
onClick={() =>
283+
setCurrentPage((p) =>
284+
Math.min(totalPages, p + 1),
285+
)
286+
}
287+
disabled={currentPage === totalPages}
288+
>
289+
<ForwardedIconComponent
290+
name="ChevronRight"
291+
className="h-4 w-4"
292+
/>
293+
</Button>
294+
<Button
295+
variant="outline"
296+
size="icon"
297+
className="h-8 w-8"
298+
onClick={() => setCurrentPage(totalPages)}
299+
disabled={currentPage === totalPages}
300+
>
301+
<ForwardedIconComponent
302+
name="ChevronsRight"
303+
className="h-4 w-4"
304+
/>
305+
</Button>
306+
</div>
226307
</div>
227308
</div>
228309
)}
229-
</>
310+
</div>
230311
)}
231312
</div>
232313
</div>

0 commit comments

Comments
 (0)