You are an expert React developer tasked with modifying the UI of my nextjs site based on my query and files below. Carefully think through this task below and generate high quality code to accomplish the task by modifying the site's files below:Task:"How can you modify my nextjs site so that the file list is displayed on the left side panel (which is expanded/reduced from the arrow button on the left side of the screen)?
Carefully analyze my files to determine how to modify them in order to have the 'filelist' features on the left panel rather than the top header of the page which is where they currently are"
here is my app's files structure and content labeled with xml tags:<context>
<directory_tree>
cool-code/
├── app/
│   ├── api/
│   │   └── github/
│   │   │   └── route.ts
│   ├── globals.css
│   ├── layout.tsx
│   ├── page.module.css
│   └── page.tsx
├── components/
│   ├── FileContent.module.css
│   ├── FileContent.tsx
│   ├── FileList.module.css
│   ├── FileList.tsx
│   ├── RepoViewer.module.css
│   ├── RepoViewer.tsx
│   └── icons.ts
├── cool-code_prompt.txt
├── next.config.js
├── package.json
└── tsconfig.json
</directory_tree>

<files>
<file>
<path>app/globals.css</path>
<content>
/* app/globals.css */

body {
  margin: 0;
  padding: 0;
  background-color: #121212;
  color: #ffffff;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

</content>
</file>

<file>
<path>app/layout.tsx</path>
<content>
// app/layout.tsx

import { Inter } from 'next/font/google';
import './globals.css';
import '../components/icons'; // Import the icons

const inter = Inter({ subsets: ['latin'] });

export const metadata = {
  title: 'GitHub Repository Viewer',
  description: 'View contents of GitHub repositories',
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>{children}</body>
    </html>
  );
}

</content>
</file>

<file>
<path>app/page.module.css</path>
<content>
.main {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 2rem;
  min-height: 100vh;
}

.form {
  display: flex;
  justify-content: center;
  width: 100%;
  margin-bottom: 2rem;
}

.input {
  width: 50%;
  padding: 0.5rem;
  font-size: 1rem;
}

.button {
  padding: 0.5rem 1rem;
  font-size: 1rem;
  background-color: #0070f3;
  color: white;
  border: none;
  cursor: pointer;
}

.content {
  display: flex;
  width: 100%;
  height: calc(100vh - 150px);
}

.fileList {
  width: 30%;
  overflow-y: auto;
  border-right: 1px solid #ccc;
  padding: 1rem;
}

.fileItem {
  cursor: pointer;
  padding: 0.5rem;
  border: 1px solid #ccc;
  margin-bottom: 0.5rem;
  border-radius: 4px;
}

.fileContent {
  width: 70%;
  overflow-y: auto;
  padding: 1rem;
  background-color: #f0f0f0;
}

</content>
</file>

<file>
<path>app/page.tsx</path>
<content>
// app/page.tsx

import RepoViewer from '../components/RepoViewer';

export default function Home() {
  return <RepoViewer />;
}

</content>
</file>

<file>
<path>components/FileContent.module.css</path>
<content>
.fileContent {
  width: 70%;
  overflow-y: auto;
  padding: 1rem;
  background-color: #f0f0f0;
}
.fileContent pre {
  white-space: pre-wrap;
  word-wrap: break-word;
}

</content>
</file>

<file>
<path>components/FileContent.tsx</path>
<content>
// components/FileContent.tsx

import React from 'react';
import styles from './FileContent.module.css';

interface File {
  name: string;
  path: string;
  type: 'file' | 'dir';
  content?: string;
  files?: File[];
}

interface FileContentProps {
  file: File | null;
}

const FileContent: React.FC<FileContentProps> = ({ file }) => {
  if (!file) {
    return <div className={styles.fileContent}>Select a file to view its content</div>;
  }

  return (
    <div className={styles.fileContent}>
      <h3>{file.name}</h3>
      <pre>{file.content}</pre>
    </div>
  );
};

export default FileContent;

</content>
</file>

<file>
<path>components/FileList.module.css</path>
<content>
/* components/FileList.module.css */

.fileList {
  width: 100%;
  overflow-y: auto;
  background-color: #252526; /* VSCode Dark Background */
  color: #CCCCCC; /* Light Gray Text */
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; /* VSCode Font */
  padding: 8px 0;
}

