ChunkInfoDrawer.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import React, { useEffect, useState } from 'react';
  2. import { X } from 'lucide-react';
  3. import { knowledgeBaseService } from '../services/knowledgeBaseService';
  4. import { useLanguage } from '../contexts/LanguageContext';
  5. interface ChunkInfo {
  6. fileId: string;
  7. fileName: string;
  8. totalChunks: number;
  9. chunkSize: number;
  10. chunkOverlap: number;
  11. chunks: Array<{
  12. index: number;
  13. content: string;
  14. contentLength: number;
  15. startPosition: number;
  16. endPosition: number;
  17. }>;
  18. }
  19. interface ChunkInfoDrawerProps {
  20. isOpen: boolean;
  21. onClose: () => void;
  22. fileId: string;
  23. fileName: string;
  24. authToken: string;
  25. }
  26. export const ChunkInfoDrawer: React.FC<ChunkInfoDrawerProps> = ({
  27. isOpen,
  28. onClose,
  29. fileId,
  30. fileName,
  31. authToken,
  32. }) => {
  33. const { t } = useLanguage();
  34. const [chunkInfo, setChunkInfo] = useState<ChunkInfo | null>(null);
  35. const [loading, setLoading] = useState(false);
  36. const [error, setError] = useState<string | null>(null);
  37. useEffect(() => {
  38. if (isOpen && fileId) {
  39. loadChunks();
  40. }
  41. }, [isOpen, fileId]);
  42. const loadChunks = async () => {
  43. setLoading(true);
  44. setError(null);
  45. try {
  46. const data = await knowledgeBaseService.getFileChunks(fileId, authToken);
  47. setChunkInfo(data);
  48. } catch (err) {
  49. console.error('Failed to load chunks:', err);
  50. setError(t('errorLoadData'));
  51. } finally {
  52. setLoading(false);
  53. }
  54. };
  55. if (!isOpen) return null;
  56. return (
  57. <>
  58. {/* Backdrop */}
  59. <div
  60. className="fixed inset-0 bg-black/50 z-[9998]"
  61. onClick={onClose}
  62. />
  63. {/* Drawer */}
  64. <div className="fixed right-0 top-0 h-full w-full md:w-2/3 lg:w-1/2 bg-white shadow-2xl z-[9999] flex flex-col">
  65. {/* Header */}
  66. <div className="flex items-center justify-between p-6 border-b border-slate-200 shrink-0">
  67. <div>
  68. <h2 className="text-xl font-semibold text-slate-800">
  69. {t('chunkInfo')}
  70. </h2>
  71. <p className="text-sm text-slate-500 mt-1">{fileName}</p>
  72. </div>
  73. <button
  74. onClick={onClose}
  75. className="p-2 hover:bg-slate-100 rounded-lg transition-colors"
  76. >
  77. <X className="w-5 h-5" />
  78. </button>
  79. </div>
  80. {/* Content */}
  81. <div className="flex-1 overflow-y-auto p-6">
  82. {loading ? (
  83. <div className="flex items-center justify-center h-full">
  84. <div className="text-slate-500">{t('loading')}</div>
  85. </div>
  86. ) : error ? (
  87. <div className="flex items-center justify-center h-full">
  88. <div className="text-red-500">{error}</div>
  89. </div>
  90. ) : chunkInfo ? (
  91. <div>
  92. {/* Summary */}
  93. <div className="bg-slate-50 rounded-lg p-4 mb-6 space-y-2">
  94. <div className="flex justify-between">
  95. <span className="text-slate-600">{t('totalChunks')}:</span>
  96. <span className="font-semibold">{chunkInfo.totalChunks}</span>
  97. </div>
  98. <div className="flex justify-between">
  99. <span className="text-slate-600">{t('chunkSize')}:</span>
  100. <span className="font-semibold">{chunkInfo.chunkSize} tokens</span>
  101. </div>
  102. <div className="flex justify-between">
  103. <span className="text-slate-600">{t('chunkOverlap')}:</span>
  104. <span className="font-semibold">{chunkInfo.chunkOverlap} tokens</span>
  105. </div>
  106. </div>
  107. {/* Chunks List */}
  108. <div className="space-y-4">
  109. {chunkInfo.chunks.map((chunk) => (
  110. <div
  111. key={chunk.index}
  112. className="border border-slate-200 rounded-lg p-4 hover:border-blue-300 transition-colors"
  113. >
  114. <div className="flex justify-between items-center mb-3">
  115. <span className="font-semibold text-slate-800">
  116. {t('chunkIndex')} #{chunk.index}
  117. </span>
  118. <span className="text-sm text-slate-500">
  119. {chunk.contentLength} {t('contentLength')}
  120. </span>
  121. </div>
  122. <div className="bg-white border border-slate-200 rounded-lg p-4 max-h-96 overflow-y-auto">
  123. <div className="text-slate-700 text-sm leading-7 whitespace-pre-wrap break-words" style={{ fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif' }}>
  124. {chunk.content}
  125. </div>
  126. </div>
  127. <div className="text-xs text-slate-400 mt-2">
  128. {t('position')}: {chunk.startPosition} - {chunk.endPosition}
  129. </div>
  130. </div>
  131. ))}
  132. </div>
  133. </div>
  134. ) : null}
  135. </div>
  136. </div>
  137. </>
  138. );
  139. };