Skip to content

Conversation

@vibegui
Copy link
Contributor

@vibegui vibegui commented Dec 29, 2025

What is this contribution about?

Describe your changes and why they're needed.

Screenshots/Demonstration

Add screenshots or a Loom video if your changes affect the UI.

Review Checklist

  • PR title is clear and descriptive
  • Changes are tested and working
  • Documentation is updated (if needed)
  • No breaking changes

Summary by cubic

Adds first-class file storage with a local filesystem provider, MCP tools, and a simple file browser/preview with drag‑and‑drop uploads and a basic text editor. Enables reading, writing, and managing files via the FILE_STORAGE_BINDING, and supports uploading files directly from chat.

  • New Features

    • Implemented FILE_STORAGE_BINDING with file/folder schemas and collection bindings (FILES, FOLDERS).
    • Local File Storage provided by mcp-local-fs; file tools live in the MCP, while Mesh provides the binding and UI.
    • Added a well-known LOCAL_FS connection and file serving at GET /api/files/:path; storage is created per request with the correct base URL.
    • Web: hooks (list, content, upload, mutations), components (FileBrowser, FilePreview, TextEditor, FileDropZone), files/folders details views, and chat drag‑and‑drop uploads with inline attachment previews.
  • Migration

    • Optional: set FILE_STORAGE_ROOT to change the storage directory (defaults to ./storage).
    • Connect a provider implementing FILE_STORAGE_BINDING to enable uploads and file UI.

Written for commit 84ea598. Summary will update on new commits.

@github-actions
Copy link
Contributor

Release Options

Should a new version be published when this PR is merged?

React with an emoji to vote on the release type:

Reaction Type Next Version
👍 Prerelease 1.0.0-alpha.40
🎉 Patch 1.0.1
❤️ Minor 1.1.0
🚀 Major 2.0.0

Current version: 1.0.0-alpha.39

Deployment

  • Deploy to production (triggers ArgoCD sync after Docker image is published)

@vibegui vibegui force-pushed the feat/file-system branch 2 times, most recently from 089866b to 70aeb04 Compare December 30, 2025 01:32
@vibegui vibegui changed the title WIP Initial implementation of File Storage binding and UI Dec 30, 2025
@vibegui vibegui force-pushed the feat/file-system branch 2 times, most recently from ced1f65 to a017c0f Compare December 30, 2025 19:58
@vibegui vibegui marked this pull request as ready for review December 30, 2025 21:42
@vibegui vibegui requested a review from mcandeia December 30, 2025 21:42
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 23 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="apps/mesh/src/web/components/details/connection/collection-tab.tsx">

<violation number="1" location="apps/mesh/src/web/components/details/connection/collection-tab.tsx:245">
P1: Using `file.name` as the unique identifier is problematic. Multiple files with the same name can be uploaded, causing: (1) duplicate React keys, and (2) incorrect state updates where all files with matching names get updated together. Consider adding a unique ID (e.g., `crypto.randomUUID()`) to each upload entry.</violation>

<violation number="2" location="apps/mesh/src/web/components/details/connection/collection-tab.tsx:289">
P2: Significant code duplication between `handleFileDrop` and `handleFileInputChange`. Both functions share nearly identical upload logic (~35 lines). Consider extracting the shared logic into a reusable `processFileUploads(files: File[])` function.</violation>
</file>

<file name="apps/mesh/src/web/components/files/file-browser.tsx">

<violation number="1" location="apps/mesh/src/web/components/files/file-browser.tsx:106">
P2: Using folder name as React key can cause collisions for paths with duplicate folder names (e.g., `/data/archive/data/`). Use `index` as the key since breadcrumb order is stable and items aren&#39;t reordered.</violation>
</file>

<file name="apps/mesh/src/web/hooks/use-file-storage.ts">

<violation number="1" location="apps/mesh/src/web/hooks/use-file-storage.ts:157">
P2: The `invalidateFileQueries` helper function exists but is not used in `useFileUpload`. This duplicates the query invalidation logic. Use the helper function for consistency and maintainability.</violation>
</file>

<file name="apps/mesh/src/web/components/files/text-editor.tsx">

<violation number="1" location="apps/mesh/src/web/components/files/text-editor.tsx:100">
P2: State initialized from `initialContent` prop won&#39;t update if the prop changes (e.g., when switching files). Consider adding a `useEffect` to reset state when `initialContent` changes, or ensure the parent uses a `key` prop to force remount.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

) : null;