.fileItem {
  padding: 4px 12px;
  font-size: 14px;
  cursor: pointer;
  display: flex;
  align-items: center;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.fileItem:hover {
  background-color: #37373d; /* Darker Hover Background */
}

.fileItem.selected {
  background-color: #094771; /* VSCode Selected Background */
  color: #ffffff; /* Selected Text Color */
}

.fileName {
  display: flex;
  align-items: center;
  word-break: break-word;
}

.icon {
  margin-right: 8px;
  font-size: 16px;
}

.folderArrow {
  margin-right: 4px;
  font-size: 12px;
}

.indent {
  margin-left: 16px; /* Increased indentation for better nesting visibility */
}

</content>
</file>

<file>
<path>components/FileList.tsx</path>
<content>
// components/FileList.tsx

import React, { useState } from 'react';
import styles from './FileList.module.css';
import { FaFile, FaFolder, FaFolderOpen } from 'react-icons/fa';

interface File {
  name: string;
  path: string;
  type: 'file' | 'dir';
  content?: string;
  files?: File[];
}

interface FileListProps {
  files: File[];
  selectedFiles: Set<string>;
  onFileSelect: (file: File) => void;
}

const FileList: React.FC<FileListProps> = ({ files, selectedFiles, onFileSelect }) => {
  const [expandedFolders, setExpandedFolders] = useState<Set<string>>(new Set());

  const toggleFolder = (path: string) => {
    setExpandedFolders((prev) => {
      const newSet = new Set(prev);
      if (newSet.has(path)) {
        newSet.delete(path);
      } else {
        newSet.add(path);
      }
      return newSet;
    });
  };

  const renderFiles = (files: File[], depth = 0) => {
    return files.map((file) => (
      <div key={file.path}>
        <div
          className={`${styles.fileItem} ${selectedFiles.has(file.path) ? styles.selected : ''}`}
          onClick={() => (file.type === 'file' ? onFileSelect(file) : toggleFolder(file.path))}
          style={{ paddingLeft: `${depth * 16}px` }} // Dynamic indentation based on depth
        >
          <span className={styles.fileName}>
            {file.type === 'dir' ? (
              <span className={styles.icon}>
                {expandedFolders.has(file.path) ? <FaFolderOpen /> : <FaFolder />}
              </span>
            ) : (
              <span className={styles.icon}>
                <FaFile />
              </span>
            )}
            {file.name}
          </span>
        </div>
        {file.type === 'dir' && expandedFolders.has(file.path) && file.files && renderFiles(file.files, depth + 1)}
      </div>
    ));
  };

  return <div className={styles.fileList}>{renderFiles(files)}</div>;
};

export default FileList;

</content>
</file>

<file>
<path>components/RepoViewer.module.css</path>
<content>
/* components/RepoViewer.module.css */

/* Existing styles ... */

.container {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  background-color: #121212;
  color: #ffffff;
}

.error {
  color: #ff6b6b;
  margin: 1rem;
  text-align: center;
}

.content {
  display: flex;
  flex: 1;
  overflow: hidden;
  transition: margin-left 0.3s ease;
}

.content.shifted {
  margin-left: 250px; /* Match the expanded width of the panel */
}

.fileListContainer {
  width: 250px; /* Expanded width */
  transition: width 0.3s ease;
  overflow: hidden;
  background-color: #1e1e1e; /* Ensure background matches */
  height: 100%;
  position: relative;
}

.fileListContainer.collapsed {
  width: 0; /* Collapsed width */
  padding: 0; /* Remove padding when collapsed */
}

.fileListContainer.open {
  width: 250px; /* Ensure width is set when open */
}

.toggleButton {
  position: fixed;
  top: 50%;
  left: 0;
  transform: translate(-50%, -50%);
  background-color: #1e1e1e;
  border: 1px solid #444;
  border-radius: 50%;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: #fff;
  z-index: 1000; /* Ensure it stays above other elements */
  transition: left 0.3s ease, transform 0.3s ease;
}

.fileListContainer.open + .toggleButton {
  left: 250px; /* Match the expanded width of the panel */
  transform: translate(-50%, -50%);
}

.toggleButton:hover {
  background-color: #333;
}

.fileContentWrapper {
  flex: 1;
  transition: margin-left 0.3s ease;
  padding: 1rem;
  background-color: #1e1e1e; /* Dark background to match overall theme */
  color: #ffffff;
  overflow-y: auto;
}

.conversationContainer {
  display: flex;
  flex-direction: column;
  gap: 10px;
  height: 100%;
  overflow-y: auto;
  word-wrap: break-word;
  overflow-wrap: break-word;
}

.messageBubble {
  padding: 10px;
  border-radius: 8px;
  max-width: 80%;
  word-wrap: break-word;
  overflow-wrap: break-word;
}

.user {
  align-self: flex-end;
  background-color: #1e3a8a;
}

.assistant {
  align-self: flex-start;
  background-color: #2c2c2c;
}

.promptContainer {
  position: fixed;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 60%;
  max-width: 800px;
  background-color: #2c2c2c;
  padding: 10px;
  box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.3);
  display: flex;
  flex-direction: column;
  align-items: center;
  transition: all 0.3s ease;
}

