use-vibes 0.2.3 → 0.2.5

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.
package/package.json CHANGED
@@ -1,21 +1,12 @@
1
1
  {
2
2
  "name": "use-vibes",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "type": "module",
5
5
  "description": "Transform any DOM element into an AI-powered micro-app",
6
- "main": "./dist/index.js",
7
- "module": "./dist/index.js",
8
- "types": "./dist/index.d.ts",
9
- "exports": {
10
- ".": {
11
- "types": "./dist/index.d.ts",
12
- "import": "./dist/index.js",
13
- "require": "./dist/index.js",
14
- "default": "./dist/index.js"
15
- },
16
- "./dist/*": "./dist/*",
17
- "./package.json": "./package.json"
18
- },
6
+ "main": "dist/index.js",
7
+ "browser": "dist/index.js",
8
+ "module": "dist/index.js",
9
+ "types": "dist/index.d.ts",
19
10
  "keywords": [
20
11
  "ai",
21
12
  "dom",
@@ -28,11 +19,7 @@
28
19
  "author": "",
29
20
  "license": "MIT",
30
21
  "files": [
31
- "dist/",
32
- "lib/",
33
- "src/",
34
- "LICENSE",
35
- "README.md"
22
+ "dist"
36
23
  ],
37
24
  "devDependencies": {
38
25
  "@playwright/test": "^1.41.2",
@@ -1,242 +0,0 @@
1
- import React, { useEffect, useState, useRef } from 'react';
2
- import { imageGen } from 'call-ai';
3
- import type { ImageGenOptions, ImageResponse } from 'call-ai';
4
-
5
- /**
6
- * Hash function to create a key from the prompt string
7
- * @param prompt The prompt string to hash
8
- * @returns A string hash of the prompt
9
- */
10
- function hashPrompt(prompt: string): string {
11
- let hash = 0;
12
- for (let i = 0; i < prompt.length; i++) {
13
- const char = prompt.charCodeAt(i);
14
- hash = (hash << 5) - hash + char;
15
- hash = hash & hash; // Convert to 32bit integer
16
- }
17
- return hash.toString(16);
18
- }
19
-
20
- interface CacheImplementation {
21
- get: (key: string) => ImageResponse | null;
22
- set: (key: string, value: ImageResponse) => void;
23
- }
24
-
25
- // Default cache implementation using localStorage
26
- const defaultCacheImpl: CacheImplementation = {
27
- get: (key: string): ImageResponse | null => {
28
- try {
29
- const data = localStorage.getItem(`imggen-${key}`);
30
- return data ? JSON.parse(data) : null;
31
- } catch (e) {
32
- console.error('Error retrieving from ImgGen cache', e);
33
- return null;
34
- }
35
- },
36
- set: (key: string, value: ImageResponse): void => {
37
- try {
38
- localStorage.setItem(`imggen-${key}`, JSON.stringify(value));
39
- } catch (e) {
40
- console.error('Error storing in ImgGen cache', e);
41
- }
42
- },
43
- };
44
-
45
- export interface ImgGenProps {
46
- /** Text prompt for image generation (required) */
47
- prompt: string;
48
-
49
- /** Options for image generation (optional) */
50
- options?: ImageGenOptions;
51
-
52
- /** Callback to retrieve cached data before load (optional) */
53
- beforeLoad?: (key: string) => ImageResponse | null | Promise<ImageResponse | null>;
54
-
55
- /** Callback when image data is loaded (optional) */
56
- onLoad?: (response: ImageResponse) => void;
57
-
58
- /** CSS class name for the image element (optional) */
59
- className?: string;
60
-
61
- /** Alt text for the image (defaults to prompt) */
62
- alt?: string;
63
- }
64
-
65
- /**
66
- * React component for generating images with call-ai's imageGen
67
- * Provides automatic caching, reactive updates, and placeholder handling
68
- */
69
- export const ImgGen: React.FC<ImgGenProps> = ({
70
- prompt,
71
- options = {},
72
- beforeLoad,
73
- onLoad,
74
- className = '',
75
- alt,
76
- }) => {
77
- const [imageData, setImageData] = useState<string | null>(null);
78
- const [loading, setLoading] = useState<boolean>(false);
79
- const [progress, setProgress] = useState<number>(0);
80
- const [error, setError] = useState<Error | null>(null);
81
- const progressTimerRef = useRef<NodeJS.Timeout | null>(null);
82
- const promptKey = hashPrompt(prompt);
83
-
84
- const size = options?.size || '1024x1024';
85
- const [width, height] = size.split('x').map(Number);
86
-
87
- // Reset state when prompt or options change
88
- useEffect(() => {
89
- setImageData(null);
90
- setError(null);
91
- setProgress(0);
92
-
93
- // Clear any existing progress timer
94
- if (progressTimerRef.current) {
95
- clearInterval(progressTimerRef.current);
96
- progressTimerRef.current = null;
97
- }
98
-
99
- // Set up progress timer simulation (45 seconds to completion)
100
- // This is just for visual feedback and doesn't reflect actual progress
101
- const timer = setInterval(() => {
102
- setProgress((prev: number) => {
103
- const next = prev + (100 - prev) * 0.05;
104
- return next > 99 ? 99 : next;
105
- });
106
- }, 1000);
107
- progressTimerRef.current = timer;
108
-
109
- // Cleanup on unmount or when dependencies change
110
- return () => {
111
- if (progressTimerRef.current) {
112
- clearInterval(progressTimerRef.current);
113
- }
114
- };
115
- }, [prompt, JSON.stringify(options)]);
116
-
117
- // Generate the image when prompt or options change
118
- useEffect(() => {
119
- let isMounted = true;
120
-
121
- // Don't generate image if prompt is falsy
122
- if (!prompt) {
123
- setLoading(false);
124
- return;
125
- }
126
-
127
- const generateImage = async (): Promise<void> => {
128
- try {
129
- setLoading(true);
130
-
131
- // Try to get from cache via the beforeLoad callback if provided
132
- let data: ImageResponse | null = null;
133
-
134
- if (beforeLoad) {
135
- data = await beforeLoad(promptKey);
136
- } else {
137
- // Use default cache implementation
138
- data = defaultCacheImpl.get(promptKey);
139
- }
140
-
141
- // If no data in cache, generate new image
142
- if (!data) {
143
- // Use the actual imageGen function from call-ai
144
- data = await imageGen(prompt, options);
145
-
146
- // Cache the result using default implementation
147
- // if beforeLoad wasn't provided
148
- if (!beforeLoad) {
149
- defaultCacheImpl.set(promptKey, data);
150
- }
151
- }
152
-
153
- // Update state with the image data
154
- if (isMounted && data) {
155
- setImageData(data.data[0].b64_json);
156
- setProgress(100);
157
-
158
- // Clear progress timer
159
- if (progressTimerRef.current) {
160
- clearInterval(progressTimerRef.current);
161
- progressTimerRef.current = null;
162
- }
163
-
164
- // Call onLoad callback if provided
165
- if (onLoad) {
166
- onLoad(data);
167
- }
168
- }
169
- } catch (err) {
170
- if (isMounted) {
171
- setError(err instanceof Error ? err : new Error(String(err)));
172
- }
173
- } finally {
174
- if (isMounted) {
175
- setLoading(false);
176
- }
177
- }
178
- };
179
-
180
- generateImage();
181
-
182
- return () => {
183
- isMounted = false;
184
- };
185
- }, [prompt, JSON.stringify(options), promptKey, beforeLoad, onLoad]);
186
-
187
- // Render placeholder while loading
188
- if (loading || !imageData) {
189
- return (
190
- <div
191
- className={`img-gen-placeholder ${className}`}
192
- style={{
193
- width: `${width}px`,
194
- height: `${height}px`,
195
- backgroundColor: '#f0f0f0',
196
- position: 'relative',
197
- overflow: 'hidden',
198
- display: 'flex',
199
- alignItems: 'center',
200
- justifyContent: 'center',
201
- }}
202
- >
203
- <div
204
- style={{
205
- position: 'absolute',
206
- bottom: 0,
207
- left: 0,
208
- height: '4px',
209
- width: `${progress}%`,
210
- backgroundColor: '#0066cc',
211
- transition: 'width 0.3s ease-in-out',
212
- }}
213
- />
214
- <div style={{ textAlign: 'center', padding: '10px' }}>
215
- {error ? (
216
- <div className="img-gen-error">
217
- <p>Error: {error.message}</p>
218
- </div>
219
- ) : !prompt ? (
220
- <div>Waiting for prompt</div>
221
- ) : (
222
- <div>Generating image...</div>
223
- )}
224
- </div>
225
- </div>
226
- );
227
- }
228
-
229
- // Render the generated image
230
- return (
231
- <img
232
- src={`data:image/png;base64,${imageData}`}
233
- className={`img-gen ${className}`}
234
- alt={alt || prompt}
235
- width={width}
236
- height={height}
237
- style={{ maxWidth: '100%' }}
238
- />
239
- );
240
- };
241
-
242
- export default ImgGen;
package/src/index.ts DELETED
@@ -1,9 +0,0 @@
1
- // Re-export everything from call-ai
2
- export * from 'call-ai';
3
-
4
- // Export ImgGen component
5
- export { default as ImgGen } from './components/ImgGen';
6
- export type { ImgGenProps } from './components/ImgGen';
7
-
8
- // Export useVibes and its types
9
- export { useVibes, type UseVibesConfig, type VibesApp } from './useVibes';
package/src/useVibes.ts DELETED
@@ -1,116 +0,0 @@
1
- import { callAI } from 'call-ai';
2
-
3
- // DOM element and configuration interface
4
- export interface UseVibesConfig {
5
- prompt: string;
6
- // Add more configuration options as needed
7
- }
8
-
9
- // App instance interface returned by useVibes
10
- export interface VibesApp {
11
- container: HTMLElement;
12
- database?: Record<string, unknown>;
13
- }
14
-
15
- /**
16
- * The useVibes function - transforms a DOM element into an AI-augmented micro-app
17
- * @param target - CSS selector string or HTMLElement to inject into
18
- * @param config - Configuration object with prompt
19
- * @returns Promise resolving to the app instance
20
- */
21
- export function useVibes(target: string | HTMLElement, config: UseVibesConfig): Promise<VibesApp> {
22
- // Get the target element if string selector was provided
23
- const targetElement =
24
- typeof target === 'string' ? (document.querySelector(target) as HTMLElement) : target;
25
-
26
- // Validate the target element
27
- if (!targetElement) {
28
- return Promise.reject(new Error(`Target element not found: ${target}`));
29
- }
30
-
31
- try {
32
- // Capture the current HTML state
33
- const htmlContext = document.body.innerHTML;
34
-
35
- // Build the prompt for the AI
36
- const userPrompt = `
37
- Transform the HTML content based on this request: ${config.prompt}
38
-
39
- The current HTML of the page is:
40
- \`\`\`html
41
- ${htmlContext}
42
- \`\`\`
43
-
44
- Generate HTML content that should be placed inside the target element.
45
- Keep your response focused and concise, generating only the HTML required.
46
- `;
47
-
48
- // Define a simple schema for the response
49
- const schema = {
50
- properties: {
51
- html: {
52
- type: 'string',
53
- description: 'The HTML content to inject into the target element',
54
- },
55
- explanation: {
56
- type: 'string',
57
- description: 'A brief explanation of the changes made (optional)',
58
- },
59
- },
60
- };
61
-
62
- // Call the AI with the prompt and schema
63
- // Explicitly set stream to false to ensure we get a string response
64
- const aiResponse = callAI(userPrompt, { schema, stream: false });
65
-
66
- // We need to handle the response which is a Promise<string> since we set stream: false
67
- if (aiResponse instanceof Promise) {
68
- return aiResponse
69
- .then(response => {
70
- try {
71
- // Parse the JSON response
72
- const result = JSON.parse(response as string) as { html: string; explanation?: string };
73
-
74
- // Extract HTML from structured response and inject it into the target element
75
- targetElement.innerHTML = result.html;
76
-
77
- // Log explanation if provided
78
- if (result.explanation) {
79
- // eslint-disable-next-line no-console
80
- console.log('AI explanation:', result.explanation);
81
- }
82
-
83
- // Return the app instance
84
- return {
85
- container: targetElement,
86
- database: undefined,
87
- };
88
- } catch (parseError: unknown) {
89
- console.error('Error parsing AI response:', parseError);
90
- const errorMessage =
91
- parseError instanceof Error ? parseError.message : String(parseError);
92
- return Promise.reject(new Error(`Failed to parse AI response: ${errorMessage}`));
93
- }
94
- })
95
- .catch((error: unknown) => {
96
- console.error('Error calling AI:', error);
97
- const errorMessage = error instanceof Error ? error.message : String(error);
98
- return Promise.reject(new Error(`Failed to process prompt: ${errorMessage}`));
99
- });
100
- } else {
101
- // This should never happen with stream: false, but we need to handle it for type safety
102
- return Promise.reject(new Error('Unexpected streaming response from callAI'));
103
- }
104
- } catch (error) {
105
- // Fallback for any unexpected errors
106
- console.error('Error initializing AI call:', error);
107
-
108
- // Provide a simple fallback that shows the prompt was received
109
- targetElement.innerHTML = `<div>🎭 Vibes received prompt: "${config.prompt}" (AI processing failed)</div>`;
110
-
111
- return Promise.resolve({
112
- container: targetElement,
113
- database: undefined,
114
- });
115
- }
116
- }