// Handle file uploads from button click
const handleFileInputChange = async (files: FileList | null) => {
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Significant code duplication between handleFileDrop and handleFileInputChange. Both functions share nearly identical upload logic (~35 lines). Consider extracting the shared logic into a reusable processFileUploads(files: File[]) function.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/details/connection/collection-tab.tsx, line 289:

<comment>Significant code duplication between `handleFileDrop` and `handleFileInputChange`. Both functions share nearly identical upload logic (~35 lines). Consider extracting the shared logic into a reusable `processFileUploads(files: File[])` function.</comment>

<file context>
@@ -195,6 +285,78 @@ export function CollectionTab({
   ) : null;
 
+  // Handle file uploads from button click
+  const handleFileInputChange = async (files: FileList | null) =&gt; {
+    if (!files || files.length === 0) return;
+
</file context>
Fix with Cubic


// Add files to uploading state
const newUploads = files.map((f) => ({
name: f.name,
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Using file.name as the unique identifier is problematic. Multiple files with the same name can be uploaded, causing: (1) duplicate React keys, and (2) incorrect state updates where all files with matching names get updated together. Consider adding a unique ID (e.g., crypto.randomUUID()) to each upload entry.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/details/connection/collection-tab.tsx, line 245:

<comment>Using `file.name` as the unique identifier is problematic. Multiple files with the same name can be uploaded, causing: (1) duplicate React keys, and (2) incorrect state updates where all files with matching names get updated together. Consider adding a unique ID (e.g., `crypto.randomUUID()`) to each upload entry.</comment>

<file context>
@@ -183,6 +209,70 @@ export function CollectionTab({
+
+    // Add files to uploading state
+    const newUploads = files.map((f) =&gt; ({
+      name: f.name,
+      status: &quot;uploading&quot; as const,
+    }));
</file context>
Fix with Cubic

>
/
</button>
{breadcrumbParts.map((part, index) => (
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Using folder name as React key can cause collisions for paths with duplicate folder names (e.g., /data/archive/data/). Use index as the key since breadcrumb order is stable and items aren't reordered.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/files/file-browser.tsx, line 106:

<comment>Using folder name as React key can cause collisions for paths with duplicate folder names (e.g., `/data/archive/data/`). Use `index` as the key since breadcrumb order is stable and items aren&#39;t reordered.</comment>

<file context>
@@ -0,0 +1,174 @@
+          &gt;
+            /
+          &lt;/button&gt;
+          {breadcrumbParts.map((part, index) =&gt; (
+            &lt;span key={part} className=&quot;flex items-center gap-1&quot;&gt;
+              &lt;span className=&quot;text-muted-foreground&quot;&gt;/&lt;/span&gt;
</file context>
Fix with Cubic


return result as { file: FileEntity };
},
onSuccess: () => {
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The invalidateFileQueries helper function exists but is not used in useFileUpload. This duplicates the query invalidation logic. Use the helper function for consistency and maintainability.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/hooks/use-file-storage.ts, line 157:

<comment>The `invalidateFileQueries` helper function exists but is not used in `useFileUpload`. This duplicates the query invalidation logic. Use the helper function for consistency and maintainability.</comment>

<file context>
@@ -0,0 +1,287 @@
+
+      return result as { file: FileEntity };
+    },
+    onSuccess: () =&gt; {
+      // Invalidate file lists to refresh
+      queryClient.invalidateQueries({
</file context>
Fix with Cubic

initialContent,
className,
}: TextEditorProps) {
const [content, setContent] = useState(initialContent);
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: State initialized from initialContent prop won't update if the prop changes (e.g., when switching files). Consider adding a useEffect to reset state when initialContent changes, or ensure the parent uses a key prop to force remount.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/files/text-editor.tsx, line 100:

<comment>State initialized from `initialContent` prop won&#39;t update if the prop changes (e.g., when switching files). Consider adding a `useEffect` to reset state when `initialContent` changes, or ensure the parent uses a `key` prop to force remount.</comment>

<file context>
@@ -0,0 +1,196 @@
+  initialContent,
+  className,
+}: TextEditorProps) {
+  const [content, setContent] = useState(initialContent);
+  const [hasChanges, setHasChanges] = useState(false);
+  const fileMutations = useFileMutations(connectionId);
</file context>
Fix with Cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants