create-cloudinary-react 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,94 @@
1
+ .app {
2
+ min-height: 100vh;
3
+ padding: 2rem;
4
+ max-width: 1200px;
5
+ margin: 0 auto;
6
+ }
7
+
8
+ .main-content {
9
+ display: flex;
10
+ flex-direction: column;
11
+ gap: 2rem;
12
+ align-items: center;
13
+ }
14
+
15
+ h1 {
16
+ font-size: 2.5rem;
17
+ margin: 0;
18
+ text-align: center;
19
+ }
20
+
21
+ h2 {
22
+ font-size: 1.5rem;
23
+ margin: 0 0 1rem 0;
24
+ }
25
+
26
+ .upload-section,
27
+ .image-section {
28
+ width: 100%;
29
+ display: flex;
30
+ flex-direction: column;
31
+ align-items: center;
32
+ padding: 2rem;
33
+ background: rgba(255, 255, 255, 0.05);
34
+ border-radius: 1rem;
35
+ }
36
+
37
+ .display-image {
38
+ max-width: 100%;
39
+ height: auto;
40
+ border-radius: 0.5rem;
41
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
42
+ }
43
+
44
+ .image-info {
45
+ margin-top: 1rem;
46
+ font-size: 0.875rem;
47
+ color: rgba(255, 255, 255, 0.7);
48
+ font-family: monospace;
49
+ }
50
+
51
+ .ai-prompts-section {
52
+ width: 100%;
53
+ display: flex;
54
+ flex-direction: column;
55
+ align-items: center;
56
+ padding: 2rem;
57
+ background: rgba(99, 102, 241, 0.1);
58
+ border-radius: 1rem;
59
+ border: 1px solid rgba(99, 102, 241, 0.3);
60
+ }
61
+
62
+ .prompts-intro {
63
+ margin: 0.5rem 0 1rem 0;
64
+ color: rgba(255, 255, 255, 0.8);
65
+ text-align: center;
66
+ }
67
+
68
+ .prompts-list {
69
+ list-style: none;
70
+ padding: 0;
71
+ margin: 0;
72
+ width: 100%;
73
+ max-width: 600px;
74
+ display: flex;
75
+ flex-direction: column;
76
+ gap: 0.75rem;
77
+ }
78
+
79
+ .prompts-list li {
80
+ padding: 0.75rem 1rem;
81
+ background: rgba(255, 255, 255, 0.05);
82
+ border-radius: 0.5rem;
83
+ border-left: 3px solid rgba(99, 102, 241, 0.5);
84
+ color: rgba(255, 255, 255, 0.9);
85
+ font-size: 0.9rem;
86
+ transition: all 0.2s;
87
+ cursor: pointer;
88
+ }
89
+
90
+ .prompts-list li:hover {
91
+ background: rgba(255, 255, 255, 0.1);
92
+ border-left-color: rgba(99, 102, 241, 0.8);
93
+ transform: translateX(4px);
94
+ }
@@ -0,0 +1,80 @@
1
+ import { useState } from 'react';
2
+ import { AdvancedImage, placeholder, lazyload } from '@cloudinary/react';
3
+ import { fill } from '@cloudinary/url-gen/actions/resize';
4
+ import { format, quality } from '@cloudinary/url-gen/actions/delivery';
5
+ import { auto } from '@cloudinary/url-gen/qualifiers/format';
6
+ import { auto as autoQuality } from '@cloudinary/url-gen/qualifiers/quality';
7
+ import { cld } from './cloudinary/config';
8
+ import { UploadWidget } from './cloudinary/UploadWidget';
9
+ import './App.css';
10
+
11
+ function App() {
12
+ const [uploadedImageId, setUploadedImageId] = useState<string | null>(null);
13
+
14
+ const handleUploadSuccess = (result: any) => {
15
+ console.log('Upload successful:', result);
16
+ setUploadedImageId(result.public_id);
17
+ };
18
+
19
+ const handleUploadError = (error: Error) => {
20
+ console.error('Upload error:', error);
21
+ alert(`Upload failed: ${error.message}`);
22
+ };
23
+
24
+ // Display uploaded image if available, otherwise show a sample
25
+ const imageId = uploadedImageId || 'samples/cloudinary-icon';
26
+
27
+ const displayImage = cld
28
+ .image(imageId)
29
+ .resize(fill().width(600).height(400))
30
+ .delivery(format(auto()))
31
+ .delivery(quality(autoQuality()));
32
+
33
+ return (
34
+ <div className="app">
35
+ <main className="main-content">
36
+ <h1>Cloudinary React + Vite Boilerplate</h1>
37
+ <p>This is a ready-to-use development environment with Cloudinary integration.</p>
38
+
39
+ <div className="upload-section">
40
+ <h2>Upload an Image</h2>
41
+ <UploadWidget
42
+ onUploadSuccess={handleUploadSuccess}
43
+ onUploadError={handleUploadError}
44
+ buttonText="Upload Image"
45
+ />
46
+ </div>
47
+
48
+ <div className="image-section">
49
+ <h2>Display Image</h2>
50
+ <AdvancedImage
51
+ cldImg={displayImage}
52
+ plugins={[placeholder({ mode: 'blur' }), lazyload()]}
53
+ alt={uploadedImageId ? 'Your uploaded image' : 'Sample image'}
54
+ className="display-image"
55
+ />
56
+ {uploadedImageId && (
57
+ <p className="image-info">Public ID: {uploadedImageId}</p>
58
+ )}
59
+ </div>
60
+
61
+ <div className="ai-prompts-section">
62
+ <h2>🤖 Try Asking Your AI Assistant</h2>
63
+ <p className="prompts-intro">Here are some Cloudinary-related tasks you can try:</p>
64
+ <ul className="prompts-list">
65
+ <li>"Add responsive image transformations with breakpoints"</li>
66
+ <li>"Create a video player component with Cloudinary"</li>
67
+ <li>"Add image effects like blur, grayscale, or sepia"</li>
68
+ <li>"Implement automatic image cropping with face detection"</li>
69
+ <li>"Add image overlays with text or logos"</li>
70
+ <li>"Create an image gallery with lazy loading"</li>
71
+ <li>"Add image optimization with format and quality auto"</li>
72
+ <li>"Implement image transformations based on screen size"</li>
73
+ </ul>
74
+ </div>
75
+ </main>
76
+ </div>
77
+ );
78
+ }
79
+
80
+ export default App;
@@ -0,0 +1,112 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import { uploadPreset } from './config';
3
+
4
+ interface UploadWidgetProps {
5
+ onUploadSuccess?: (result: any) => void;
6
+ onUploadError?: (error: Error) => void;
7
+ buttonText?: string;
8
+ className?: string;
9
+ }
10
+
11
+ declare global {
12
+ interface Window {
13
+ cloudinary: any;
14
+ }
15
+ }
16
+
17
+ export function UploadWidget({
18
+ onUploadSuccess,
19
+ onUploadError,
20
+ buttonText = 'Upload Image',
21
+ className = '',
22
+ }: UploadWidgetProps) {
23
+ const widgetRef = useRef<any>(null);
24
+ const buttonRef = useRef<HTMLButtonElement>(null);
25
+
26
+ useEffect(() => {
27
+ // Load Cloudinary Upload Widget script
28
+ if (!window.cloudinary) {
29
+ const script = document.createElement('script');
30
+ script.src = 'https://upload-widget.cloudinary.com/global/all.js';
31
+ script.async = true;
32
+ document.body.appendChild(script);
33
+
34
+ script.onload = () => {
35
+ initializeWidget();
36
+ };
37
+ } else {
38
+ initializeWidget();
39
+ }
40
+
41
+ function initializeWidget() {
42
+ if (!window.cloudinary || !buttonRef.current) return;
43
+
44
+ if (!uploadPreset) {
45
+ console.warn(
46
+ 'VITE_CLOUDINARY_UPLOAD_PRESET is not set. ' +
47
+ 'Create an unsigned upload preset in your Cloudinary dashboard.'
48
+ );
49
+ }
50
+
51
+ widgetRef.current = window.cloudinary.createUploadWidget(
52
+ {
53
+ cloudName: import.meta.env.VITE_CLOUDINARY_CLOUD_NAME,
54
+ uploadPreset: uploadPreset || undefined,
55
+ sources: ['local', 'camera', 'url'],
56
+ multiple: false,
57
+ },
58
+ (error: any, result: any) => {
59
+ if (error) {
60
+ console.error('Upload error:', error);
61
+ onUploadError?.(new Error(error.message || 'Upload failed'));
62
+ return;
63
+ }
64
+
65
+ if (result && result.event === 'success') {
66
+ console.log('Upload success:', result.info);
67
+ onUploadSuccess?.(result.info);
68
+ }
69
+ }
70
+ );
71
+
72
+ buttonRef.current.addEventListener('click', () => {
73
+ widgetRef.current?.open();
74
+ });
75
+ }
76
+
77
+ return () => {
78
+ if (buttonRef.current && widgetRef.current) {
79
+ buttonRef.current.removeEventListener('click', () => {
80
+ widgetRef.current?.open();
81
+ });
82
+ }
83
+ };
84
+ }, [onUploadSuccess, onUploadError]);
85
+
86
+ return (
87
+ <button
88
+ ref={buttonRef}
89
+ type="button"
90
+ className={className}
91
+ style={{
92
+ padding: '0.75rem 1.5rem',
93
+ fontSize: '1rem',
94
+ fontWeight: 500,
95
+ color: 'white',
96
+ backgroundColor: '#6366f1',
97
+ border: 'none',
98
+ borderRadius: '0.5rem',
99
+ cursor: 'pointer',
100
+ transition: 'background-color 0.2s',
101
+ }}
102
+ onMouseEnter={(e) => {
103
+ e.currentTarget.style.backgroundColor = '#4f46e5';
104
+ }}
105
+ onMouseLeave={(e) => {
106
+ e.currentTarget.style.backgroundColor = '#6366f1';
107
+ }}
108
+ >
109
+ {buttonText}
110
+ </button>
111
+ );
112
+ }
@@ -0,0 +1,21 @@
1
+ import { Cloudinary } from '@cloudinary/url-gen';
2
+
3
+ // Get environment variables - Vite requires VITE_ prefix
4
+ const cloudName = import.meta.env.VITE_CLOUDINARY_CLOUD_NAME;
5
+
6
+ if (!cloudName) {
7
+ throw new Error(
8
+ 'VITE_CLOUDINARY_CLOUD_NAME is not set. Please create a .env file with your Cloudinary cloud name.\n' +
9
+ 'See .env.example for reference.'
10
+ );
11
+ }
12
+
13
+ // Create and export Cloudinary instance
14
+ export const cld = new Cloudinary({
15
+ cloud: {
16
+ cloudName: cloudName,
17
+ },
18
+ });
19
+
20
+ // Export upload preset for convenience
21
+ export const uploadPreset = import.meta.env.VITE_CLOUDINARY_UPLOAD_PRESET || '';
@@ -0,0 +1,29 @@
1
+ :root {
2
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
3
+ line-height: 1.5;
4
+ font-weight: 400;
5
+
6
+ color-scheme: light dark;
7
+ color: rgba(255, 255, 255, 0.87);
8
+ background-color: #242424;
9
+
10
+ font-synthesis: none;
11
+ text-rendering: optimizeLegibility;
12
+ -webkit-font-smoothing: antialiased;
13
+ -moz-osx-font-smoothing: grayscale;
14
+ }
15
+
16
+ * {
17
+ box-sizing: border-box;
18
+ }
19
+
20
+ body {
21
+ margin: 0;
22
+ min-width: 320px;
23
+ min-height: 100vh;
24
+ }
25
+
26
+ #root {
27
+ width: 100%;
28
+ min-height: 100vh;
29
+ }
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import './index.css'
4
+ import App from './App.tsx'
5
+
6
+ createRoot(document.getElementById('root')!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>,
10
+ )
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4
+ "target": "ES2022",
5
+ "useDefineForClassFields": true,
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "module": "ESNext",
8
+ "types": ["vite/client"],
9
+ "skipLibCheck": true,
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "verbatimModuleSyntax": true,
13
+ "moduleDetection": "force",
14
+ "noEmit": true,
15
+ "jsx": "react-jsx",
16
+ "strict": true,
17
+ "noUnusedLocals": true,
18
+ "noUnusedParameters": true,
19
+ "erasableSyntaxOnly": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "noUncheckedSideEffectImports": true
22
+ },
23
+ "include": ["src"]
24
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ { "path": "./tsconfig.app.json" },
5
+ { "path": "./tsconfig.node.json" }
6
+ ]
7
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
+ "target": "ES2023",
5
+ "lib": ["ES2023"],
6
+ "module": "ESNext",
7
+ "types": ["node"],
8
+ "skipLibCheck": true,
9
+ "moduleResolution": "bundler",
10
+ "allowImportingTsExtensions": true,
11
+ "verbatimModuleSyntax": true,
12
+ "moduleDetection": "force",
13
+ "noEmit": true,
14
+ "strict": true,
15
+ "noUnusedLocals": true,
16
+ "noUnusedParameters": true,
17
+ "erasableSyntaxOnly": true,
18
+ "noFallthroughCasesInSwitch": true,
19
+ "noUncheckedSideEffectImports": true
20
+ },
21
+ "include": ["vite.config.ts"]
22
+ }
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+
4
+ // https://vite.dev/config/
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ })