diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b89574..0a27e20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,72 @@ +# [1.0.0-beta.21](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.20...v1.0.0-beta.21) (2026-02-25) + + +### Bug Fixes + +* update title in readme and ui ([54f633e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/54f633e6fc2a6e800979c9962383c8f83523f79b)) + +# [1.0.0-beta.20](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.19...v1.0.0-beta.20) (2026-02-23) + + +### Bug Fixes + +* Merge pull request [#9](https://github.com/cloudinary-devs/create-cloudinary-react/issues/9) from jlooper-cloudinary/main ([9696686](https://github.com/cloudinary-devs/create-cloudinary-react/commit/96966864d65168b0210a8200f26c9d0d144b6fa9)) + +# [1.0.0-beta.19](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.18...v1.0.0-beta.19) (2026-02-18) + + +### Bug Fixes + +* update readme ([b7a77ca](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b7a77caaf4bf052c68bd912a0ae9c51d424d07cd)) + +# [1.0.0-beta.18](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.17...v1.0.0-beta.18) (2026-02-18) + + +### Bug Fixes + +* update registration link ([2a69d7a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/2a69d7ada9de1cb2751bbd4c1d344422b4c2dd5e)) +* update video player example ([9db39ee](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9db39ee8fc149c748ea422544a79b45a60365f47)) + +# [1.0.0-beta.17](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.16...v1.0.0-beta.17) (2026-02-12) + + +### Bug Fixes + +* simplified questions into arrays ([bade600](https://github.com/cloudinary-devs/create-cloudinary-react/commit/bade600ee63de7a7c3d8eec40569f8896f96a7bd)) + + +### Features + +* prompt copied animation ([9ad35b2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9ad35b2351a69bca6d4dcd716b10a1508d5228a8)) + +# [1.0.0-beta.16](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.15...v1.0.0-beta.16) (2026-02-11) + + +### Bug Fixes + +* add copy on click ([a4ecf5d](https://github.com/cloudinary-devs/create-cloudinary-react/commit/a4ecf5d1d1478d854683a115735fa71f7fb50cea)) +* complete the sentance ([d6c68dd](https://github.com/cloudinary-devs/create-cloudinary-react/commit/d6c68ddc213fbc18771f26840f33edb805950799)) +* remove upload question ([fbf01ae](https://github.com/cloudinary-devs/create-cloudinary-react/commit/fbf01ae696e158cab48feb053a3515bb21453fdd)) + +# [1.0.0-beta.15](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.14...v1.0.0-beta.15) (2026-02-10) + + +### Bug Fixes + +* use correct CLAUDE.md convention for Claude Code ([07a8a08](https://github.com/cloudinary-devs/create-cloudinary-react/commit/07a8a085686b160387388464a722d90c09cb6805)) + + +### Features + +* **analytics:** add CLI feature detection for React SDK ([4dfe495](https://github.com/cloudinary-devs/create-cloudinary-react/commit/4dfe4957bf615e9df08afe729e4f024c02d1cc7f)) + +# [1.0.0-beta.14](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.13...v1.0.0-beta.14) (2026-02-04) + + +### Bug Fixes + +* improve upload widget reliability and add video player poster options ([ac51e42](https://github.com/cloudinary-devs/create-cloudinary-react/commit/ac51e420f1b81f2a055bb3fb7e0331841e86b37a)) + # [1.0.0-beta.13](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.12...v1.0.0-beta.13) (2026-02-03) diff --git a/README.md b/README.md index 2fc267a..dcea541 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,99 @@ # create-cloudinary-react -> **Beta Release** - This is a beta version. We welcome feedback and bug reports! - -Part of the [Cloudinary Developers](https://github.com/cloudinary-devs) organization. +[![npm version](https://img.shields.io/npm/v/create-cloudinary-react.svg?style=flat-square)](https://www.npmjs.com/package/create-cloudinary-react) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) -Scaffold a Cloudinary React + Vite + TypeScript project with interactive setup. +**The fastest way to start building with Cloudinary and React.** -## Prerequisites +Scaffold a modern, production-ready Cloudinary application with React 19, Vite 6, and TypeScript 5. Features interactive setup, automatic environment configuration, and built-in AI coding assistance. - Node.js 18+ installed - A Cloudinary account (free tier available) - - [Sign up for free](https://cloudinary.com/users/register/free) + - [Sign up for free](https://cld.media/reactregister) - Your cloud name is in your [dashboard](https://console.cloudinary.com/app/home/dashboard) -## Usage +> **Beta Release** - This is a beta version. We welcome feedback and bug reports! + +Part of the [Cloudinary Developers](https://github.com/cloudinary-devs) organization. + +![Build with Cloudinary!](https://res.cloudinary.com/cloudinary-creators-community/image/upload/c_thumb,w_200,g_face/v1771434800/Tee-Mascot-Hacktoberfest-cloudicorn_x6zvtf.png) + +## 📽️ Demo + +[![Watch the demo](https://res.cloudinary.com/drir0kpia/video/upload/so_1/reactstarterdemo.jpg)](https://res.cloudinary.com/drir0kpia/video/upload/v1771449633/reactstarterdemo.mp4) + + +## 🎬 Features + +- **🚀 Modern Stack**: React 19 + Vite 6 + TypeScript 5.7 +- **📦 Cloudinary SDKs**: Pre-configured `@cloudinary/react` +- **🤖 AI-First**: Auto-generates configuration for Cursor, GitHub Copilot, and Claude +- **🛠️ Best Practices**: ESLint 9 + TypeScript-ESLint, strict type checking +- **⚡ Interactive Setup**: Validates your cloud name and configures `.env` automatically +- **🎨 Typed Components**: Includes a fully typed Upload Widget component +- **🔌 MCP Support**: Built-in Model Context Protocol configuration for advanced AI integrations + +## 🚀 Quick Start + +Ensure you have Node.js 18+ installed. ```bash npx create-cloudinary-react ``` +*(No installation required)* + +The CLI will guide you through: +1. **Project Name**: naming your new folder +2. **Cloud Name**: entering your [Cloudinary cloud name](https://console.cloudinary.com/app/home/dashboard) +3. **Upload Preset** (Optional): handling unsigned uploads +4. **AI Assistant**: generating custom rules for your tool of choice (Cursor, VS Code, etc.) + +## 🛠️ What's Included -The CLI will prompt you for: -- Project name -- **Cloudinary cloud name** (found in your [dashboard](https://console.cloudinary.com/app/home/dashboard)) -- Unsigned upload preset (optional - required for uploads, but transformations work without it) -- AI coding assistant(s) you're using (Cursor, GitHub Copilot, Claude, etc.) -- Whether to install dependencies -- Whether to start dev server +Your new project comes with: -## Features +- **`src/`**: specialized for Cloudinary workflows +- **`src/components/UploadWidget.tsx`**: A ready-to-use, typed upload component +- **`.env`**: Pre-filled with your Cloud Name (and Upload Preset if provided) +- **`README.md`**: Custom instructions for your specific project +- **AI Configuration**: + - `.cursorrules` / `.cursor/mcp.json` (for Cursor) + - `.github/copilot-instructions.md` (for Copilot) + - `.claude` / `claude.md` (for Claude) -- ✅ Interactive setup with validation -- ✅ Pre-configured Cloudinary React SDK -- ✅ TypeScript + Vite + React 19 -- ✅ Typed Upload Widget component -- ✅ Environment variables with VITE_ prefix -- ✅ Multi-tool AI assistant support (Cursor, GitHub Copilot, Claude, and more) -- ✅ MCP configuration for Cloudinary integration -- ✅ ESLint + TypeScript configured +## 🤖 AI Assistant Support -## AI Assistant Support +We believe AI is the future of development. This starter kit doesn't just give you code; it gives your AI context. -During setup, you'll be asked which AI coding assistant(s) you're using. The CLI will generate the appropriate configuration files: +During setup, select your AI tool to generate **Context Rules**. These rules teach your AI: +- How to construct Cloudinary transformation URLs correctly +- How to use the `@cloudinary/react` SDK components +- Common pitfalls to avoid (like mixing up import paths) +- How to handle upload widget events -- ✅ **Cursor** → `.cursorrules` + `.cursor/mcp.json` (if selected) -- ✅ **GitHub Copilot** → `.github/copilot-instructions.md` -- ✅ **Claude Code (VS Code extension)** → `.claude`, `claude.md` + `.cursor/mcp.json` (if selected) -- ✅ **Generic AI tools** → `AI_INSTRUCTIONS.md`, `PROMPT.md` +**Supported Tools:** +- ✅ **Cursor** (Rules + MCP) +- ✅ **GitHub Copilot** (Instructions) +- ✅ **Claude** (Project context + MCP) +- ✅ **Generic LLMs** (System prompts provided) -**MCP Configuration**: The `.cursor/mcp.json` file is automatically generated if you select Cursor or Claude, as it works with both tools. +## 📋 Prerequisites -These rules help AI assistants understand Cloudinary React SDK patterns, common errors, and best practices. The generated app also includes an "AI Prompts" section with ready-to-use suggestions for your AI assistant. +- **Node.js 18+** +- **Cloudinary Account**: [Sign up for free](https://cloudinary.com/users/register/free) if you haven't already. -## Development +## 🤝 Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. + +1. Fork the repository +2. Create your feature branch (`git checkout -b feature/amazing-feature`) +3. Commit your changes (`git commit -m 'feat: add some amazing feature'`) +4. Push to the branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request + +## ⚙️ Development This project uses [Conventional Commits](https://www.conventionalcommits.org/) for version management and [semantic-release](https://github.com/semantic-release/semantic-release) for automated releases. @@ -79,3 +121,6 @@ Releases are triggered manually via GitHub Actions workflow. The workflow uses n - `perf`: Performance improvements - `chore`: Other changes +## 📄 License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. diff --git a/cli.js b/cli.js index dda211c..01a6c15 100755 --- a/cli.js +++ b/cli.js @@ -69,10 +69,10 @@ async function main() { } else { - console.log(chalk.cyan.bold('\n🚀 Cloudinary React + Vite\n')); - console.log(chalk.gray('💡 Need a Cloudinary account? Sign up for free: https://cloudinary.com/users/register/free\n')); + console.log(chalk.cyan.bold('\n🚀 Cloudinary React Starter Kit\n')); + console.log(chalk.gray('💡 Need a Cloudinary account? Sign up for free: https://cld.media/reactregister\n')); - answers = await inquirer.prompt([ + const questions = [ { type: 'input', name: 'projectName', @@ -101,7 +101,7 @@ async function main() { if (!input.trim()) { return chalk.yellow( 'Cloud name is required.\n' + - ' → Sign up: https://cloudinary.com/users/register/free\n' + + ' → Sign up: https://cld.media/reactregister\n' + ' → Find your cloud name: https://console.cloudinary.com/app/home/dashboard' ); } @@ -135,11 +135,13 @@ async function main() { { type: 'checkbox', name: 'aiTools', - message: 'Which AI coding assistant(s) are you using? (Select all that apply)', + message: + 'Which AI coding assistant(s) are you using? (Select all that apply)\n' + + chalk.gray(' We’ll add local instruction files so your assistant knows Cloudinary patterns.\n'), choices: [ { name: 'Cursor', value: 'cursor' }, { name: 'GitHub Copilot', value: 'copilot' }, - { name: 'Claude Code (VS Code extension)', value: 'claude' }, + { name: 'Claude Code', value: 'claude' }, { name: 'Other / Generic AI tools', value: 'generic' }, ], default: ['cursor'], @@ -157,7 +159,9 @@ async function main() { default: false, when: (answers) => answers.installDeps, }, - ]); + ]; + + answers = await inquirer.prompt(questions); } const { projectName, cloudName, uploadPreset, aiTools, installDeps, startDev } = answers; @@ -183,6 +187,9 @@ async function main() { PROJECT_NAME: projectName, CLOUD_NAME: cloudName, UPLOAD_PRESET: uploadPreset || '', + UPLOAD_PRESET_ENV_LINE: uploadPreset + ? `- \`VITE_CLOUDINARY_UPLOAD_PRESET\`: ${uploadPreset}` + : '- `VITE_CLOUDINARY_UPLOAD_PRESET`: (not set - add one for uploads)', }; // Function to copy template file @@ -247,8 +254,7 @@ async function main() { } if (aiTools.includes('claude')) { - writeFileSync(join(projectPath, '.claude'), aiRulesContent); - writeFileSync(join(projectPath, 'claude.md'), aiRulesContent); + writeFileSync(join(projectPath, 'CLAUDE.md'), aiRulesContent); } if (aiTools.includes('generic')) { @@ -256,18 +262,21 @@ async function main() { writeFileSync(join(projectPath, 'PROMPT.md'), aiRulesContent); } - // Generate MCP configuration if using Cursor or Claude (MCP works with both) - if (aiTools.includes('cursor') || aiTools.includes('claude')) { - const mcpTemplatePath = join(TEMPLATES_DIR, '.cursor/mcp.json.template'); - if (existsSync(mcpTemplatePath)) { + // Generate MCP configuration: Cursor uses .cursor/mcp.json, Claude Code uses .mcp.json in project root + const mcpTemplatePath = join(TEMPLATES_DIR, '.cursor/mcp.json.template'); + if (existsSync(mcpTemplatePath)) { + const mcpContent = replaceTemplate( + readFileSync(mcpTemplatePath, 'utf-8'), + templateVars + ); + if (aiTools.includes('cursor')) { const cursorDir = join(projectPath, '.cursor'); mkdirSync(cursorDir, { recursive: true }); - const mcpContent = replaceTemplate( - readFileSync(mcpTemplatePath, 'utf-8'), - templateVars - ); writeFileSync(join(cursorDir, 'mcp.json'), mcpContent); } + if (aiTools.includes('claude')) { + writeFileSync(join(projectPath, '.mcp.json'), mcpContent); + } } } @@ -279,6 +288,17 @@ async function main() { console.log(chalk.green('✅ Project created successfully!\n')); + if (aiTools && aiTools.length > 0) { + console.log(chalk.cyan('📋 AI assistant files created:')); + if (aiTools.includes('cursor')) console.log(chalk.gray(' • Cursor: .cursorrules')); + if (aiTools.includes('copilot')) console.log(chalk.gray(' • GitHub Copilot: .github/copilot-instructions.md')); + if (aiTools.includes('claude')) console.log(chalk.gray(' • Claude: CLAUDE.md')); + if (aiTools.includes('generic')) console.log(chalk.gray(' • Generic: AI_INSTRUCTIONS.md, PROMPT.md')); + if (aiTools.includes('cursor')) console.log(chalk.gray(' • MCP (Cursor): .cursor/mcp.json')); + if (aiTools.includes('claude')) console.log(chalk.gray(' • MCP (Claude Code): .mcp.json')); + console.log(''); + } + if (!answers.hasUploadPreset) { console.log(chalk.yellow('\n📝 Note: Upload preset not configured')); console.log(chalk.gray(' • Transformations will work with sample images')); @@ -287,7 +307,8 @@ async function main() { console.log(chalk.cyan(' 1. Go to https://console.cloudinary.com/app/settings/upload/presets')); console.log(chalk.cyan(' 2. Click "Add upload preset"')); console.log(chalk.cyan(' 3. Set it to "Unsigned" mode')); - console.log(chalk.cyan(' 4. Add the preset name to your .env file\n')); + console.log(chalk.cyan(' 4. Add the preset name to your .env file')); + console.log(chalk.cyan(' 5. Save the file and restart the dev server so it loads correctly\n')); } if (installDeps) { diff --git a/package-lock.json b/package-lock.json index 1d72ba9..c8d64fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "create-cloudinary-react", - "version": "1.0.0-beta.12", + "version": "1.0.0-beta.15", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "create-cloudinary-react", - "version": "1.0.0-beta.12", + "version": "1.0.0-beta.15", "license": "MIT", "dependencies": { "chalk": "^5.3.0", diff --git a/package.json b/package.json index 59bff43..6718323 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "create-cloudinary-react", - "version": "1.0.0-beta.13", + "version": "1.0.0-beta.21", "description": "Scaffold a Cloudinary React + Vite + TypeScript project with interactive setup", "type": "module", "bin": { diff --git a/templates/.cursorrules.template b/templates/.cursorrules.template index 2680dd0..c95f311 100644 --- a/templates/.cursorrules.template +++ b/templates/.cursorrules.template @@ -30,7 +30,7 @@ If the user is **not** using the create-cloudinary-react CLI and only has these Create a `.env` file in the project root with **Vite prefix** (required for client access): - `VITE_CLOUDINARY_CLOUD_NAME=your_cloud_name` (required) - `VITE_CLOUDINARY_UPLOAD_PRESET=your_unsigned_preset_name` (optional; required for unsigned upload widget) -- Restart the dev server after adding or changing `.env`. Use `import.meta.env.VITE_*` in code, not `process.env`. +- Save the `.env` file after editing it, then restart the dev server so changes load correctly. Use `import.meta.env.VITE_*` in code, not `process.env`. **2. Reusable Cloudinary instance (config)** Create a config file (e.g. `src/cloudinary/config.ts`) so the rest of the app can use a single `cld` instance: @@ -420,7 +420,8 @@ Use when the user asks for a **video player** (styled UI, controls, playlists). **Rule: imperative element only.** Do **not** pass a React-managed `