110 lines
3.4 KiB
TypeScript
110 lines
3.4 KiB
TypeScript
/**
|
|
* FileListPanel
|
|
*
|
|
* Lists text files grouped by date, draggable into the prompt textarea.
|
|
* Drag a file into the chat input to insert an @fileName reference.
|
|
*/
|
|
|
|
import React, { useMemo } from 'react';
|
|
import { FaFile, FaGripVertical } from 'react-icons/fa';
|
|
import styles from './CodeEditor.module.css';
|
|
|
|
export interface FileInfo {
|
|
fileId: string;
|
|
fileName: string;
|
|
mimeType: string;
|
|
sizeBytes: number;
|
|
modifiedAt?: number;
|
|
}
|
|
|
|
interface FileListPanelProps {
|
|
files: FileInfo[];
|
|
}
|
|
|
|
interface DateGroup {
|
|
label: string;
|
|
files: FileInfo[];
|
|
}
|
|
|
|
export const FileListPanel: React.FC<FileListPanelProps> = ({ files }) => {
|
|
const groups = useMemo(() => _groupByDate(files), [files]);
|
|
|
|
const handleDragStart = (e: React.DragEvent, file: FileInfo) => {
|
|
e.dataTransfer.setData('application/x-codeeditor-file', JSON.stringify({
|
|
fileId: file.fileId,
|
|
fileName: file.fileName,
|
|
}));
|
|
e.dataTransfer.setData('text/plain', `@${file.fileName}`);
|
|
e.dataTransfer.effectAllowed = 'copy';
|
|
};
|
|
|
|
return (
|
|
<div className={styles.fileList}>
|
|
<div className={styles.panelHeader}>
|
|
<h3>Files ({files.length})</h3>
|
|
<span className={styles.dragHint}>drag into prompt</span>
|
|
</div>
|
|
<div className={styles.fileItems}>
|
|
{files.length === 0 ? (
|
|
<div className={styles.emptyState}>No text files uploaded yet</div>
|
|
) : (
|
|
groups.map((group) => (
|
|
<div key={group.label} className={styles.dateGroup}>
|
|
<div className={styles.dateGroupHeader}>{group.label}</div>
|
|
{group.files.map((file) => (
|
|
<div
|
|
key={file.fileId}
|
|
className={styles.fileItem}
|
|
draggable
|
|
onDragStart={(e) => handleDragStart(e, file)}
|
|
>
|
|
<FaGripVertical className={styles.dragHandle} />
|
|
<FaFile className={styles.fileIcon} />
|
|
<span className={styles.fileName}>{file.fileName}</span>
|
|
<span className={styles.fileSize}>{_formatSize(file.sizeBytes)}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
function _groupByDate(files: FileInfo[]): DateGroup[] {
|
|
const now = new Date();
|
|
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime() / 1000;
|
|
const yesterdayStart = todayStart - 86400;
|
|
|
|
const today: FileInfo[] = [];
|
|
const yesterday: FileInfo[] = [];
|
|
const older: FileInfo[] = [];
|
|
|
|
const sorted = [...files].sort((a, b) => (b.modifiedAt || 0) - (a.modifiedAt || 0));
|
|
|
|
for (const file of sorted) {
|
|
const ts = file.modifiedAt || 0;
|
|
if (ts >= todayStart) {
|
|
today.push(file);
|
|
} else if (ts >= yesterdayStart) {
|
|
yesterday.push(file);
|
|
} else {
|
|
older.push(file);
|
|
}
|
|
}
|
|
|
|
const groups: DateGroup[] = [];
|
|
if (today.length > 0) groups.push({ label: 'Today', files: today });
|
|
if (yesterday.length > 0) groups.push({ label: 'Yesterday', files: yesterday });
|
|
if (older.length > 0) groups.push({ label: 'Older', files: older });
|
|
if (groups.length === 0 && files.length > 0) groups.push({ label: 'All files', files: sorted });
|
|
|
|
return groups;
|
|
}
|
|
|
|
function _formatSize(bytes: number): string {
|
|
if (bytes < 1024) return `${bytes} B`;
|
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
}
|