carljmosca commited on
Commit
e5b1920
·
1 Parent(s): bb50e4c

feat: enhance App.jsx with file attachment and PDF/text parsing

Browse files

- Added support for attaching .txt and .pdf files in the chat interface
- Implemented PDF parsing using pdfjs-dist for extracting text content
- Updated UI to show attached file name and restrict file types
- Improved input resizing and message handling logic
- Updated dependencies in package.json for new features

Files changed (4) hide show
  1. .gitignore +1 -0
  2. package-lock.json +0 -0
  3. package.json +1 -0
  4. src/App.jsx +54 -6
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ node_modules
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json CHANGED
@@ -13,6 +13,7 @@
13
  "@huggingface/transformers": "3.7.5",
14
  "dompurify": "^3.1.2",
15
  "marked": "^12.0.2",
 
16
  "react": "^18.3.1",
17
  "react-dom": "^18.3.1"
18
  },
 
13
  "@huggingface/transformers": "3.7.5",
14
  "dompurify": "^3.1.2",
15
  "marked": "^12.0.2",
16
+ "pdfjs-dist": "^5.4.296",
17
  "react": "^18.3.1",
18
  "react-dom": "^18.3.1"
19
  },
src/App.jsx CHANGED
@@ -32,12 +32,39 @@ function App() {
32
  const [messages, setMessages] = useState([]);
33
  const [tps, setTps] = useState(null);
34
  const [numTokens, setNumTokens] = useState(null);
35
-
36
- function onEnter(message) {
37
- setMessages((prev) => [...prev, { role: "user", content: message }]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  setTps(null);
39
  setIsRunning(true);
40
  setInput("");
 
41
  }
42
 
43
  function onInterrupt() {
@@ -326,10 +353,10 @@ function App() {
326
  </div>
327
  )}
328
 
329
- <div className="mt-2 border dark:bg-gray-700 rounded-lg w-[600px] max-w-[80%] max-h-[200px] mx-auto relative mb-3 flex">
330
  <textarea
331
  ref={textareaRef}
332
- className="scrollbar-thin w-[550px] dark:bg-gray-700 px-3 py-4 rounded-lg bg-transparent border-none outline-none text-gray-800 disabled:text-gray-400 dark:text-gray-200 placeholder-gray-500 dark:placeholder-gray-400 disabled:placeholder-gray-200 resize-none disabled:cursor-not-allowed"
333
  placeholder="Type your message..."
334
  type="text"
335
  rows={1}
@@ -349,11 +376,29 @@ function App() {
349
  }}
350
  onInput={(e) => setInput(e.target.value)}
351
  />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
  {isRunning ? (
353
  <div className="cursor-pointer" onClick={onInterrupt}>
354
  <StopIcon className="h-8 w-8 p-1 rounded-md text-gray-800 dark:text-gray-100 absolute right-3 bottom-3" />
355
  </div>
356
- ) : input.length > 0 ? (
357
  <div className="cursor-pointer" onClick={() => onEnter(input)}>
358
  <ArrowRightIcon
359
  className={`h-8 w-8 p-1 bg-gray-800 dark:bg-gray-100 text-white dark:text-black rounded-md absolute right-3 bottom-3`}
@@ -366,6 +411,9 @@ function App() {
366
  />
367
  </div>
368
  )}
 
 
 
369
  </div>
370
 
371
  <p className="text-xs text-gray-400 text-center mb-3">
 
32
  const [messages, setMessages] = useState([]);
33
  const [tps, setTps] = useState(null);
34
  const [numTokens, setNumTokens] = useState(null);
35
+ const [attachedFile, setAttachedFile] = useState(null);
36
+
37
+ async function onEnter(message) {
38
+ let fileText = "";
39
+ if (attachedFile) {
40
+ if (attachedFile.name.endsWith(".txt")) {
41
+ fileText = await attachedFile.text();
42
+ } else if (attachedFile.name.endsWith(".pdf")) {
43
+ // Dynamically import pdfjs-dist
44
+ const pdfjsLib = await import("pdfjs-dist/build/pdf");
45
+ const workerSrc = (await import("pdfjs-dist/build/pdf.worker?url")).default;
46
+ pdfjsLib.GlobalWorkerOptions.workerSrc = workerSrc;
47
+ const arrayBuffer = await attachedFile.arrayBuffer();
48
+ const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
49
+ let pdfText = "";
50
+ for (let i = 1; i <= pdf.numPages; i++) {
51
+ const page = await pdf.getPage(i);
52
+ const content = await page.getTextContent();
53
+ pdfText += content.items.map(item => item.str).join(" ") + "\n";
54
+ }
55
+ fileText = pdfText;
56
+ }
57
+ }
58
+ let fullPrompt = message;
59
+ if (fileText) {
60
+ fullPrompt += "\n\n--- File Content ---\n" + fileText;
61
+ }
62
+ let userMsg = { role: "user", content: fullPrompt };
63
+ setMessages((prev) => [...prev, userMsg]);
64
  setTps(null);
65
  setIsRunning(true);
66
  setInput("");
67
+ setAttachedFile(null);
68
  }
69
 
70
  function onInterrupt() {
 
353
  </div>
354
  )}
355
 
356
+ <div className="mt-2 border dark:bg-gray-700 rounded-lg w-[600px] max-w-[80%] max-h-[200px] mx-auto relative mb-3 flex items-center gap-2">
357
  <textarea
358
  ref={textareaRef}
359
+ className="scrollbar-thin w-[420px] dark:bg-gray-700 px-3 py-4 rounded-lg bg-transparent border-none outline-none text-gray-800 disabled:text-gray-400 dark:text-gray-200 placeholder-gray-500 dark:placeholder-gray-400 disabled:placeholder-gray-200 resize-none disabled:cursor-not-allowed"
360
  placeholder="Type your message..."
361
  type="text"
362
  rows={1}
 
376
  }}
377
  onInput={(e) => setInput(e.target.value)}
378
  />
379
+ <label className="flex items-center px-2 py-2 bg-blue-100 text-blue-700 rounded cursor-pointer hover:bg-blue-200">
380
+ 📎 Attach
381
+ <input
382
+ type="file"
383
+ accept=".txt,.pdf"
384
+ className="hidden"
385
+ onChange={e => {
386
+ const file = e.target.files[0];
387
+ if (file && ![".txt", ".pdf"].some(ext => file.name.toLowerCase().endsWith(ext))) {
388
+ alert("Only .txt and .pdf files are allowed.");
389
+ e.target.value = "";
390
+ return;
391
+ }
392
+ setAttachedFile(file);
393
+ }}
394
+ disabled={status !== "ready"}
395
+ />
396
+ </label>
397
  {isRunning ? (
398
  <div className="cursor-pointer" onClick={onInterrupt}>
399
  <StopIcon className="h-8 w-8 p-1 rounded-md text-gray-800 dark:text-gray-100 absolute right-3 bottom-3" />
400
  </div>
401
+ ) : input.length > 0 || attachedFile ? (
402
  <div className="cursor-pointer" onClick={() => onEnter(input)}>
403
  <ArrowRightIcon
404
  className={`h-8 w-8 p-1 bg-gray-800 dark:bg-gray-100 text-white dark:text-black rounded-md absolute right-3 bottom-3`}
 
411
  />
412
  </div>
413
  )}
414
+ {attachedFile && (
415
+ <span className="ml-2 text-xs text-gray-600">{attachedFile.name}</span>
416
+ )}
417
  </div>
418
 
419
  <p className="text-xs text-gray-400 text-center mb-3">