.promptContainer.minimized {
  width: auto;
  height: auto;
  background-color: transparent;
  box-shadow: none;
}

.minimizeButton {
  position: absolute;
  top: -20px;
  left: 50%;
  transform: translateX(-50%);
  width: 40px;
  height: 20px;
  background-color: #2c2c2c;
  border: none;
  border-top-left-radius: 20px;
  border-top-right-radius: 20px;
  cursor: pointer;
  display: flex;
  justify-content: center;
  align-items: center;
  box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.3);
  color: #ffffff;
  font-size: 14px;
}

.minimizeButton::before {
  content: '';
  width: 0;
  height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-top: 6px solid #ffffff;
  transition: transform 0.2s ease;
}

.minimized .minimizeButton::before {
  transform: rotate(180deg);
}

.promptForm {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.conversationContainer.fullHeight {
  height: calc(100vh - 100px);
}

.inputContainer {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 20px;
}

.inputBox {
  width: 80%;
  padding: 0.5rem;
  font-size: 1rem;
  margin-bottom: 1rem;
  background-color: #2c2c2c;
  color: #fff;
  border: none;
  border-radius: 4px;
}

.submitButton {
  background-color: #0070f3;
  color: #fff;
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.submitButton:disabled {
  background-color: #444444;
  cursor: not-allowed;
}

.promptInput {
  width: 100%;
  min-height: 50px;
  padding: 0.5rem;
  resize: none;
  border: none;
  border-radius: 4px;
  margin-bottom: 0.5rem;
}

.buttonContainer {
  display: flex;
  justify-content: flex-end;
  width: 100%;
}

.promptButton {
  background-color: #198960;
  color: white;
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.promptButton:disabled {
  background-color: #444444;
  cursor: not-allowed;
}

</content>
</file>

<file>
<path>components/RepoViewer.tsx</path>
<content>
// components/RepoViewer.tsx

'use client';

import React, { useState, useRef, useEffect } from 'react';
import styles from './RepoViewer.module.css';
import FileList from './FileList';
import FileContent from './FileContent';
import { useChat } from 'ai/react';
import ReactMarkdown from 'react-markdown';
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa'; // Import toggle icons

interface File {
  name: string;
  path: string;
  type: 'file' | 'dir';
  content?: string;
  files?: File[];
}

interface CachedRepo {
  timestamp: number;
  files: File[];
}

const RepoViewer: React.FC = () => {
  const [repoUrl, setRepoUrl] = useState('');
  const [files, setFiles] = useState<File[]>([]);
  const [selectedFile, setSelectedFile] = useState<File | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [loading, setLoading] = useState(false);
  const [selectedFiles, setSelectedFiles] = useState<Set<string>>(new Set());
  const [fileContents, setFileContents] = useState<{ [path: string]: string }>({});
  const [isPromptMinimized, setIsPromptMinimized] = useState(false);
  const [isFileListOpen, setIsFileListOpen] = useState(false); // State for toggling file list

  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
    api: '/api/chat',
    onFinish: () => {
      // Any additional logic to run when the chat response is complete
    },
  });

  const messagesEndRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (messagesEndRef.current) {
      messagesEndRef.current.scrollIntoView({ behavior: 'smooth' });
    }
  }, [messages]);

  useEffect(() => {
    clearExpiredCache();
  }, []);

  const clearExpiredCache = () => {
    const now = Date.now();
    Object.keys(localStorage).forEach((key) => {
      if (key.startsWith('repo_')) {
        const cachedRepo = JSON.parse(localStorage.getItem(key) || '{}') as CachedRepo;
        if (now - cachedRepo.timestamp > 10 * 60 * 60 * 1000) { // 10 hours
          localStorage.removeItem(key);
        }
      }
    });
  };

  const handleRepoSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setError(null);
    setLoading(true);
    setFiles([]);
    setSelectedFile(null);

    const urlParts = repoUrl.split('/');
    const owner = urlParts[urlParts.length - 2];
    const repo = urlParts[urlParts.length - 1];

    if (!owner || !repo) {
      setError('Invalid repository URL. Please enter a valid GitHub repository URL.');
      setLoading(false);
      return;
    }

    try {
      const cacheKey = `repo_${owner}_${repo}`;
      const cachedData = localStorage.getItem(cacheKey);
      let repoFiles: File[];

      if (cachedData) {
        const { timestamp, files } = JSON.parse(cachedData) as CachedRepo;
        const now = Date.now();
        if (now - timestamp <= 10 * 60 * 60 * 1000) { // 10 hours
          repoFiles = files;
        } else {
          localStorage.removeItem(cacheKey);
          repoFiles = await fetchRepoContents(owner, repo);
        }
      } else {
        repoFiles = await fetchRepoContents(owner, repo);
      }

      setFiles(repoFiles);
      setIsFileListOpen(true); // Automatically open the file list panel when repo is loaded
      localStorage.setItem(
        cacheKey,
        JSON.stringify({
          timestamp: Date.now(),
          files: repoFiles,
        })
      );
    } catch (error: any) {
      setError(`Error fetching repository contents: ${error.message}`);
    } finally {
      setLoading(false);
    }
  };

  const fetchRepoContents = async (owner: string, repo: string): Promise<File[]> => {
    const response = await fetch(`/api/github?owner=${owner}&repo=${repo}&fetchAll=true`);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    return data;
  };

  const fetchDirectoryContents = async (path: string): Promise<File[]> => {
    const urlParts = repoUrl.split('/');
    const owner = urlParts[urlParts.length - 2];
    const repo = urlParts[urlParts.length - 1];

    try {
      const response = await fetch(`/api/github?owner=${owner}&repo=${repo}&path=${path}`);
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      const data = await response.json();
      return data;
    } catch (error: any) {
      console.error(`Error fetching directory contents: ${error.message}`);
      return [];
    }
  };

  const fetchFileContent = async (path: string): Promise<string> => {
    const urlParts = repoUrl.split('/');
    const owner = urlParts[urlParts.length - 2];
    const repo = urlParts[urlParts.length - 1];

    try {
      const cacheKey = `repo_${owner}_${repo}`;
      const cachedData = localStorage.getItem(cacheKey);

      if (cachedData) {
        const { files } = JSON.parse(cachedData) as CachedRepo;
        const file = findFileByPath(files, path);
        if (file && file.content) {
          return file.content;
        }
      }

      // If not found in cache, fetch from API
      const response = await fetch(`/api/github?owner=${owner}&repo=${repo}&path=${path}`);
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      const data = await response.json();
      if (typeof data === 'object' && 'content' in data && typeof data.content === 'string') {
        return atob(data.content);
      } else {
        throw new Error('Unexpected response format');
      }
    } catch (error: any) {
      setError(`Error fetching file content: ${error.message}`);
      throw error;
    }
  };

  const findFileByPath = (files: File[], path: string): File | null => {
    for (const file of files) {
      if (file.path === path) {
        return file;
      }
      if (file.type === 'dir' && file.files) {
        const found = findFileByPath(file.files, path);
        if (found) return found;
      }
    }
    return null;
  };

  const handleFileSelect = async (file: File) => {
    setSelectedFiles((prev) => {
      const newSet = new Set(prev);
      if (newSet.has(file.path)) {
        newSet.delete(file.path);
      } else {
        newSet.add(file.path);
      }
      return newSet;
    });

    if (file.type === 'file' && !fileContents[file.path]) {
      const content = await fetchFileContent(file.path);
      setFileContents((prev) => ({ ...prev, [file.path]: content }));
    }
  };

  const handleChatSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    const selectedFileContents = Array.from(selectedFiles).reduce((acc, path) => {
      if (fileContents[path]) {
        acc[path] = fileContents[path];
      }
      return acc;
    }, {} as { [path: string]: string });

    const fileContextMessage = Object.entries(selectedFileContents)
      .map(([path, content]) => `File: ${path}\n\nContent:\n${content}\n\n`)
      .join('---\n\n');

    const fullPrompt = `Here are the contents of the selected files:\n\n${fileContextMessage}\nUser query: ${input}\n\nPlease provide an answer based on the file contents and the user's query.`;

    handleSubmit(e, {
      options: {
        body: { prompt: fullPrompt },
      },
    });
  };

  const togglePromptMinimize = () => {
    setIsPromptMinimized(!isPromptMinimized);
  };

  const toggleFileList = () => { // Toggle function for file list
    setIsFileListOpen(!isFileListOpen);
  };

  return (
    <main className={styles.container}>
      {/* Removed the sticky header containing the GitHub URL input */}

      {error && <div className={styles.error}>{error}</div>}
      <div className={`${styles.content} ${isFileListOpen ? styles.shifted : ''}`}>
        <div className={`${styles.fileListContainer} ${isFileListOpen ? 'open' : 'collapsed'}`}>
          {files.length === 0 ? (
            <form onSubmit={handleRepoSubmit} className={styles.inputContainer}>
              <input
                type="text"
                value={repoUrl}
                onChange={(e) => setRepoUrl(e.target.value)}
                placeholder="Enter GitHub repository URL (e.g., https://github.com/owner/repo)"
                className={styles.inputBox}
              />
              <button type="submit" className={styles.submitButton} disabled={loading}>
                {loading ? 'Loading...' : 'Load Repo'}
              </button>
            </form>
          ) : (
            <FileList
              files={files}
              selectedFiles={selectedFiles}
              onFileSelect={handleFileSelect}
            />
          )}
        </div>
        <div className={styles.fileContentWrapper}>
          {selectedFile ? (
            <FileContent file={selectedFile} />
          ) : (
            <div className={`${styles.conversationContainer} ${isPromptMinimized ? styles.fullHeight : ''}`}>
              {messages.map((m, index) => (
                <div key={index} className={`${styles.messageBubble} ${styles[m.role]}`}>
                  {m.role === 'user' ? 'User: ' : 'AI: '}
                  <ReactMarkdown>{m.content}</ReactMarkdown>
                </div>
              ))}
              <div ref={messagesEndRef} />
            </div>
          )}
        </div>
      </div>
      {/* Toggle Button */}
      <button
        onClick={toggleFileList}
        className={styles.toggleButton}
        aria-label={isFileListOpen ? 'Collapse file list' : 'Expand file list'}
      >
        {isFileListOpen ? <FaChevronLeft /> : <FaChevronRight />}
      </button>
      <div className={`${styles.promptContainer} ${isPromptMinimized ? styles.minimized : ''}`}>
        <button onClick={togglePromptMinimize} className={styles.minimizeButton} aria-label={isPromptMinimized ? 'Maximize prompt' : 'Minimize prompt'}>
          {/* You can add an icon here if desired */}
          {isPromptMinimized ? '🔼' : '🔽'}
        </button>
        {!isPromptMinimized && (
          <form onSubmit={handleChatSubmit} className={styles.promptForm}>
            <textarea
              value={input}
              onChange={handleInputChange}
              onInput={(e) => {
                const target = e.target as HTMLTextAreaElement;
                target.style.height = 'auto';
                target.style.height = `${target.scrollHeight}px`;
              }}
              placeholder="Ask about the selected files..."
              className={styles.promptInput}
              disabled={isLoading}
            />
            <div className={styles.buttonContainer}>
              <button type="submit" className={styles.promptButton} disabled={isLoading}>
                {isLoading ? 'Sending...' : 'Send'}
              </button>
            </div>
          </form>
        )}
      </div>
    </main>
  );
};

export default RepoViewer;

</content>
</file>

</files>
</context>
Carefully analyze my files above along with the task at the beginning of the image. Then, respond with the neccessary code changes that need to be made to accomplish the task: