modifywithai 1.2.4 → 1.3.0

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/AGENTS.md CHANGED
@@ -128,6 +128,170 @@ Run the following command to add the API key to `.env.local`:
128
128
  echo "MWAI_API_KEY={{API_KEY}}" >> .env.local
129
129
  ```
130
130
 
131
+ ## Step 5: Add "Modify With AI" Button
132
+
133
+ Create a "Modify With AI" button in an appropriate location in the application. This button allows end-users to submit modification requests.
134
+
135
+ Choose a suitable location based on the app's structure. Common placements include:
136
+ - Header/navbar (recommended for most apps)
137
+ - Settings page
138
+ - Admin dashboard
139
+ - Floating action button
140
+ - Footer
141
+
142
+ ### Example Implementation
143
+
144
+ Create a client component for the button:
145
+
146
+ ```tsx
147
+ "use client"
148
+
149
+ import { useState } from "react"
150
+ import { modifyWithAI } from "modifywithai"
151
+
152
+ export function ModifyWithAIButton() {
153
+ const [isOpen, setIsOpen] = useState(false)
154
+ const [prompt, setPrompt] = useState("")
155
+ const [isLoading, setIsLoading] = useState(false)
156
+
157
+ const handleSubmit = async () => {
158
+ if (!prompt.trim()) return
159
+
160
+ setIsLoading(true)
161
+ const { success, error } = await modifyWithAI(prompt)
162
+ setIsLoading(false)
163
+
164
+ if (success) {
165
+ setPrompt("")
166
+ setIsOpen(false)
167
+ // Optionally show a success toast/notification
168
+ } else {
169
+ // Optionally show an error toast/notification
170
+ console.error(error)
171
+ }
172
+ }
173
+
174
+ if (!isOpen) {
175
+ return (
176
+ <button onClick={() => setIsOpen(true)}>
177
+ Modify With AI
178
+ </button>
179
+ )
180
+ }
181
+
182
+ return (
183
+ <div>
184
+ <textarea
185
+ value={prompt}
186
+ onChange={(e) => setPrompt(e.target.value)}
187
+ placeholder="Describe the changes you want to make..."
188
+ disabled={isLoading}
189
+ />
190
+ <button onClick={handleSubmit} disabled={isLoading || !prompt.trim()}>
191
+ {isLoading ? "Submitting..." : "Submit"}
192
+ </button>
193
+ <button onClick={() => setIsOpen(false)} disabled={isLoading}>
194
+ Cancel
195
+ </button>
196
+ </div>
197
+ )
198
+ }
199
+ ```
200
+
201
+ ### Styling Requirements
202
+
203
+ **Important:** The example above is a minimal reference implementation. You MUST:
204
+
205
+ 1. **Match existing button styles** - Look at other buttons in the location you're placing this (e.g., the header/navbar) and match their styling exactly (size, colors, border-radius, font, hover states, etc.)
206
+ 2. **Use the project's UI library** - If the project uses shadcn/ui, Radix, Chakra, MUI, Ant Design, or any other component library, use their Button, Dialog/Modal, Popover, Input/Textarea, and other relevant components
207
+ 3. **Follow the design system** - Use the project's existing CSS variables, Tailwind classes, or styled-components patterns
208
+
209
+ The button should look like it belongs in the app, not like a foreign element.
210
+
211
+ **After completing all setup steps, ask the user:** "I've placed the 'Modify With AI' button in [location]. Would you like me to move it somewhere else?"
212
+
213
+ ## Step 6: Add `data-mwai-id` Attributes (VERY IMPORTANT)
214
+
215
+ **This step is critical for ModifyWithAI to function properly.**
216
+
217
+ ModifyWithAI uses `data-mwai-id` attributes to identify which elements in the UI can be modified. Without these attributes, the AI cannot target specific elements for modification.
218
+
219
+ ### How to Add IDs
220
+
221
+ Add a `data-mwai-id` attribute to **every single element** in the codebase that represents a meaningful UI component or section. The ID should be descriptive and based on the element's purpose/context.
222
+
223
+ ```tsx
224
+ // Examples:
225
+ <header data-mwai-id="main-header">...</header>
226
+ <nav data-mwai-id="primary-navigation">...</nav>
227
+ <section data-mwai-id="hero-section">...</section>
228
+ <div data-mwai-id="pricing-card-pro">...</div>
229
+ <button data-mwai-id="submit-contact-form">Submit</button>
230
+ <h1 data-mwai-id="page-title">Welcome</h1>
231
+ <p data-mwai-id="feature-description">...</p>
232
+ <img data-mwai-id="team-member-photo-jane" src="..." />
233
+ <footer data-mwai-id="site-footer">...</footer>
234
+ ```
235
+
236
+ ### Guidelines for Naming IDs
237
+
238
+ 1. **Be descriptive**: Use names that clearly describe what the element is or does
239
+ 2. **Use kebab-case**: Separate words with hyphens (e.g., `hero-section`, `contact-form`)
240
+ 3. **Include context**: If there are multiple similar elements, include distinguishing context (e.g., `pricing-card-basic`, `pricing-card-pro`, `pricing-card-enterprise`)
241
+ 4. **Cover all elements**: Add IDs to headings, paragraphs, buttons, images, sections, cards, forms, inputs, navigation items, etc.
242
+
243
+ ### What to Cover
244
+
245
+ Go through **every page and component** in the project and add `data-mwai-id` to:
246
+
247
+ - Layout components (headers, footers, sidebars, navigation)
248
+ - Page sections (hero, features, testimonials, pricing, FAQ, CTA)
249
+ - Interactive elements (buttons, links, form inputs)
250
+ - Content elements (headings, paragraphs, images, icons)
251
+ - Cards and list items
252
+ - Modal/dialog content
253
+ - Any other visible UI element
254
+
255
+ **The more elements you tag, the more precisely ModifyWithAI can make targeted modifications.**
256
+
257
+ ### Persist the Rule for Future Development
258
+
259
+ To ensure all future elements created by AI assistants also include `data-mwai-id` attributes, add instructions to the appropriate file based on the IDE being used:
260
+
261
+ **If using Claude Code IDE**, append to `CLAUDE.md` (create if it doesn't exist):
262
+
263
+ ```bash
264
+ echo '
265
+ ## ModifyWithAI Integration
266
+ When creating or modifying React/JSX elements, ALWAYS add a `data-mwai-id` attribute with a descriptive kebab-case identifier. This is required for ModifyWithAI to identify and modify elements. Example: `<section data-mwai-id="hero-section">...</section>`
267
+ ' >> CLAUDE.md
268
+ ```
269
+
270
+ **If using Cursor IDE**, create a rule file at `.cursor/rules/mwai.mdc`:
271
+
272
+ ```bash
273
+ mkdir -p .cursor/rules && cat > .cursor/rules/mwai.mdc << 'EOF'
274
+ ---
275
+ description: ModifyWithAI element identification
276
+ globs: ["**/*.tsx", "**/*.jsx"]
277
+ alwaysApply: true
278
+ ---
279
+
280
+ When creating or modifying React/JSX elements, ALWAYS add a `data-mwai-id` attribute with a descriptive kebab-case identifier. This is required for ModifyWithAI to identify and modify elements.
281
+
282
+ Example: `<section data-mwai-id="hero-section">...</section>`
283
+ EOF
284
+ ```
285
+
286
+ **For other IDEs or general use**, append to `AGENTS.md`:
287
+
288
+ ```bash
289
+ echo '
290
+ ## ModifyWithAI Element IDs
291
+ When creating or modifying React/JSX elements, ALWAYS add a `data-mwai-id` attribute with a descriptive kebab-case identifier. This is required for ModifyWithAI to identify and modify elements. Example: `<section data-mwai-id="hero-section">...</section>`
292
+ ' >> AGENTS.md
293
+ ```
294
+
131
295
  ## Verification Checklist
132
296
 
133
297
  After completing the setup, verify:
@@ -139,4 +303,8 @@ After completing the setup, verify:
139
303
  - [ ] `app/api/mwai/api/[path]/route.ts` exists and exports HTTP method handlers
140
304
  - [ ] `app/api/mwai/shims/[path]/route.ts` exists and re-exports from `"modifywithai/nextjs/shims"`
141
305
  - [ ] `MWAI_API_KEY` is set in environment variables
306
+ - [ ] "Modify With AI" button component is created and placed in an appropriate location
307
+ - [ ] `data-mwai-id` attributes are added to all meaningful UI elements across the codebase
308
+ - [ ] AI assistant instructions are configured to add `data-mwai-id` to new elements (CLAUDE.md, .cursor/rules, or AGENTS.md)
309
+ - [ ] User has been asked if they want the "Modify With AI" button moved to a different location
142
310
 
package/dist/index.d.ts CHANGED
@@ -17,44 +17,26 @@ declare const MWAIProvider: ({
17
17
  children: ReactNode;
18
18
  }) => react_jsx_runtime0.JSX.Element;
19
19
  //#endregion
20
- //#region src/popover.d.ts
21
- type ModificationPopoverProps = {
22
- /** Text for the trigger button (defaults to "Modify With AI") */
23
- children?: ReactNode;
20
+ //#region src/hook.d.ts
21
+ declare const useMWAIComponents: () => react_jsx_runtime0.JSX.Element | null;
22
+ declare const MWAIComponents: () => react_jsx_runtime0.JSX.Element | null;
23
+ //#endregion
24
+ //#region src/modify.d.ts
25
+ type ModifyWithAIOptions = {
24
26
  /** Called when modification is submitted successfully */
25
27
  onSuccess?: () => void;
26
28
  /** Called when modification submission fails */
27
29
  onError?: (error: Error) => void;
28
- /** Custom title for the popover */
29
- title?: string;
30
- /** Custom description for the popover */
31
- description?: string;
32
- /** Custom placeholder for the textarea */
33
- placeholder?: string;
34
- /** Custom submit button text */
35
- submitText?: string;
36
- /** Custom loading text */
37
- loadingText?: string;
38
- /** Popover side positioning */
39
- side?: "top" | "right" | "bottom" | "left";
40
- /** Popover alignment */
41
- align?: "start" | "center" | "end";
42
30
  };
43
- declare function MWAIButton({
44
- children,
45
- onSuccess,
46
- onError,
47
- title,
48
- description,
49
- placeholder,
50
- submitText,
51
- loadingText,
52
- side,
53
- align
54
- }: ModificationPopoverProps): react_jsx_runtime0.JSX.Element;
55
- //#endregion
56
- //#region src/hook.d.ts
57
- declare const useMWAIComponents: () => react_jsx_runtime0.JSX.Element | null;
58
- declare const MWAIComponents: () => react_jsx_runtime0.JSX.Element | null;
31
+ /**
32
+ * Submit a modification request to ModifyWithAI
33
+ * @param prompt - The modification prompt describing the changes to make
34
+ * @param options - Optional callbacks for success and error handling
35
+ * @returns Promise that resolves when the modification is submitted
36
+ */
37
+ declare function modifyWithAI(prompt: string, options?: ModifyWithAIOptions): Promise<{
38
+ success: boolean;
39
+ error?: Error;
40
+ }>;
59
41
  //#endregion
60
- export { MWAIButton, MWAIComponents, MWAIHead, MWAIProvider, useMWAIComponents };
42
+ export { MWAIComponents, MWAIHead, MWAIProvider, type ModifyWithAIOptions, modifyWithAI, useMWAIComponents };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use client";import*as e from"react/jsx-runtime";import{Fragment as t,jsx as n,jsxs as r}from"react/jsx-runtime";import*as i from"react";import{createContext as a,useContext as o,useLayoutEffect as s,useState as c}from"react";import{Popover as l}from"@base-ui/react";import{createFetch as u,createSchema as d}from"@better-fetch/fetch";import{z as f}from"zod";import{logger as p}from"@better-fetch/logger";import*as m from"react-dom";import*as h from"react-dom/client";const g=({basePath:e=`/api/mwai`}={})=>n(`script`,{type:`importmap`,dangerouslySetInnerHTML:{__html:JSON.stringify({imports:{react:`${e}/shims/react-shim.js`,"react/jsx-runtime":`${e}/shims/react-jsx-shim.js`,"react-dom":`${e}/shims/react-dom-shim.js`,"react-dom/client":`${e}/shims/react-dom-client-shim.js`}})}}),_=a(null),v=({id:e,children:t})=>n(_.Provider,{value:e,children:t}),y=u({schema:d({"/api/mwai/api/list":{output:f.array(f.object({id:f.string(),title:f.string(),description:f.string().nullable(),status:f.enum([`pending`,`success`,`error`]),createdAt:f.number(),updatedAt:f.number(),enabled:f.boolean()}))},"/api/mwai/api/modify":{input:f.object({prompt:f.string()}),output:f.object({success:f.boolean()})}}),retry:{type:`exponential`,attempts:5,baseDelay:1e3,maxDelay:1e4},plugins:[p({enabled:!1})]}),b={triggerButton:{display:`inline-flex`,alignItems:`center`,justifyContent:`center`,gap:`6px`,height:`32px`,padding:`0 10px`,fontSize:`14px`,fontWeight:500,fontFamily:`system-ui, -apple-system, sans-serif`,borderRadius:`8px`,border:`1px solid #e5e5e5`,backgroundColor:`#fff`,color:`#171717`,cursor:`pointer`,transition:`all 150ms`,outline:`none`,userSelect:`none`,whiteSpace:`nowrap`},popoverContent:{display:`flex`,flexDirection:`column`,gap:`10px`,width:`320px`,padding:`12px`,backgroundColor:`#fff`,borderRadius:`10px`,boxShadow:`0 10px 38px -10px rgba(22, 23, 24, 0.35), 0 10px 20px -15px rgba(22, 23, 24, 0.2)`,border:`1px solid #e5e5e5`,fontSize:`14px`,fontFamily:`system-ui, -apple-system, sans-serif`,color:`#171717`,zIndex:50},header:{display:`flex`,flexDirection:`column`,gap:`2px`},title:{fontWeight:600,fontSize:`14px`,color:`#171717`,margin:0},description:{fontSize:`13px`,color:`#737373`,margin:0},textarea:{width:`100%`,minHeight:`96px`,padding:`10px`,fontSize:`14px`,fontFamily:`system-ui, -apple-system, sans-serif`,borderRadius:`8px`,borderWidth:`1px`,borderStyle:`solid`,borderColor:`#e5e5e5`,backgroundColor:`#fff`,color:`#171717`,resize:`none`,outline:`none`,transition:`border-color 150ms, box-shadow 150ms`,boxShadow:`none`},textareaFocus:{borderColor:`#a3a3a3`,boxShadow:`0 0 0 3px rgba(163, 163, 163, 0.2)`},buttonGroup:{display:`flex`,justifyContent:`flex-end`,gap:`8px`},cancelButton:{display:`inline-flex`,alignItems:`center`,justifyContent:`center`,height:`28px`,padding:`0 10px`,fontSize:`13px`,fontWeight:500,fontFamily:`system-ui, -apple-system, sans-serif`,borderRadius:`6px`,border:`1px solid #e5e5e5`,backgroundColor:`#fff`,color:`#171717`,cursor:`pointer`,transition:`all 150ms`,outline:`none`},submitButton:{display:`inline-flex`,alignItems:`center`,justifyContent:`center`,height:`28px`,padding:`0 10px`,fontSize:`13px`,fontWeight:500,fontFamily:`system-ui, -apple-system, sans-serif`,borderRadius:`6px`,border:`1px solid transparent`,backgroundColor:`#6366f1`,color:`#fff`,cursor:`pointer`,transition:`all 150ms`,outline:`none`},submitButtonDisabled:{opacity:.5,cursor:`not-allowed`}};function x({children:e=`Modify With AI`,onSuccess:t,onError:i,title:a=`Modify with AI`,description:o=`Describe the changes you want to make`,placeholder:s=`E.g., Make the header sticky, change the button color to blue...`,submitText:u=`Submit`,loadingText:d=`Submitting...`,side:f=`bottom`,align:p=`center`}){let[m,h]=c(``),[g,_]=c(!1),[v,x]=c(!1),[S,C]=c(!1),[w,T]=c(!1),[E,D]=c(!1),[O,k]=c(!1),A=async()=>{if(!(!m.trim()||g)){_(!0);try{let{error:e}=await y(`/api/mwai/api/modify`,{method:`POST`,body:{prompt:m.trim()}});if(e)throw Error(e.message||`Request failed`);h(``),x(!1),t?.()}catch(e){let t=e instanceof Error?e:Error(`Unknown error`);console.error(`Modification failed:`,t),i?.(t)}finally{_(!1)}}},j=e=>{e.key===`Enter`&&(e.metaKey||e.ctrlKey)&&(e.preventDefault(),A())},M=!m.trim()||g;return r(l.Root,{open:v,onOpenChange:x,children:[n(l.Trigger,{style:{...b.triggerButton,...w?{backgroundColor:`#f5f5f5`}:{}},onMouseEnter:()=>T(!0),onMouseLeave:()=>T(!1),children:e}),n(l.Portal,{children:n(l.Positioner,{align:p,side:f,sideOffset:4,style:{zIndex:50},children:r(l.Popup,{style:b.popoverContent,children:[r(`div`,{style:b.header,children:[n(l.Title,{style:b.title,children:a}),n(l.Description,{style:b.description,children:o})]}),n(`textarea`,{placeholder:s,value:m,onChange:e=>h(e.target.value),onKeyDown:j,onFocus:()=>C(!0),onBlur:()=>C(!1),disabled:g,style:{...b.textarea,...S?b.textareaFocus:{}}}),r(`div`,{style:b.buttonGroup,children:[n(`button`,{type:`button`,onClick:()=>x(!1),disabled:g,onMouseEnter:()=>D(!0),onMouseLeave:()=>D(!1),style:{...b.cancelButton,...E?{backgroundColor:`#f5f5f5`}:{}},children:`Cancel`}),n(`button`,{type:`button`,onClick:A,disabled:M,onMouseEnter:()=>k(!0),onMouseLeave:()=>k(!1),style:{...b.submitButton,...M?b.submitButtonDisabled:{},...O&&!M?{backgroundColor:`#4f46e5`}:{}},children:g?d:u})]})]})})})]})}const S=()=>{let r=o(_),[a,l]=c(null);return s(()=>{if(!r){console.error(`ModifyWithAI: No id provided. Wrap your app with <ModifyWithAIProvider id='your-id'>`);return}typeof window<`u`&&(window.__REACT__=i,window.__REACT_JSX_RUNTIME__=e,window.__REACT_DOM__=m,window.__REACT_DOM_CLIENT__=h);let t=Function(`url`,`return import(url)`);y(`/api/mwai/api/list`).then(({data:e,error:n})=>{if(n){console.error(`Failed to fetch component list:`,n);return}console.log(`data`,e);let i=e.filter(e=>e.enabled&&e.status===`success`).map(e=>e.id);console.log(`componentsToLoad`,i),i.forEach(e=>{t(`https://bucket.modifywithai.com/${r}/${e}.js`).then(e=>l(t=>[...t||[],e.default])).catch(e=>console.error(`Failed to load external module:`,e))})})},[r]),a?n(t,{children:a.map(e=>n(e,{},e.name))}):null},C=()=>S();export{x as MWAIButton,C as MWAIComponents,g as MWAIHead,v as MWAIProvider,S as useMWAIComponents};
1
+ "use client";import*as e from"react/jsx-runtime";import{Fragment as t,jsx as n}from"react/jsx-runtime";import*as r from"react";import{createContext as i,useContext as a,useLayoutEffect as o,useState as s}from"react";import*as c from"react-dom";import*as l from"react-dom/client";import{createFetch as u,createSchema as d}from"@better-fetch/fetch";import{z as f}from"zod";import{logger as p}from"@better-fetch/logger";const m=({basePath:e=`/api/mwai`}={})=>n(`script`,{type:`importmap`,dangerouslySetInnerHTML:{__html:JSON.stringify({imports:{react:`${e}/shims/react-shim.js`,"react/jsx-runtime":`${e}/shims/react-jsx-shim.js`,"react-dom":`${e}/shims/react-dom-shim.js`,"react-dom/client":`${e}/shims/react-dom-client-shim.js`}})}}),h=i(null),g=({id:e,children:t})=>n(h.Provider,{value:e,children:t}),_=u({schema:d({"/api/mwai/api/list":{output:f.array(f.object({id:f.string(),title:f.string(),description:f.string().nullable(),status:f.enum([`pending`,`success`,`error`]),createdAt:f.number(),updatedAt:f.number(),enabled:f.boolean()}))},"/api/mwai/api/modify":{input:f.object({prompt:f.string()}),output:f.object({success:f.boolean()})}}),retry:{type:`exponential`,attempts:5,baseDelay:1e3,maxDelay:1e4},plugins:[p({enabled:!1})]}),v=()=>{let i=a(h),[u,d]=s(null);return o(()=>{if(!i){console.error(`ModifyWithAI: No id provided. Wrap your app with <ModifyWithAIProvider id='your-id'>`);return}typeof window<`u`&&(window.__REACT__=r,window.__REACT_JSX_RUNTIME__=e,window.__REACT_DOM__=c,window.__REACT_DOM_CLIENT__=l);let t=Function(`url`,`return import(url)`);_(`/api/mwai/api/list`).then(({data:e,error:n})=>{if(n){console.error(`Failed to fetch component list:`,n);return}console.log(`data`,e);let r=e.filter(e=>e.enabled&&e.status===`success`).map(e=>e.id);console.log(`componentsToLoad`,r),r.forEach(e=>{t(`https://bucket.modifywithai.com/${i}/${e}.js`).then(e=>d(t=>[...t||[],e.default])).catch(e=>console.error(`Failed to load external module:`,e))})})},[i]),u?n(t,{children:u.map(e=>n(e,{},e.name))}):null},y=()=>v();async function b(e,t){let n=e.trim();if(!n){let e=Error(`Prompt cannot be empty`);return t?.onError?.(e),{success:!1,error:e}}try{let{error:e}=await _(`/api/mwai/api/modify`,{method:`POST`,body:{prompt:n}});if(e){let n=Error(e.message||`Request failed`);return t?.onError?.(n),{success:!1,error:n}}return t?.onSuccess?.(),{success:!0}}catch(e){let n=e instanceof Error?e:Error(`Unknown error`);return console.error(`Modification failed:`,n),t?.onError?.(n),{success:!1,error:n}}}export{y as MWAIComponents,m as MWAIHead,g as MWAIProvider,b as modifyWithAI,v as useMWAIComponents};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modifywithai",
3
- "version": "1.2.4",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -32,7 +32,6 @@
32
32
  "next": "^16.0.8"
33
33
  },
34
34
  "dependencies": {
35
- "@base-ui/react": "^1.0.0",
36
35
  "@better-fetch/fetch": "^1.1.21",
37
36
  "@better-fetch/logger": "^1.1.21",
38
37
  "zod": "^4.1.13"