diff --git a/apps/landing/src/app/demo/upload/page.tsx b/apps/landing/src/app/demo/upload/page.tsx
index c323d42..11db857 100644
--- a/apps/landing/src/app/demo/upload/page.tsx
+++ b/apps/landing/src/app/demo/upload/page.tsx
@@ -4,6 +4,7 @@ import { useState, useEffect, useRef, DragEvent, ChangeEvent } from 'react';
import { MinimizedApiKey } from '@/components/demo/MinimizedApiKey';
import { CodeExamplesWidget } from '@/components/demo/CodeExamplesWidget';
import { ImageZoomModal } from '@/components/demo/ImageZoomModal';
+import { SelectedFileCodePreview } from '@/components/demo/SelectedFileCodePreview';
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000';
const API_KEY_STORAGE_KEY = 'banatie_demo_api_key';
@@ -330,14 +331,25 @@ export default function DemoUploadPage() {
};
const generateUploadCodeExamples = (item: UploadHistoryItem, key: string, baseUrl: string) => {
- const localPath = `./${item.originalName}`;
+ const fileName = item.originalName;
return {
- curl: `curl -X POST "${baseUrl}/api/upload" \\
+ curl: `# Navigate to your images folder
+cd /your/images/folder
+
+# Upload the file
+curl -X POST "${baseUrl}/api/upload" \\
-H "X-API-Key: ${key}" \\
- -F "file=@${localPath}"`,
- fetch: `const formData = new FormData();
-formData.append('file', fileInput.files[0]);
+ -F "file=@${fileName}"`,
+ fetch: `// Set your images folder path
+const imagePath = '/your/images/folder';
+const fileName = '${fileName}';
+
+// For Node.js with fs module:
+const fs = require('fs');
+const FormData = require('form-data');
+const formData = new FormData();
+formData.append('file', fs.createReadStream(\`\${imagePath}/\${fileName}\`));
fetch('${baseUrl}/api/upload', {
method: 'POST',
@@ -349,13 +361,15 @@ fetch('${baseUrl}/api/upload', {
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));`,
- rest: `POST ${baseUrl}/api/upload
+ rest: `# From your images folder: /your/images/folder
+
+POST ${baseUrl}/api/upload
Headers:
X-API-Key: ${key}
Content-Type: multipart/form-data
Body (form-data):
- file: @${localPath}`,
+ file: @${fileName}`,
};
};
@@ -545,6 +559,17 @@ Body (form-data):
)}
+ {selectedFile && apiKeyValidated && !validationError && (
+
+
+
+ )}
+
{uploading ? 'Uploading...' : selectedFile ? 'Ready to upload' : 'No file selected'}
diff --git a/apps/landing/src/components/demo/CodeExamplesWidget.tsx b/apps/landing/src/components/demo/CodeExamplesWidget.tsx
index 4461a32..e55407a 100644
--- a/apps/landing/src/components/demo/CodeExamplesWidget.tsx
+++ b/apps/landing/src/components/demo/CodeExamplesWidget.tsx
@@ -2,6 +2,25 @@
import { useState } from 'react';
+/**
+ * Production-ready code examples widget.
+ *
+ * Core Principle: All code snippets are designed to be COPY-PASTE-AND-RUN ready.
+ * Users should be able to copy the code, replace the placeholder folder path,
+ * and execute it immediately on their machine with minimal edits.
+ *
+ * Pattern Used:
+ * - Navigate to folder (with placeholder path: /your/images/folder)
+ * - Use actual filename from selected/uploaded file
+ * - Clear comments showing what to replace
+ *
+ * @example
+ * // User has file at: /home/user/Downloads/sunset.jpg
+ * // Code shows: cd /your/images/folder; curl ... -F "file=@sunset.jpg"
+ * // User replaces: /your/images/folder → /home/user/Downloads
+ * // Result: Works immediately after one simple edit!
+ */
+
type CodeTab = 'curl' | 'fetch' | 'rest';
interface CodeExamplesWidgetProps {
diff --git a/apps/landing/src/components/demo/SelectedFileCodePreview.tsx b/apps/landing/src/components/demo/SelectedFileCodePreview.tsx
new file mode 100644
index 0000000..38cc598
--- /dev/null
+++ b/apps/landing/src/components/demo/SelectedFileCodePreview.tsx
@@ -0,0 +1,206 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+import { CodeExamplesWidget } from './CodeExamplesWidget';
+
+/**
+ * SelectedFileCodePreview Component
+ *
+ * Shows code snippets for uploading a selected file BEFORE upload.
+ * Helps users understand they can either:
+ * 1. Upload via UI button
+ * 2. Copy code and run programmatically
+ *
+ * Layout:
+ * - Mobile: Stacked vertically
+ * - Desktop: 3-column grid (1 col preview + 2 cols code)
+ */
+
+interface SelectedFileCodePreviewProps {
+ file: File;
+ apiKey: string;
+ apiBaseUrl: string;
+ onCopy: (text: string) => void;
+}
+
+export const SelectedFileCodePreview = ({
+ file,
+ apiKey,
+ apiBaseUrl,
+ onCopy,
+}: SelectedFileCodePreviewProps) => {
+ const [previewUrl, setPreviewUrl] = useState
('');
+
+ // Create and cleanup preview URL
+ useEffect(() => {
+ const objectUrl = URL.createObjectURL(file);
+ setPreviewUrl(objectUrl);
+
+ return () => {
+ URL.revokeObjectURL(objectUrl);
+ };
+ }, [file]);
+
+ const codeExamples = generateSelectedFileCodeExamples(file, apiKey, apiBaseUrl);
+
+ return (
+
+
+
+ Selected File - Code Preview
+
+
+
+
+ {/* Column 1: Compact File Preview Card */}
+
+
+ {/* Image Preview - Compact */}
+
+

+
+
+ {/* File Metadata */}
+
+
+ {file.name}
+
+
+
+ {/* File Size */}
+
+ {formatFileSize(file.size)}
+
+
+ {/* File Type Badge */}
+
+ {getFileType(file.name)}
+
+
+
+ {/* Status Badge */}
+
+
+
+ Ready to Upload
+
+
+
+ {/* Helper Text */}
+
+ Click upload button or copy code to run programmatically
+
+
+
+
+
+ {/* Columns 2-3: Code Examples Widget */}
+
+
+
+
+
+ );
+};
+
+/**
+ * Generate production-ready code examples for selected file upload
+ *
+ * Pattern: Users navigate to their folder, then run the upload command.
+ * This matches how real workflows work - users know where their file is.
+ */
+const generateSelectedFileCodeExamples = (
+ file: File,
+ apiKey: string,
+ apiBaseUrl: string
+) => {
+ const fileName = file.name;
+
+ return {
+ curl: `# Navigate to your images folder
+cd /your/images/folder
+
+# Upload the file
+curl -X POST "${apiBaseUrl}/api/upload" \\
+ -H "X-API-Key: ${apiKey}" \\
+ -F "file=@${fileName}"`,
+
+ fetch: `// Set your images folder path
+const imagePath = '/your/images/folder';
+const fileName = '${fileName}';
+
+// For Node.js with fs module:
+const fs = require('fs');
+const FormData = require('form-data');
+const formData = new FormData();
+formData.append('file', fs.createReadStream(\`\${imagePath}/\${fileName}\`));
+
+fetch('${apiBaseUrl}/api/upload', {
+ method: 'POST',
+ headers: {
+ 'X-API-Key': '${apiKey}'
+ },
+ body: formData
+})
+ .then(response => response.json())
+ .then(data => console.log(data))
+ .catch(error => console.error('Error:', error));`,
+
+ rest: `# From your images folder: /your/images/folder
+
+POST ${apiBaseUrl}/api/upload
+Headers:
+ X-API-Key: ${apiKey}
+ Content-Type: multipart/form-data
+
+Body (form-data):
+ file: @${fileName}`,
+ };
+};
+
+/**
+ * Format file size in human-readable format
+ */
+const formatFileSize = (bytes: number): string => {
+ if (bytes === 0) return '0 B';
+ const k = 1024;
+ const sizes = ['B', 'KB', 'MB', 'GB'];
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
+ return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
+};
+
+/**
+ * Extract file type from filename
+ */
+const getFileType = (filename: string): string => {
+ const extension = filename.split('.').pop()?.toUpperCase();
+ if (extension === 'JPG' || extension === 'JPEG') return 'JPEG';
+ if (extension === 'PNG') return 'PNG';
+ if (extension === 'WEBP') return 'WebP';
+ if (extension === 'GIF') return 'GIF';
+ return extension || 'FILE';
+};