From 2db8d7df3f78caec2559be854e52c33a12d26dbd Mon Sep 17 00:00:00 2001 From: Om Gupta Date: Thu, 23 Apr 2026 14:23:05 +0530 Subject: [PATCH] feat(desktop): remove new-project attachments, add text-file creator in files view Drop the "Files & context" dropzone and "Suggest name" button from the new project form. Expand the Upload button in the project files view into an add-menu offering "Upload from device" and a new "Add text content" modal that writes a .md file directly to the project. Co-Authored-By: Claude Opus 4.7 --- .../src/components/files/ProjectFilesView.tsx | 190 ++++++++++++++++-- .../components/projects/NewProjectView.tsx | 47 +---- packages/desktop/src/index.css | 137 ++++++++----- 3 files changed, 269 insertions(+), 105 deletions(-) diff --git a/packages/desktop/src/components/files/ProjectFilesView.tsx b/packages/desktop/src/components/files/ProjectFilesView.tsx index 0d2a2c3d..ece9212a 100644 --- a/packages/desktop/src/components/files/ProjectFilesView.tsx +++ b/packages/desktop/src/components/files/ProjectFilesView.tsx @@ -11,8 +11,11 @@ import { Home, Image, Loader2, + Paperclip, + Plus, Search, Trash2, + Type, Upload, X, } from 'lucide-react' @@ -151,6 +154,12 @@ export function ProjectFilesView() { const [newFolderOpen, setNewFolderOpen] = useState(false) const [newFolderName, setNewFolderName] = useState('') + // Add menu + text-content modal + const [addMenuOpen, setAddMenuOpen] = useState(false) + const [textModalOpen, setTextModalOpen] = useState(false) + const [textTitle, setTextTitle] = useState('') + const [textBody, setTextBody] = useState('') + // Preview pane state const [selected, setSelected] = useState(null) const [preview, setPreview] = useState(null) @@ -160,6 +169,8 @@ export function ProjectFilesView() { const fileInputRef = useRef(null) const newFolderInputRef = useRef(null) + const addMenuRef = useRef(null) + const textTitleInputRef = useRef(null) // Breadcrumbs const breadcrumbs = useMemo(() => { @@ -285,6 +296,23 @@ export function ProjectFilesView() { return unsub }, [refresh]) + useEffect(() => { + if (!addMenuOpen) return + const onDown = (e: MouseEvent) => { + if (!addMenuRef.current) return + if (!addMenuRef.current.contains(e.target as Node)) setAddMenuOpen(false) + } + const onKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') setAddMenuOpen(false) + } + window.addEventListener('mousedown', onDown) + window.addEventListener('keydown', onKey) + return () => { + window.removeEventListener('mousedown', onDown) + window.removeEventListener('keydown', onKey) + } + }, [addMenuOpen]) + useEffect(() => { if (!loading) return const timer = setTimeout(() => { @@ -386,6 +414,23 @@ export function ProjectFilesView() { setNewFolderName('') } + const cancelTextModal = () => { + setTextModalOpen(false) + setTextTitle('') + setTextBody('') + } + + const canSubmitText = textTitle.trim().length > 0 && textBody.trim().length > 0 + + const submitTextContent = () => { + if (!canSubmitText) return + const raw = textTitle.trim() + const hasExt = /\.[A-Za-z0-9]{1,8}$/.test(raw) + const filename = hasExt ? raw : `${raw}.md` + connection.sendFilesystemWrite(resolvePath(filename), textBody, 'utf-8') + cancelTextModal() + } + const handleDelete = () => { if (!selected) return setDeleteTarget({ name: selected.name, path: resolvePath(selected.name) }) @@ -478,10 +523,49 @@ export function ProjectFilesView() {
- +
+ + {addMenuOpen && ( +
+ + +
+ )} +
{/* Split: list + preview */} -
+
- {/* Preview pane */} - + + )}
{dragging && ( @@ -804,6 +881,83 @@ export function ProjectFilesView() {
)} + + {textModalOpen && ( +
{ + if (e.key === 'Escape') cancelTextModal() + }} + > +
e.stopPropagation()} + onKeyDown={(e) => e.stopPropagation()} + > +
+

Add text content

+ +
+
+
+ + setTextTitle(e.target.value)} + onKeyDown={(e) => { + const mod = e.metaKey || e.ctrlKey + if (mod && e.key === 'Enter') submitTextContent() + }} + /> +
+
+ +