Skip to content

Commit 1120e32

Browse files
committed
feat: show pageindex structure in documents view
1 parent 254e950 commit 1120e32

File tree

3 files changed

+84
-57
lines changed

3 files changed

+84
-57
lines changed

netlify.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
X-Content-Type-Options = "nosniff"
2727
Permissions-Policy = "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(self), payment=(), usb=()"
2828
Referrer-Policy = "strict-origin-when-cross-origin"
29-
Content-Security-Policy = "default-src 'self'; script-src 'self' 'unsafe-inline' blob: data: https://app.termly.io https://www.googletagmanager.com https://checkout.razorpay.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://api.fontshare.com; font-src 'self' https://fonts.gstatic.com data: https://cdn.fontshare.com; img-src 'self' data: blob: https:; media-src 'self' data: blob: https:; connect-src 'self' https://xlzwfkgurrrspcdyqele.supabase.co wss://xlzwfkgurrrspcdyqele.supabase.co https://clerk-tts-production.up.railway.app wss://clerk-tts-production.up.railway.app https://ai.clerktree.com https://api.clerktree.com https://tts.clerktree.com https://app.termly.io https://backend-sq0u.onrender.com https://www.google-analytics.com https://*.sentry.io https://*.razorpay.com https://api.deapi.ai https://api.elevenlabs.io wss://*.elevenlabs.io https://api.pageindex.ai; worker-src 'self' blob: data:; frame-src 'self' https://api.razorpay.com https://checkout.razorpay.com; object-src 'none'; base-uri 'self'; form-action 'self';"
29+
Content-Security-Policy = "default-src 'self'; script-src 'self' 'unsafe-inline' blob: data: https://app.termly.io https://www.googletagmanager.com https://checkout.razorpay.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://api.fontshare.com; font-src 'self' https://fonts.gstatic.com data: https://cdn.fontshare.com; img-src 'self' data: blob: https:; media-src 'self' data: blob: https:; connect-src 'self' https://xlzwfkgurrrspcdyqele.supabase.co wss://xlzwfkgurrrspcdyqele.supabase.co https://clerk-tts-production.up.railway.app wss://clerk-tts-production.up.railway.app https://ai.clerktree.com https://api.clerktree.com https://tts.clerktree.com https://app.termly.io https://backend-sq0u.onrender.com https://www.google-analytics.com https://*.sentry.io https://*.razorpay.com https://api.deapi.ai https://api.elevenlabs.io wss://*.elevenlabs.io https://api.pageindex.ai https://api.groq.com; worker-src 'self' blob: data:; frame-src 'self' https://api.razorpay.com https://checkout.razorpay.com; object-src 'none'; base-uri 'self'; form-action 'self';"
3030

3131
[[headers]]
3232
for = "/assets/*"

src/components/DashboardViews/DocumentsView.tsx

Lines changed: 68 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
import { useAuth } from '../../contexts/AuthContext';
2525
import { useLanguage } from '../../contexts/LanguageContext';
2626
import { useDemoCall } from '../../contexts/DemoCallContext';
27-
import { ragService, UploadedDocument, DocumentChunk, ProcessingProgress } from '../../services/ragService';
27+
import { ragService, UploadedDocument, ProcessingProgress } from '../../services/ragService';
2828
import { pageIndexService, PageIndexNode } from '../../services/pageIndexService';
2929
import { motion, AnimatePresence } from 'framer-motion';
3030

@@ -58,6 +58,19 @@ function getFileIcon(fileType: string) {
5858
}
5959
}
6060

61+
function getMaxPageIndex(nodes: PageIndexNode[]): number {
62+
let maxPage = 0;
63+
for (const node of nodes) {
64+
if (typeof node.page_index === 'number') {
65+
maxPage = Math.max(maxPage, node.page_index);
66+
}
67+
if (node.nodes && node.nodes.length > 0) {
68+
maxPage = Math.max(maxPage, getMaxPageIndex(node.nodes));
69+
}
70+
}
71+
return maxPage;
72+
}
73+
6174
const ACCEPTED_TYPES = '.pdf,.txt,.md,.csv,.docx';
6275
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
6376

@@ -126,8 +139,6 @@ export default function DocumentsView({ isDark = true }: { isDark?: boolean }) {
126139
const [documents, setDocuments] = useState<UploadedDocument[]>([]);
127140
const [isLoading, setIsLoading] = useState(true);
128141
const [expandedDocId, setExpandedDocId] = useState<string | null>(null);
129-
const [chunks, setChunks] = useState<DocumentChunk[]>([]);
130-
const [loadingChunks, setLoadingChunks] = useState(false);
131142
const [processingProgress, setProcessingProgress] = useState<ProcessingProgress | null>(null);
132143
const [textTitle, setTextTitle] = useState('');
133144
const [textContent, setTextContent] = useState('');
@@ -137,7 +148,8 @@ export default function DocumentsView({ isDark = true }: { isDark?: boolean }) {
137148
const [uploadMode, setUploadMode] = useState<'text' | 'pdf'>('pdf');
138149
const [selectedFile, setSelectedFile] = useState<File | null>(null);
139150
const [pageIndexTree, setPageIndexTree] = useState<PageIndexNode[]>([]);
140-
const [activePageIndexId, setActivePageIndexId] = useState<string | null>(null);
151+
const [pageIndexByDocId, setPageIndexByDocId] = useState<Record<string, PageIndexNode[]>>({});
152+
const [pageIndexDocIdByDocId, setPageIndexDocIdByDocId] = useState<Record<string, string>>({});
141153
const [chatMessages, setChatMessages] = useState<{ role: string; content: string }[]>([]);
142154
const [chatInput, setChatInput] = useState('');
143155
const [isChatting, setIsChatting] = useState(false);
@@ -221,7 +233,6 @@ export default function DocumentsView({ isDark = true }: { isDark?: boolean }) {
221233

222234
try {
223235
const docId = await pageIndexService.submitDocument(selectedFile);
224-
setActivePageIndexId(docId);
225236

226237
setProcessingProgress({
227238
stage: 'embedding',
@@ -247,12 +258,16 @@ export default function DocumentsView({ isDark = true }: { isDark?: boolean }) {
247258
// In a real app, we'd store the pageindex_id in the DB.
248259

249260
// Let's create a backup record in Supabase so it shows in the list
250-
await ragService.processDocument(
261+
const ragDoc = await ragService.processDocument(
251262
selectedFile,
252263
kbId,
253264
user.id,
254265
() => { } // Silent progress for standard RAG
255266
);
267+
if (ragDoc) {
268+
setPageIndexByDocId(prev => ({ ...prev, [ragDoc.id]: result }));
269+
setPageIndexDocIdByDocId(prev => ({ ...prev, [ragDoc.id]: docId }));
270+
}
256271

257272
setSelectedFile(null);
258273
setTextTitle('');
@@ -304,19 +319,13 @@ export default function DocumentsView({ isDark = true }: { isDark?: boolean }) {
304319
}
305320
}, [chatMessages, isChatting]);
306321

307-
// ─── Expand / view chunks ─────────────────────────────────
308-
const toggleExpand = async (docId: string) => {
322+
// ─── Expand / view structure ──────────────────────────────
323+
const toggleExpand = (docId: string) => {
309324
if (expandedDocId === docId) {
310325
setExpandedDocId(null);
311-
setChunks([]);
312326
return;
313327
}
314-
315328
setExpandedDocId(docId);
316-
setLoadingChunks(true);
317-
const docChunks = await ragService.getDocumentChunks(docId);
318-
setChunks(docChunks);
319-
setLoadingChunks(false);
320329
};
321330

322331
// ─── Delete ───────────────────────────────────────────────
@@ -327,8 +336,17 @@ export default function DocumentsView({ isDark = true }: { isDark?: boolean }) {
327336
setDocuments(prev => prev.filter(d => d.id !== docId));
328337
if (expandedDocId === docId) {
329338
setExpandedDocId(null);
330-
setChunks([]);
331339
}
340+
setPageIndexByDocId(prev => {
341+
const next = { ...prev };
342+
delete next[docId];
343+
return next;
344+
});
345+
setPageIndexDocIdByDocId(prev => {
346+
const next = { ...prev };
347+
delete next[docId];
348+
return next;
349+
});
332350
}
333351
setDeleteConfirmId(null);
334352
};
@@ -664,8 +682,14 @@ export default function DocumentsView({ isDark = true }: { isDark?: boolean }) {
664682
</div>
665683
) : (
666684
<div className="space-y-3">
667-
{documents.map((doc) => (
668-
<div key={doc.id}>
685+
{documents.map((doc) => {
686+
const treeForDoc = pageIndexByDocId[doc.id];
687+
const nodeCount = treeForDoc ? pageIndexService.countNodes(treeForDoc) : 0;
688+
const maxPage = treeForDoc ? getMaxPageIndex(treeForDoc) : 0;
689+
const pageIndexId = pageIndexDocIdByDocId[doc.id];
690+
691+
return (
692+
<div key={doc.id}>
669693
{/* Document Row */}
670694
<div className={cn(
671695
"rounded-xl border p-4 transition-all duration-200",
@@ -728,7 +752,7 @@ export default function DocumentsView({ isDark = true }: { isDark?: boolean }) {
728752
isDark ? "bg-white/5 text-white/60" : "bg-gray-100 text-gray-600"
729753
)}>
730754
<Layers className="w-3 h-3" />
731-
{doc.chunk_count} Chunks
755+
{treeForDoc ? `${nodeCount} Nodes` : `${doc.chunk_count} Chunks`}
732756
</span>
733757
</div>
734758
)}
@@ -794,7 +818,7 @@ export default function DocumentsView({ isDark = true }: { isDark?: boolean }) {
794818
)}
795819
</div>
796820

797-
{/* Expanded Chunks */}
821+
{/* Expanded Structure */}
798822
<AnimatePresence>
799823
{expandedDocId === doc.id && (
800824
<motion.div
@@ -809,67 +833,57 @@ export default function DocumentsView({ isDark = true }: { isDark?: boolean }) {
809833
isDark ? "bg-white/[0.03] border-white/10 backdrop-blur-md" : "bg-gray-50 border-gray-100"
810834
)}>
811835
{/* Stats bar */}
812-
<div className={cn("flex items-center gap-4 text-xs pb-3 border-b", isDark ? "border-white/5" : "border-gray-200")}>
836+
<div className={cn("flex flex-wrap items-center gap-4 text-xs pb-3 border-b", isDark ? "border-white/5" : "border-gray-200")}>
837+
<span className={textSecondary}>
838+
<strong className={textPrimary}>{treeForDoc ? nodeCount : 'N/A'}</strong> nodes
839+
</span>
813840
<span className={textSecondary}>
814-
<strong className={textPrimary}>{doc.chunk_count}</strong> chunks
841+
<strong className={textPrimary}>{treeForDoc ? treeForDoc.length : 'N/A'}</strong> sections
815842
</span>
816843
<span className={textSecondary}>
817-
<strong className={textPrimary}>{doc.total_tokens?.toLocaleString()}</strong> words
844+
<strong className={textPrimary}>{treeForDoc ? (maxPage || 'N/A') : 'N/A'}</strong> pages
818845
</span>
819-
<span className={cn("uppercase text-[10px] font-mono px-1.5 py-0.5 rounded", isDark ? "bg-white/5" : "bg-gray-200")}>
846+
{pageIndexId && (
847+
<span className={cn(
848+
"text-[10px] font-mono px-1.5 py-0.5 rounded border",
849+
isDark ? "bg-white/5 border-white/10 text-white/60" : "bg-gray-100 border-gray-200 text-gray-600"
850+
)}>
851+
ID {pageIndexId}
852+
</span>
853+
)}
854+
<span className={cn("uppercase text-[10px] font-mono px-1.5 py-0.5 rounded", isDark ? "bg-white/5" : "bg-gray-200")}>
820855
{doc.file_type}
821856
</span>
822857
</div>
823858

824-
{loadingChunks ? (
825-
<div className="flex items-center justify-center py-8">
826-
<Loader2 className={cn("w-5 h-5 animate-spin", textMuted)} />
827-
</div>
828-
) : chunks.length === 0 ? (
829-
<p className={cn("text-sm text-center py-6", textMuted)}>
830-
No chunks found
831-
</p>
832-
) : (
859+
{treeForDoc ? (
833860
<div className="space-y-2 max-h-[400px] overflow-y-auto scrollbar-hide">
834-
{chunks.map((chunk, idx) => (
861+
{treeForDoc.map((node, idx) => (
835862
<div
836-
key={chunk.id}
863+
key={`${doc.id}-${idx}`}
837864
className={cn(
838865
"rounded-lg border p-3 transition-colors",
839866
isDark
840867
? "bg-white/[0.03] border-white/10 hover:bg-white/[0.05]"
841868
: "bg-white border-gray-200 hover:bg-gray-50"
842869
)}
843870
>
844-
<div className="flex items-start gap-3">
845-
<span className={cn(
846-
"flex-shrink-0 w-7 h-7 rounded-lg flex items-center justify-center text-xs font-mono font-bold",
847-
isDark ? "bg-[#FF8A5B]/10 text-[#FFB286]" : "bg-orange-50 text-orange-600"
848-
)}>
849-
{idx + 1}
850-
</span>
851-
<div className="flex-1 min-w-0">
852-
<p className={cn(
853-
"text-xs leading-relaxed line-clamp-4",
854-
isDark ? "text-white/70" : "text-gray-600"
855-
)}>
856-
{chunk.content}
857-
</p>
858-
<p className={cn("text-[10px] mt-1.5", textMuted)}>
859-
{chunk.content.split(/\s+/).length} words
860-
</p>
861-
</div>
862-
</div>
871+
<TreeNodeNode node={node} />
863872
</div>
864873
))}
865874
</div>
875+
) : (
876+
<p className={cn("text-sm text-center py-6", textMuted)}>
877+
This document hasn't been indexed with PageIndex yet. Re-upload using Vectorless Indexing to generate structure.
878+
</p>
866879
)}
867880
</div>
868881
</motion.div>
869882
)}
870883
</AnimatePresence>
871884
</div>
872-
))}
885+
);
886+
})}
873887
</div>
874888
)}
875889
</div>

0 commit comments

Comments
 (0)