@@ -67,11 +67,10 @@ export default function DocumentsView({ isDark = true }: { isDark?: boolean }) {
6767 const [ chunks , setChunks ] = useState < DocumentChunk [ ] > ( [ ] ) ;
6868 const [ loadingChunks , setLoadingChunks ] = useState ( false ) ;
6969 const [ processingProgress , setProcessingProgress ] = useState < ProcessingProgress | null > ( null ) ;
70- const [ isDragging , setIsDragging ] = useState ( false ) ;
70+ const [ textTitle , setTextTitle ] = useState ( '' ) ;
71+ const [ textContent , setTextContent ] = useState ( '' ) ;
7172 const [ deleteConfirmId , setDeleteConfirmId ] = useState < string | null > ( null ) ;
7273
73- const fileInputRef = useRef < HTMLInputElement > ( null ) ;
74-
7574 const kbId = knowledgeBase ?. id || user ?. id || '' ;
7675
7776 // ─── Load documents ───────────────────────────────────────
@@ -91,15 +90,16 @@ export default function DocumentsView({ isDark = true }: { isDark?: boolean }) {
9190 loadDocuments ( ) ;
9291 } , [ loadDocuments ] ) ;
9392
94- // ─── Upload handler ───────────────────────────────────────
95- const handleUpload = useCallback ( async ( file : File ) => {
93+ // ─── Direct Text Submission ───────────────────────────────────────
94+ const handleSubmitText = useCallback ( async ( ) => {
9695 if ( ! user ?. id || ! kbId ) return ;
97-
98- if ( file . size > MAX_FILE_SIZE ) {
99- alert ( `File too large. Maximum size is ${ formatBytes ( MAX_FILE_SIZE ) } .` ) ;
96+ if ( ! textTitle . trim ( ) || ! textContent . trim ( ) ) {
97+ alert ( 'Please provide both a title and text content.' ) ;
10098 return ;
10199 }
102100
101+ const syntheticFile = new File ( [ textContent ] , `${ textTitle . trim ( ) } .txt` , { type : 'text/plain' } ) ;
102+
103103 setProcessingProgress ( {
104104 stage : 'uploading' ,
105105 current : 0 ,
@@ -108,42 +108,20 @@ export default function DocumentsView({ isDark = true }: { isDark?: boolean }) {
108108 } ) ;
109109
110110 const result = await ragService . processDocument (
111- file ,
111+ syntheticFile ,
112112 kbId ,
113113 user . id ,
114114 ( progress ) => setProcessingProgress ( progress ) ,
115115 ) ;
116116
117117 if ( result ) {
118- // Refresh list
118+ setTextTitle ( '' ) ;
119+ setTextContent ( '' ) ;
119120 await loadDocuments ( ) ;
120121 }
121122
122- // Clear progress after a delay
123123 setTimeout ( ( ) => setProcessingProgress ( null ) , 3000 ) ;
124- } , [ user ?. id , kbId , loadDocuments ] ) ;
125-
126- const handleFileSelect = ( e : React . ChangeEvent < HTMLInputElement > ) => {
127- const file = e . target . files ?. [ 0 ] ;
128- if ( file ) handleUpload ( file ) ;
129- e . target . value = '' ;
130- } ;
131-
132- // ─── Drag & drop ──────────────────────────────────────────
133- const handleDragOver = ( e : React . DragEvent ) => {
134- e . preventDefault ( ) ;
135- setIsDragging ( true ) ;
136- } ;
137- const handleDragLeave = ( e : React . DragEvent ) => {
138- e . preventDefault ( ) ;
139- setIsDragging ( false ) ;
140- } ;
141- const handleDrop = ( e : React . DragEvent ) => {
142- e . preventDefault ( ) ;
143- setIsDragging ( false ) ;
144- const file = e . dataTransfer . files ?. [ 0 ] ;
145- if ( file ) handleUpload ( file ) ;
146- } ;
124+ } , [ user ?. id , kbId , textTitle , textContent , loadDocuments ] ) ;
147125
148126 // ─── Expand / view chunks ─────────────────────────────────
149127 const toggleExpand = async ( docId : string ) => {
@@ -192,47 +170,13 @@ export default function DocumentsView({ isDark = true }: { isDark?: boolean }) {
192170 { t ( 'kbDocs.description' ) }
193171 </ p >
194172 </ div >
195- < button
196- onClick = { ( ) => fileInputRef . current ?. click ( ) }
197- disabled = { ! ! processingProgress && processingProgress . stage !== 'done' && processingProgress . stage !== 'error' }
198- className = { cn (
199- "inline-flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm font-semibold transition-all duration-200 cursor-pointer border shadow-sm" ,
200- "bg-gradient-to-r from-[#FF8A5B] via-[#FF9E6C] to-[#FFB286] text-white border-[#FF8A5B]/30" ,
201- "hover:from-[#FF9E6C] hover:via-[#FF8A5B] hover:to-[#FFB286] hover:shadow-md hover:-translate-y-0.5" ,
202- "disabled:opacity-50 disabled:pointer-events-none"
203- ) }
204- >
205- < Upload className = "w-4 h-4" />
206- { t ( 'kbDocs.upload' ) }
207- </ button >
208- < input
209- ref = { fileInputRef }
210- type = "file"
211- accept = { ACCEPTED_TYPES }
212- className = "hidden"
213- onChange = { handleFileSelect }
214- />
215173 </ div >
216174
217- { /* Drop Zone */ }
218- < div
219- onDragOver = { handleDragOver }
220- onDragLeave = { handleDragLeave }
221- onDrop = { handleDrop }
222- onClick = { ( ) => {
223- if ( ! processingProgress || processingProgress . stage === 'done' || processingProgress . stage === 'error' ) {
224- fileInputRef . current ?. click ( ) ;
225- }
226- } }
227- className = { cn (
228- "relative rounded-xl border-2 border-dashed p-8 transition-all duration-200 cursor-pointer" ,
229- isDragging
230- ? "border-[#FF8A5B] bg-[#FF8A5B]/10 scale-[1.01]"
231- : isDark
232- ? "border-white/10 hover:border-white/20 bg-white/[0.02] hover:bg-white/[0.04] backdrop-blur-sm"
233- : "border-gray-200 hover:border-gray-300 bg-gray-50/50 hover:bg-gray-50" ,
234- ) }
235- >
175+ { /* Text Input Zone */ }
176+ < div className = { cn (
177+ "relative rounded-xl border p-6 transition-all duration-200" ,
178+ isDark ? "bg-white/5 border-white/10" : "bg-white border-gray-200 shadow-sm"
179+ ) } >
236180 { /* Processing Progress Overlay */ }
237181 { processingProgress && processingProgress . stage !== 'done' && processingProgress . stage !== 'error' && (
238182 < div className = "absolute inset-0 rounded-xl bg-black/50 backdrop-blur-sm flex items-center justify-center z-10" >
@@ -276,22 +220,55 @@ export default function DocumentsView({ isDark = true }: { isDark?: boolean }) {
276220 </ div >
277221 ) }
278222
279- < div className = "flex flex-col items-center gap-3" >
280- < div className = { cn (
281- "w-14 h-14 rounded-2xl flex items-center justify-center border" ,
282- isDark
283- ? "bg-white/5 border-white/10"
284- : "bg-gray-100 border-gray-200"
285- ) } >
286- < UploadCloud className = { cn ( "w-7 h-7" , isDark ? "text-white/40" : "text-gray-400" ) } />
223+ < div className = "space-y-4" >
224+ < div >
225+ < label className = { cn ( "block text-sm font-medium mb-1.5" , textPrimary ) } >
226+ Document Title
227+ </ label >
228+ < input
229+ type = "text"
230+ placeholder = "e.g. Employee Handbook"
231+ value = { textTitle }
232+ onChange = { ( e ) => setTextTitle ( e . target . value ) }
233+ className = { cn (
234+ "w-full rounded-lg px-4 py-2 border text-sm outline-none transition-all" ,
235+ isDark
236+ ? "bg-black/20 border-white/10 text-white placeholder:text-white/30 focus:border-[#FF8A5B]/50"
237+ : "bg-gray-50 border-gray-200 text-gray-900 placeholder:text-gray-400 focus:border-[#FF8A5B]"
238+ ) }
239+ />
240+ </ div >
241+ < div >
242+ < label className = { cn ( "block text-sm font-medium mb-1.5" , textPrimary ) } >
243+ Content
244+ </ label >
245+ < textarea
246+ placeholder = "Paste your text content here..."
247+ rows = { 6 }
248+ value = { textContent }
249+ onChange = { ( e ) => setTextContent ( e . target . value ) }
250+ className = { cn (
251+ "w-full rounded-lg px-4 py-3 border text-sm outline-none transition-all resize-y min-h-[120px]" ,
252+ isDark
253+ ? "bg-black/20 border-white/10 text-white placeholder:text-white/30 focus:border-[#FF8A5B]/50"
254+ : "bg-gray-50 border-gray-200 text-gray-900 placeholder:text-gray-400 focus:border-[#FF8A5B]"
255+ ) }
256+ />
287257 </ div >
288- < div className = "text-center" >
289- < p className = { cn ( "text-sm font-medium" , textPrimary ) } >
290- { t ( 'kbDocs.dropzone' ) }
291- </ p >
292- < p className = { cn ( "text-xs mt-1" , textMuted ) } >
293- PDF, TXT, MD, CSV, DOCX · Max { formatBytes ( MAX_FILE_SIZE ) }
294- </ p >
258+ < div className = "flex justify-end pt-2" >
259+ < button
260+ onClick = { handleSubmitText }
261+ disabled = { ! textTitle . trim ( ) || ! textContent . trim ( ) || ! ! processingProgress }
262+ className = { cn (
263+ "inline-flex items-center gap-2 px-5 py-2.5 rounded-xl text-sm font-semibold transition-all duration-200 cursor-pointer border shadow-sm" ,
264+ "bg-gradient-to-r from-[#FF8A5B] via-[#FF9E6C] to-[#FFB286] text-white border-[#FF8A5B]/30" ,
265+ "hover:from-[#FF9E6C] hover:via-[#FF8A5B] hover:to-[#FFB286] hover:shadow-md hover:-translate-y-0.5" ,
266+ "disabled:opacity-50 disabled:pointer-events-none"
267+ ) }
268+ >
269+ < FileText className = "w-4 h-4" />
270+ Save to Knowledge Base
271+ </ button >
295272 </ div >
296273 </ div >
297274 </ div >
@@ -488,12 +465,12 @@ export default function DocumentsView({ isDark = true }: { isDark?: boolean }) {
488465 { chunks . map ( ( chunk , idx ) => (
489466 < div
490467 key = { chunk . id }
491- className = { cn (
492- "rounded-lg border p-3 transition-colors" ,
493- isDark
468+ className = { cn (
469+ "rounded-lg border p-3 transition-colors" ,
470+ isDark
494471 ? "bg-white/[0.03] border-white/10 hover:bg-white/[0.05]"
495472 : "bg-white border-gray-200 hover:bg-gray-50"
496- ) }
473+ ) }
497474 >
498475 < div className = "flex items-start gap-3" >
499476 < span className = { cn (
0 commit comments