Skip to content

Commit abc480f

Browse files
committed
feat: EMCN breadcrumb; improvement(KB): UI
1 parent c9f6154 commit abc480f

File tree

29 files changed

+1530
-2311
lines changed

29 files changed

+1530
-2311
lines changed

CLAUDE.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Expert Programming Standards
2+
3+
**You are tasked with implementing solutions that follow best practices. You MUST be accurate, elegant, and efficient as an expert programmer.**
4+
5+
---
6+
7+
# Role
8+
9+
You are a professional software engineer. All code you write MUST follow best practices, ensuring accuracy, quality, readability, and cleanliness. You MUST make FOCUSED EDITS that are EFFICIENT and ELEGANT.
10+
11+
## Logs
12+
13+
ENSURE that you use the logger.info and logger.warn and logger.error instead of the console.log whenever you want to display logs.
14+
15+
## Comments
16+
17+
You must use TSDOC for comments. Do not use ==== for comments to separate sections. Do not leave any comments that are not TSDOC.
18+
19+
## Global Styles
20+
21+
You should not update the global styles unless it is absolutely necessary. Keep all styling local to components and files.
22+
23+
## Bun
24+
25+
Use bun and bunx not npm and npx.
26+
27+
## Code Quality
28+
29+
- Write clean, maintainable code that follows the project's existing patterns
30+
- Prefer composition over inheritance
31+
- Keep functions small and focused on a single responsibility
32+
- Use meaningful variable and function names
33+
- Handle errors gracefully and provide useful error messages
34+
- Write type-safe code with proper TypeScript types
35+
36+
## Testing
37+
38+
- Write tests for new functionality when appropriate
39+
- Ensure existing tests pass before completing work
40+
- Follow the project's testing conventions
41+
42+
## Performance
43+
44+
- Consider performance implications of your code
45+
- Avoid unnecessary re-renders in React components
46+
- Use appropriate data structures and algorithms
47+
- Profile and optimize when necessary

apps/sim/app/templates/[id]/template.tsx

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import { useEffect, useState } from 'react'
44
import { formatDistanceToNow } from 'date-fns'
55
import {
6-
ArrowLeft,
76
ChartNoAxesColumn,
87
ChevronDown,
98
Globe,
@@ -16,6 +15,7 @@ import {
1615
import { useParams, useRouter, useSearchParams } from 'next/navigation'
1716
import ReactMarkdown from 'react-markdown'
1817
import {
18+
Breadcrumb,
1919
Button,
2020
Copy,
2121
Popover,
@@ -267,13 +267,13 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
267267
}
268268
}
269269

270-
const handleBack = () => {
271-
if (isWorkspaceContext) {
272-
router.back()
273-
} else {
274-
router.push('/templates')
275-
}
276-
}
270+
const breadcrumbItems = [
271+
{
272+
label: 'Templates',
273+
href: isWorkspaceContext ? `/workspace/${workspaceId}/templates` : '/templates',
274+
},
275+
{ label: template?.name || 'Template' },
276+
]
277277
/**
278278
* Intercepts wheel events over the workflow preview so that the page handles scrolling
279279
* instead of the underlying canvas. We stop propagation in the capture phase to prevent
@@ -545,20 +545,11 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
545545
<div className={cn('flex min-h-screen flex-col', isWorkspaceContext && 'pl-64')}>
546546
<div className='flex flex-1 overflow-hidden'>
547547
<div className='flex flex-1 flex-col overflow-auto px-[24px] pt-[24px] pb-[24px]'>
548-
{/* Top bar with back button */}
549-
<div className='flex items-center justify-between'>
550-
{/* Back button */}
551-
<button
552-
onClick={handleBack}
553-
className='flex items-center gap-[6px] font-medium text-[#ADADAD] text-[14px] transition-colors hover:text-white'
554-
>
555-
<ArrowLeft className='h-[14px] w-[14px]' />
556-
<span>More Templates</span>
557-
</button>
558-
</div>
548+
{/* Breadcrumb navigation */}
549+
<Breadcrumb items={breadcrumbItems} />
559550

560551
{/* Template name and action buttons */}
561-
<div className='mt-[24px] flex items-center justify-between'>
552+
<div className='mt-[14px] flex items-center justify-between'>
562553
<h1 className='font-medium text-[18px]'>{template.name}</h1>
563554

564555
{/* Action buttons */}

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/components/document-loading.tsx

Lines changed: 97 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,110 @@
33
import { Search } from 'lucide-react'
44
import { useParams } from 'next/navigation'
55
import { Button } from '@/components/emcn'
6-
import {
7-
ChunkTableSkeleton,
8-
KnowledgeHeader,
9-
} from '@/app/workspace/[workspaceId]/knowledge/components'
6+
import { KnowledgeHeader } from '@/app/workspace/[workspaceId]/knowledge/components'
107

118
interface DocumentLoadingProps {
129
knowledgeBaseId: string
1310
knowledgeBaseName: string
1411
documentName: string
1512
}
1613

14+
function ChunkTableRowSkeleton() {
15+
return (
16+
<tr className='border-b'>
17+
<td className='px-4 py-3'>
18+
<div className='h-3.5 w-3.5 animate-pulse rounded bg-muted' />
19+
</td>
20+
<td className='px-4 py-3'>
21+
<div className='h-4 w-6 animate-pulse rounded bg-muted' />
22+
</td>
23+
<td className='px-4 py-3'>
24+
<div className='space-y-2'>
25+
<div className='h-4 w-full animate-pulse rounded bg-muted' />
26+
<div className='h-4 w-3/4 animate-pulse rounded bg-muted' />
27+
<div className='h-4 w-1/2 animate-pulse rounded bg-muted' />
28+
</div>
29+
</td>
30+
<td className='px-4 py-3'>
31+
<div className='h-3 w-8 animate-pulse rounded bg-muted' />
32+
</td>
33+
<td className='px-4 py-3'>
34+
<div className='h-6 w-16 animate-pulse rounded-md bg-muted' />
35+
</td>
36+
<td className='px-4 py-3'>
37+
<div className='flex items-center gap-1'>
38+
<div className='h-8 w-8 animate-pulse rounded bg-muted' />
39+
<div className='h-8 w-8 animate-pulse rounded bg-muted' />
40+
</div>
41+
</td>
42+
</tr>
43+
)
44+
}
45+
46+
function ChunkTableSkeleton({
47+
isSidebarCollapsed,
48+
rowCount = 5,
49+
}: {
50+
isSidebarCollapsed: boolean
51+
rowCount?: number
52+
}) {
53+
return (
54+
<div className='flex flex-1 flex-col overflow-hidden'>
55+
<div className='sticky top-0 z-10 border-b bg-background'>
56+
<table className='w-full table-fixed'>
57+
<colgroup>
58+
<col className='w-[5%]' />
59+
<col className='w-[8%]' />
60+
<col className={`${isSidebarCollapsed ? 'w-[57%]' : 'w-[55%]'}`} />
61+
<col className='w-[10%]' />
62+
<col className='w-[10%]' />
63+
<col className='w-[12%]' />
64+
</colgroup>
65+
<thead>
66+
<tr>
67+
<th className='px-4 pt-2 pb-3 text-left font-medium'>
68+
<div className='h-3.5 w-3.5 animate-pulse rounded bg-muted' />
69+
</th>
70+
<th className='px-4 pt-2 pb-3 text-left font-medium'>
71+
<span className='text-muted-foreground text-xs leading-none'>Index</span>
72+
</th>
73+
<th className='px-4 pt-2 pb-3 text-left font-medium'>
74+
<span className='text-muted-foreground text-xs leading-none'>Content</span>
75+
</th>
76+
<th className='px-4 pt-2 pb-3 text-left font-medium'>
77+
<span className='text-muted-foreground text-xs leading-none'>Tokens</span>
78+
</th>
79+
<th className='px-4 pt-2 pb-3 text-left font-medium'>
80+
<span className='text-muted-foreground text-xs leading-none'>Status</span>
81+
</th>
82+
<th className='px-4 pt-2 pb-3 text-left font-medium'>
83+
<span className='text-muted-foreground text-xs leading-none'>Actions</span>
84+
</th>
85+
</tr>
86+
</thead>
87+
</table>
88+
</div>
89+
<div className='flex-1 overflow-auto'>
90+
<table className='w-full table-fixed'>
91+
<colgroup>
92+
<col className='w-[5%]' />
93+
<col className='w-[8%]' />
94+
<col className={`${isSidebarCollapsed ? 'w-[57%]' : 'w-[55%]'}`} />
95+
<col className='w-[10%]' />
96+
<col className='w-[10%]' />
97+
<col className='w-[12%]' />
98+
</colgroup>
99+
<tbody>
100+
{Array.from({ length: rowCount }).map((_, i) => (
101+
<ChunkTableRowSkeleton key={i} />
102+
))}
103+
</tbody>
104+
</table>
105+
</div>
106+
</div>
107+
)
108+
}
109+
17110
export function DocumentLoading({
18111
knowledgeBaseId,
19112
knowledgeBaseName,

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
'use client'
22

33
import { Suspense, startTransition, useCallback, useEffect, useState } from 'react'
4-
import { ChevronLeft, ChevronRight, Circle, CircleOff, FileText, Plus } from 'lucide-react'
4+
import {
5+
ChevronLeft,
6+
ChevronRight,
7+
Circle,
8+
CircleOff,
9+
FileText,
10+
Plus,
11+
Search,
12+
X,
13+
} from 'lucide-react'
514
import { useParams, useSearchParams } from 'next/navigation'
615
import { Button, Tooltip } from '@/components/emcn'
716
import { Trash } from '@/components/emcn/icons/trash'
@@ -14,7 +23,7 @@ import {
1423
EditChunkModal,
1524
} from '@/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/components'
1625
import { ActionBar } from '@/app/workspace/[workspaceId]/knowledge/[id]/components'
17-
import { KnowledgeHeader, SearchInput } from '@/app/workspace/[workspaceId]/knowledge/components'
26+
import { KnowledgeHeader } from '@/app/workspace/[workspaceId]/knowledge/components'
1827
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
1928
import { useDocumentChunks } from '@/hooks/use-knowledge'
2029
import { type ChunkData, type DocumentData, useKnowledgeStore } from '@/stores/knowledge/store'
@@ -662,17 +671,38 @@ export function Document({
662671
<div className='px-6 pb-6'>
663672
{/* Search Section */}
664673
<div className='mb-4 flex items-center justify-between pt-1'>
665-
<SearchInput
666-
value={searchQuery}
667-
onChange={setSearchQuery}
668-
placeholder={
669-
documentData?.processingStatus === 'completed'
670-
? 'Search chunks...'
671-
: 'Document processing...'
672-
}
673-
disabled={documentData?.processingStatus !== 'completed'}
674-
isLoading={isLoadingSearch}
675-
/>
674+
<div className='relative max-w-md flex-1'>
675+
<div className='relative flex items-center'>
676+
<Search className='-translate-y-1/2 pointer-events-none absolute top-1/2 left-3 h-[18px] w-[18px] transform text-muted-foreground' />
677+
<input
678+
type='text'
679+
value={searchQuery}
680+
onChange={(e) => setSearchQuery(e.target.value)}
681+
placeholder={
682+
documentData?.processingStatus === 'completed'
683+
? 'Search chunks...'
684+
: 'Document processing...'
685+
}
686+
disabled={documentData?.processingStatus !== 'completed'}
687+
className='h-10 w-full rounded-md border bg-background px-9 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:font-medium file:text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50'
688+
/>
689+
{isLoadingSearch ? (
690+
<div className='-translate-y-1/2 absolute top-1/2 right-3'>
691+
<div className='h-[18px] w-[18px] animate-spin rounded-full border-2 border-gray-300 border-t-[var(--brand-primary-hex)]' />
692+
</div>
693+
) : (
694+
searchQuery &&
695+
documentData?.processingStatus === 'completed' && (
696+
<button
697+
onClick={() => setSearchQuery('')}
698+
className='-translate-y-1/2 absolute top-1/2 right-3 transform text-muted-foreground hover:text-foreground'
699+
>
700+
<X className='h-[18px] w-[18px]' />
701+
</button>
702+
)
703+
)}
704+
</div>
705+
</div>
676706

677707
<Button
678708
onClick={() => setIsCreateChunkModalOpen(true)}

0 commit comments

Comments
 (0)