diff --git a/PACKAGE_IMPORT_EXPORT.md b/PACKAGE_IMPORT_EXPORT.md new file mode 100644 index 000000000..0e3e6d268 --- /dev/null +++ b/PACKAGE_IMPORT_EXPORT.md @@ -0,0 +1,264 @@ +# Package Import/Export Guide + +## Overview + +The MetaBuilder Package Import/Export system allows you to package and share complete applications, features, or database snapshots as ZIP files. This enables modular development and easy distribution of pre-built functionality. + +## Features + +### Export Capabilities + +1. **Custom Package Export** + - Create reusable packages with selected components + - Include or exclude specific data types + - Add metadata (name, version, author, description, tags) + - Automatic README generation + +2. **Database Snapshot Export** + - Complete backup of entire database + - One-click export with timestamp + - Includes all schemas, pages, workflows, scripts, and configurations + +3. **Selective Export Options** + - ✅ Data schemas + - ✅ Page configurations + - ✅ Workflows + - ✅ Lua scripts + - ✅ Component hierarchies + - ✅ Component configurations + - ✅ CSS classes + - ✅ Dropdown configurations + - ✅ Seed data + - ✅ Assets (images, videos, audio, documents) + +### Import Capabilities + +1. **Package Installation** + - Import packages from ZIP files + - Automatic validation of package structure + - Merge with existing data + - Asset restoration + +2. **Safety Features** + - Package validation before import + - Non-destructive merging (adds to existing data) + - Import warnings and confirmations + +## ZIP Package Structure + +``` +package-name-1.0.0.zip +├── manifest.json # Package metadata +├── content.json # Database content +├── README.md # Auto-generated documentation +└── assets/ # Asset files + ├── asset-manifest.json + ├── images/ + │ └── *.png, *.jpg, *.svg + ├── videos/ + │ └── *.mp4, *.webm + ├── audios/ + │ └── *.mp3, *.wav + └── documents/ + └── *.pdf, *.txt +``` + +### manifest.json + +```json +{ + "id": "pkg_1234567890", + "name": "My Package", + "version": "1.0.0", + "description": "Package description", + "author": "Your Name", + "category": "social", + "icon": "📦", + "screenshots": [], + "tags": ["tag1", "tag2"], + "dependencies": [], + "createdAt": 1234567890, + "updatedAt": 1234567890, + "downloadCount": 0, + "rating": 0, + "installed": false +} +``` + +### content.json + +```json +{ + "schemas": [...], + "pages": [...], + "workflows": [...], + "luaScripts": [...], + "componentHierarchy": {...}, + "componentConfigs": {...}, + "cssClasses": [...], + "dropdownConfigs": [...], + "seedData": {...} +} +``` + +## Usage + +### Exporting a Package + +1. Navigate to **Level 4 (God Panel)** or **Level 5 (Super God Panel)** +2. Open **Package Manager** +3. Click **Export** button +4. Choose export type: + - **Custom Package**: Configure metadata and select what to include + - **Full Snapshot**: Export everything instantly +5. For custom packages: + - Fill in package name (required) + - Add version, author, description + - Add tags for searchability + - Select export options (checkboxes) +6. Click **Export Package** +7. ZIP file will download automatically + +### Importing a Package + +1. Navigate to **Level 4 (God Panel)** or **Level 5 (Super God Panel)** +2. Open **Package Manager** +3. Click **Import** button +4. Click the upload area or drag a ZIP file +5. Package will be validated and imported +6. Success message shows what was imported +7. Refresh the page if needed to see new content + +## Pre-Built Packages + +The system comes with several pre-built packages in the Package Catalog: + +### 1. **Classic Forum** 💬 +- Discussion threads and categories +- User profiles and moderation +- Schema: ForumCategory, ForumThread, ForumPost + +### 2. **Retro Guestbook** 📖 +- 90s-style visitor messages +- Custom backgrounds and GIFs +- Schema: GuestbookEntry + +### 3. **Video Platform** 🎥 +- Video upload and streaming +- Comments, likes, subscriptions +- Schema: Video, VideoComment, Subscription, Playlist + +### 4. **Music Streaming Platform** 🎵 +- Artists, albums, tracks +- Playlists and playback +- Schema: Artist, Album, Track, MusicPlaylist + +### 5. **Retro Games Arcade** 🕹️ +- Game collection with high scores +- Leaderboards and achievements +- Schema: Game, HighScore, Achievement, UserAchievement + +### 6. **E-Commerce Store** 🛒 +- Product catalog and inventory +- Shopping cart and orders +- Schema: Product, Cart, Order + +## Best Practices + +### For Package Authors + +1. **Descriptive Naming**: Use clear, descriptive package names +2. **Versioning**: Follow semantic versioning (major.minor.patch) +3. **Documentation**: Add comprehensive descriptions and tags +4. **Dependencies**: List any required packages +5. **Testing**: Test your package before distribution +6. **Assets**: Include all necessary assets in the package + +### For Package Users + +1. **Backup First**: Export a database snapshot before importing new packages +2. **Review Contents**: Check package contents in Package Manager before installing +3. **Test in Development**: Test new packages in a development environment first +4. **Check Conflicts**: Be aware of potential schema or page ID conflicts +5. **Documentation**: Read the package README for setup instructions + +## API Reference + +### Export Functions + +```typescript +import { exportPackageAsZip, downloadZip } from '@/lib/package-export' + +// Export a custom package +const blob = await exportPackageAsZip(manifest, content, assets, options) +downloadZip(blob, 'package-name.zip') + +// Export database snapshot +const blob = await exportDatabaseSnapshot( + schemas, + pages, + workflows, + luaScripts, + componentHierarchy, + componentConfigs, + cssClasses, + dropdownConfigs, + assets +) +``` + +### Import Functions + +```typescript +import { importPackageFromZip } from '@/lib/package-export' + +// Import from ZIP file +const { manifest, content, assets } = await importPackageFromZip(zipFile) +``` + +## Troubleshooting + +### Import Fails + +- **Invalid ZIP**: Ensure the ZIP file has the correct structure +- **Missing manifest.json**: Package must include a manifest file +- **Missing content.json**: Package must include content data +- **Corrupted File**: Try re-downloading or re-exporting the package + +### Export Fails + +- **No Package Name**: Package name is required for custom exports +- **No Data**: Ensure your database has data to export +- **Browser Memory**: Large exports may fail on low-memory devices + +### Assets Not Working + +- **Path Issues**: Asset paths are preserved from the original location +- **Missing Files**: Ensure all assets were included during export +- **Format Support**: Only specific formats are supported (see structure above) + +## Future Enhancements + +Planned features for future versions: + +- 🔄 Package versioning and updates +- 🔍 Package marketplace/registry +- 🔐 Package signing and verification +- 📦 Dependency resolution +- 🎨 Custom package icons +- 📸 Package screenshots +- 💬 Package reviews and ratings +- 🔗 Remote package installation via URL +- 📊 Package analytics + +## Support + +For issues or questions: +- Check the console for error messages +- Verify ZIP file structure +- Ensure you have the latest version of MetaBuilder +- Review this documentation for proper usage + +--- + +**Note**: The import/export system is designed to be non-destructive. Imported data is merged with existing data rather than replacing it. Always backup your database before major imports. diff --git a/PRD.md b/PRD.md index 291113f14..53a28a033 100644 --- a/PRD.md +++ b/PRD.md @@ -104,7 +104,25 @@ Elevate MetaBuilder to support multi-tenant architecture with a Super God level - Search and filter by category, rating, downloads - Seed data automatically loaded with packages -### 2. CSS Class Builder +### 8. Package Import/Export System +**Functionality:** Export database configurations and packages as ZIP files, import packages from ZIP files, including support for assets (images, videos, audio, documents) +**Purpose:** Enable sharing of complete application packages, backing up database configurations, and distributing reusable modules across MetaBuilder instances +**Trigger:** User clicks Import/Export buttons in Package Manager +**Progression:** +- **Export**: Click Export → Choose Custom Package or Full Snapshot → Enter metadata (name, version, author, description, tags) → Select export options → Click Export Package → ZIP downloads +- **Import**: Click Import → Select/drag ZIP file → Package validated → Data merged with existing database → Assets restored → Success notification +**Success Criteria:** +- Export packages as ZIP files with manifest.json, content.json, README.md, and assets folder +- Import packages from ZIP files with validation +- Selective export options (schemas, pages, workflows, Lua scripts, components, CSS, dropdowns, seed data, assets) +- Full database snapshot export for backup +- Non-destructive import (merges with existing data) +- Asset support for images, videos, audio, and documents +- Auto-generated README in packages +- Import/Export accessible from Package Manager +- Visual feedback during import/export operations + +### 9. CSS Class Builder **Functionality:** Visual selector for Tailwind CSS classes organized into logical categories **Purpose:** Eliminate the need to memorize or type CSS class names, reducing errors and speeding up styling **Trigger:** User clicks palette icon next to any className field in PropertyInspector @@ -115,7 +133,7 @@ Elevate MetaBuilder to support multi-tenant architecture with a Super God level - 200+ predefined classes organized into 10 categories - Custom class input available for edge cases -### 3. Dynamic Dropdown Configuration +### 10. Dynamic Dropdown Configuration **Functionality:** Centralized management of dropdown option sets usable across multiple components **Purpose:** Prevent duplication and ensure consistency when the same options appear in multiple places **Trigger:** User navigates to "Dropdowns" tab in god-tier panel or components reference dropdown by name @@ -126,7 +144,7 @@ Elevate MetaBuilder to support multi-tenant architecture with a Super God level - Visual GUI for managing options (no JSON required) - Pre-loaded with common examples (status, priority, category) -### 4. CSS Class Library Manager +### 11. CSS Class Library Manager **Functionality:** Manage the catalog of CSS classes available in the builder **Purpose:** Allow customization of available classes and organization for project-specific needs **Trigger:** User navigates to "CSS Classes" tab in god-tier panel @@ -137,7 +155,7 @@ Elevate MetaBuilder to support multi-tenant architecture with a Super God level - Changes immediately reflected in CSS Class Builder - System initializes with comprehensive Tailwind utilities -### 5. Monaco Code Editor Integration +### 12. Monaco Code Editor Integration **Functionality:** Professional-grade code editor for JSON and Lua with syntax highlighting and validation **Purpose:** When code editing is necessary, provide best-in-class tooling comparable to VS Code **Trigger:** User opens SchemaEditor, LuaEditor, or JsonEditor components diff --git a/package-lock.json b/package-lock.json index fd11fe7af..a3218c497 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,6 +44,7 @@ "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/vite": "^4.1.11", "@tanstack/react-query": "^5.83.1", + "@types/jszip": "^3.4.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -54,6 +55,7 @@ "fengari-web": "^0.1.4", "framer-motion": "^12.6.2", "input-otp": "^1.4.2", + "jszip": "^3.10.1", "lucide-react": "^0.484.0", "marked": "^15.0.7", "next-themes": "^0.4.6", @@ -4358,6 +4360,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/jszip": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@types/jszip/-/jszip-3.4.0.tgz", + "integrity": "sha512-GFHqtQQP3R4NNuvZH3hNCYD0NbyBZ42bkN7kO3NDrU/SnvIZWMS8Bp38XCsRKBT5BXvgm0y1zqpZWp/ZkRzBzg==", + "license": "MIT", + "dependencies": { + "jszip": "*" + } + }, "node_modules/@types/node": { "version": "22.19.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", @@ -5382,6 +5393,12 @@ "node": ">=6.6.0" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -7270,6 +7287,12 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -7446,6 +7469,12 @@ "@types/estree": "*" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -7543,6 +7572,18 @@ "dev": true, "license": "MIT" }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -7567,6 +7608,15 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lightningcss": { "version": "1.30.2", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", @@ -8259,6 +8309,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -8468,6 +8524,12 @@ "license": "MIT", "peer": true }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -8774,6 +8836,27 @@ "react-dom": ">=16.6.0" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/readline-sync": { "version": "1.4.10", "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", @@ -9139,6 +9222,12 @@ "node": ">= 18" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -9342,6 +9431,21 @@ "dev": true, "license": "MIT" }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -9863,6 +9967,12 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/uuid": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", diff --git a/package.json b/package.json index 9a680e9a9..46c57c5a6 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/vite": "^4.1.11", "@tanstack/react-query": "^5.83.1", + "@types/jszip": "^3.4.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -58,6 +59,7 @@ "fengari-web": "^0.1.4", "framer-motion": "^12.6.2", "input-otp": "^1.4.2", + "jszip": "^3.10.1", "lucide-react": "^0.484.0", "marked": "^15.0.7", "next-themes": "^0.4.6", diff --git a/src/components/PackageImportExport.tsx b/src/components/PackageImportExport.tsx new file mode 100644 index 000000000..4a9c3a80a --- /dev/null +++ b/src/components/PackageImportExport.tsx @@ -0,0 +1,638 @@ +import { useState, useRef } from 'react' +import { Button } from '@/components/ui/button' +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' +import { Label } from '@/components/ui/label' +import { Input } from '@/components/ui/input' +import { Textarea } from '@/components/ui/textarea' +import { Checkbox } from '@/components/ui/checkbox' +import { ScrollArea } from '@/components/ui/scroll-area' +import { Separator } from '@/components/ui/separator' +import { toast } from 'sonner' +import { Database } from '@/lib/database' +import { exportPackageAsZip, importPackageFromZip, downloadZip, exportDatabaseSnapshot } from '@/lib/package-export' +import type { PackageManifest, PackageContent } from '@/lib/package-types' +import type { ExportPackageOptions } from '@/lib/package-export' +import { + Export, + ArrowSquareIn, + FileArchive, + FileArrowDown, + FileArrowUp, + Package, + CloudArrowDown, + Database as DatabaseIcon, + CheckCircle, + Warning, + Image as ImageIcon, + FilmStrip, + MusicNote, + FileText +} from '@phosphor-icons/react' + +interface PackageImportExportProps { + open: boolean + onOpenChange: (open: boolean) => void + mode: 'export' | 'import' +} + +export function PackageImportExport({ open, onOpenChange, mode }: PackageImportExportProps) { + const [exporting, setExporting] = useState(false) + const [importing, setImporting] = useState(false) + const [exportOptions, setExportOptions] = useState({ + includeAssets: true, + includeSchemas: true, + includePages: true, + includeWorkflows: true, + includeLuaScripts: true, + includeComponentHierarchy: true, + includeComponentConfigs: true, + includeCssClasses: true, + includeDropdownConfigs: true, + includeSeedData: true, + }) + const [manifest, setManifest] = useState>({ + name: '', + version: '1.0.0', + description: '', + author: '', + category: 'other', + tags: [], + }) + const [tagInput, setTagInput] = useState('') + const fileInputRef = useRef(null) + + const handleExport = async () => { + if (!manifest.name) { + toast.error('Please provide a package name') + return + } + + setExporting(true) + try { + const schemas = await Database.getSchemas() + const pages = await Database.getPages() + const workflows = await Database.getWorkflows() + const luaScripts = await Database.getLuaScripts() + const componentHierarchy = await Database.getComponentHierarchy() + const componentConfigs = await Database.getComponentConfigs() + const cssClasses = await Database.getCssClasses() + const dropdownConfigs = await Database.getDropdownConfigs() + + const fullManifest: PackageManifest = { + id: `pkg_${Date.now()}`, + name: manifest.name!, + version: manifest.version || '1.0.0', + description: manifest.description || '', + author: manifest.author || 'Anonymous', + category: manifest.category as any || 'other', + icon: '📦', + screenshots: [], + tags: manifest.tags || [], + dependencies: [], + createdAt: Date.now(), + updatedAt: Date.now(), + downloadCount: 0, + rating: 0, + installed: false, + } + + const content: PackageContent = { + schemas: exportOptions.includeSchemas ? schemas : [], + pages: exportOptions.includePages ? pages : [], + workflows: exportOptions.includeWorkflows ? workflows : [], + luaScripts: exportOptions.includeLuaScripts ? luaScripts : [], + componentHierarchy: exportOptions.includeComponentHierarchy ? componentHierarchy : {}, + componentConfigs: exportOptions.includeComponentConfigs ? componentConfigs : {}, + cssClasses: exportOptions.includeCssClasses ? cssClasses : undefined, + dropdownConfigs: exportOptions.includeDropdownConfigs ? dropdownConfigs : undefined, + } + + const blob = await exportPackageAsZip(fullManifest, content, [], exportOptions) + const fileName = `${manifest.name.toLowerCase().replace(/\s+/g, '-')}-${manifest.version}.zip` + downloadZip(blob, fileName) + + toast.success('Package exported successfully!') + onOpenChange(false) + } catch (error) { + console.error('Export error:', error) + toast.error('Failed to export package') + } finally { + setExporting(false) + } + } + + const handleExportSnapshot = async () => { + setExporting(true) + try { + const schemas = await Database.getSchemas() + const pages = await Database.getPages() + const workflows = await Database.getWorkflows() + const luaScripts = await Database.getLuaScripts() + const componentHierarchy = await Database.getComponentHierarchy() + const componentConfigs = await Database.getComponentConfigs() + const cssClasses = await Database.getCssClasses() + const dropdownConfigs = await Database.getDropdownConfigs() + + const blob = await exportDatabaseSnapshot( + schemas, + pages, + workflows, + luaScripts, + componentHierarchy, + componentConfigs, + cssClasses, + dropdownConfigs + ) + + const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5) + downloadZip(blob, `database-snapshot-${timestamp}.zip`) + + toast.success('Database snapshot exported successfully!') + onOpenChange(false) + } catch (error) { + console.error('Snapshot export error:', error) + toast.error('Failed to export database snapshot') + } finally { + setExporting(false) + } + } + + const handleImport = async (file: File) => { + setImporting(true) + try { + const { manifest: importedManifest, content, assets } = await importPackageFromZip(file) + + const currentSchemas = await Database.getSchemas() + const currentPages = await Database.getPages() + const currentWorkflows = await Database.getWorkflows() + const currentLuaScripts = await Database.getLuaScripts() + const currentHierarchy = await Database.getComponentHierarchy() + const currentConfigs = await Database.getComponentConfigs() + + const newSchemas = [...currentSchemas, ...content.schemas] + const newPages = [...currentPages, ...content.pages] + const newWorkflows = [...currentWorkflows, ...content.workflows] + const newLuaScripts = [...currentLuaScripts, ...content.luaScripts] + const newHierarchy = { ...currentHierarchy, ...content.componentHierarchy } + const newConfigs = { ...currentConfigs, ...content.componentConfigs } + + await Database.setSchemas(newSchemas) + await Database.setPages(newPages) + await Database.setWorkflows(newWorkflows) + await Database.setLuaScripts(newLuaScripts) + await Database.setComponentHierarchy(newHierarchy) + await Database.setComponentConfigs(newConfigs) + + if (content.cssClasses) { + const currentCssClasses = await Database.getCssClasses() + await Database.setCssClasses([...currentCssClasses, ...content.cssClasses]) + } + + if (content.dropdownConfigs) { + const currentDropdowns = await Database.getDropdownConfigs() + await Database.setDropdownConfigs([...currentDropdowns, ...content.dropdownConfigs]) + } + + if (content.seedData) { + await Database.setPackageData(importedManifest.id, content.seedData) + } + + const installedPackage = { + packageId: importedManifest.id, + installedAt: Date.now(), + version: importedManifest.version, + enabled: true, + } + + await Database.installPackage(installedPackage) + + toast.success(`Package "${importedManifest.name}" imported successfully!`) + toast.info(`Imported: ${content.schemas.length} schemas, ${content.pages.length} pages, ${content.workflows.length} workflows, ${assets.length} assets`) + + onOpenChange(false) + } catch (error) { + console.error('Import error:', error) + toast.error(`Failed to import package: ${error instanceof Error ? error.message : 'Unknown error'}`) + } finally { + setImporting(false) + } + } + + const handleFileSelect = (e: React.ChangeEvent) => { + const file = e.target.files?.[0] + if (file) { + if (!file.name.endsWith('.zip')) { + toast.error('Please select a .zip file') + return + } + handleImport(file) + } + } + + const handleAddTag = () => { + if (tagInput.trim() && !manifest.tags?.includes(tagInput.trim())) { + setManifest(prev => ({ + ...prev, + tags: [...(prev.tags || []), tagInput.trim()] + })) + setTagInput('') + } + } + + const handleRemoveTag = (tag: string) => { + setManifest(prev => ({ + ...prev, + tags: (prev.tags || []).filter(t => t !== tag) + })) + } + + if (mode === 'import') { + return ( + + + +
+
+ +
+
+ Import Package + Import a package from a ZIP file +
+
+
+ +
+ + + Select Package File + Choose a .zip file containing a MetaBuilder package + + +
+
fileInputRef.current?.click()} + > + +

Click to select a package file

+

Supports .zip files only

+ +
+ + {importing && ( +
+
+ Importing package... +
+ )} +
+ + + + + + What's Included in Packages? + + +
+
+ + Data schemas +
+
+ + Page configurations +
+
+ + Workflows +
+
+ + Lua scripts +
+
+ + Component hierarchies +
+
+ + CSS configurations +
+
+ + Assets (images, etc.) +
+
+ + Seed data +
+
+
+
+ +
+ +
+

Import Warning

+

Imported packages will be merged with existing data. Make sure to back up your database before importing.

+
+
+
+ +
+ ) + } + + return ( + + + +
+
+ +
+
+ Export Package + Create a shareable package or database snapshot +
+
+
+ + +
+
+ + +
+
+ +
+ Custom Package +
+ Export selected data as a reusable package +
+
+ + + +
+
+ +
+ Full Snapshot +
+ Export entire database as backup +
+
+
+ + + +
+
+ + setManifest(prev => ({ ...prev, name: e.target.value }))} + /> +
+ +
+
+ + setManifest(prev => ({ ...prev, version: e.target.value }))} + /> +
+ +
+ + setManifest(prev => ({ ...prev, author: e.target.value }))} + /> +
+
+ +
+ +