modifywithai 1.7.0 → 1.7.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.
- package/AGENTS.md +180 -23
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -37,7 +37,8 @@ import { MWAIHead, MWAIProvider, MWAIComponents } from "modifywithai/react"
|
|
|
37
37
|
Then modify the layout to include:
|
|
38
38
|
|
|
39
39
|
1. `<MWAIHead />` inside the `<head>` tag
|
|
40
|
-
2. `<MWAIProvider>`
|
|
40
|
+
2. `<MWAIProvider>` wrapping `<body>`
|
|
41
|
+
3. `<MWAIComponents />` at the end of `<body>`, after `{children}`
|
|
41
42
|
|
|
42
43
|
Example structure:
|
|
43
44
|
|
|
@@ -48,12 +49,12 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|
|
48
49
|
<head>
|
|
49
50
|
<MWAIHead />
|
|
50
51
|
</head>
|
|
51
|
-
<body>
|
|
52
|
-
{children}
|
|
53
52
|
<MWAIProvider id="{{MWAI_ID}}">
|
|
54
|
-
<
|
|
53
|
+
<body>
|
|
54
|
+
{children}
|
|
55
|
+
<MWAIComponents />
|
|
56
|
+
</body>
|
|
55
57
|
</MWAIProvider>
|
|
56
|
-
</body>
|
|
57
58
|
</html>
|
|
58
59
|
)
|
|
59
60
|
}
|
|
@@ -141,23 +142,87 @@ Choose a suitable location based on the app's structure. Common placements inclu
|
|
|
141
142
|
|
|
142
143
|
### Example Implementation
|
|
143
144
|
|
|
144
|
-
Create a client component for the button:
|
|
145
|
+
Create a client component for the button. The modal has two views:
|
|
146
|
+
|
|
147
|
+
1. **Default view**: A textarea for submitting new modifications, submit/cancel buttons, and a list showing up to 3 recent modifications (sorted by enabled first, then by creation date). If there are more than 3 modifications, a "Show all" button appears.
|
|
148
|
+
|
|
149
|
+
2. **Show all view**: When "Show all" is clicked, the modal switches to display a search bar at the top and a scrollable list of all modifications. The textarea and submit/cancel buttons are hidden in this view.
|
|
150
|
+
|
|
151
|
+
Each modification item should display a **status indicator dot** based on the `status` field:
|
|
152
|
+
- 🟢 **Green dot**: `status === "success"` - modification was applied successfully
|
|
153
|
+
- 🟡 **Yellow dot**: `status === "pending"` - modification is being processed
|
|
154
|
+
- 🔴 **Red dot**: `status === "error"` - modification failed to apply
|
|
145
155
|
|
|
146
156
|
```tsx
|
|
147
157
|
"use client"
|
|
148
158
|
|
|
149
|
-
import { useState } from "react"
|
|
150
|
-
import { useModify } from "modifywithai/react"
|
|
159
|
+
import { useState, useMemo } from "react"
|
|
160
|
+
import { useModify, useList, useEnable, useDisable } from "modifywithai/react"
|
|
161
|
+
|
|
162
|
+
// Status indicator dot component
|
|
163
|
+
function StatusDot({ status }: { status: "success" | "pending" | "error" }) {
|
|
164
|
+
const colors = {
|
|
165
|
+
success: "#22c55e", // green
|
|
166
|
+
pending: "#eab308", // yellow
|
|
167
|
+
error: "#ef4444", // red
|
|
168
|
+
}
|
|
169
|
+
return (
|
|
170
|
+
<span
|
|
171
|
+
style={{
|
|
172
|
+
display: "inline-block",
|
|
173
|
+
width: 8,
|
|
174
|
+
height: 8,
|
|
175
|
+
borderRadius: "50%",
|
|
176
|
+
backgroundColor: colors[status],
|
|
177
|
+
marginRight: 8,
|
|
178
|
+
}}
|
|
179
|
+
title={status}
|
|
180
|
+
/>
|
|
181
|
+
)
|
|
182
|
+
}
|
|
151
183
|
|
|
152
184
|
export function ModifyWithAIButton() {
|
|
153
185
|
const [isOpen, setIsOpen] = useState(false)
|
|
154
186
|
const [prompt, setPrompt] = useState("")
|
|
155
|
-
const
|
|
187
|
+
const [showAll, setShowAll] = useState(false)
|
|
188
|
+
const [searchQuery, setSearchQuery] = useState("")
|
|
189
|
+
|
|
190
|
+
const { modify, isPending } = useModify()
|
|
191
|
+
const { data: modifications = [], isLoading: isLoadingList } = useList()
|
|
192
|
+
const { enable } = useEnable()
|
|
193
|
+
const { disable } = useDisable()
|
|
194
|
+
|
|
195
|
+
// Sort modifications: enabled first, then by createdAt (newest first)
|
|
196
|
+
const sortedModifications = useMemo(() => {
|
|
197
|
+
return [...modifications].sort((a, b) => {
|
|
198
|
+
// Enabled items first
|
|
199
|
+
if (a.enabled && !b.enabled) return -1
|
|
200
|
+
if (!a.enabled && b.enabled) return 1
|
|
201
|
+
// Then by createdAt descending (newest first)
|
|
202
|
+
return b.createdAt - a.createdAt
|
|
203
|
+
})
|
|
204
|
+
}, [modifications])
|
|
205
|
+
|
|
206
|
+
// Filter modifications by search query (searches through titles)
|
|
207
|
+
const filteredModifications = useMemo(() => {
|
|
208
|
+
if (!searchQuery.trim()) return sortedModifications
|
|
209
|
+
const query = searchQuery.toLowerCase()
|
|
210
|
+
return sortedModifications.filter((mod) =>
|
|
211
|
+
mod.title?.toLowerCase().includes(query)
|
|
212
|
+
)
|
|
213
|
+
}, [sortedModifications, searchQuery])
|
|
214
|
+
|
|
215
|
+
// Show only first 3 in default view
|
|
216
|
+
const displayedModifications = showAll
|
|
217
|
+
? filteredModifications
|
|
218
|
+
: sortedModifications.slice(0, 3)
|
|
219
|
+
|
|
220
|
+
const hasMoreThanThree = sortedModifications.length > 3
|
|
156
221
|
|
|
157
222
|
const handleSubmit = () => {
|
|
158
223
|
if (!prompt.trim()) return
|
|
159
224
|
|
|
160
|
-
|
|
225
|
+
modify(prompt, {
|
|
161
226
|
onSuccess: () => {
|
|
162
227
|
setPrompt("")
|
|
163
228
|
setIsOpen(false)
|
|
@@ -170,6 +235,26 @@ export function ModifyWithAIButton() {
|
|
|
170
235
|
})
|
|
171
236
|
}
|
|
172
237
|
|
|
238
|
+
const handleToggleModification = (mod: typeof modifications[0]) => {
|
|
239
|
+
if (mod.enabled) {
|
|
240
|
+
disable(mod.id)
|
|
241
|
+
} else {
|
|
242
|
+
enable(mod.id)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const handleClose = () => {
|
|
247
|
+
setIsOpen(false)
|
|
248
|
+
setShowAll(false)
|
|
249
|
+
setSearchQuery("")
|
|
250
|
+
setPrompt("")
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const handleBackToDefault = () => {
|
|
254
|
+
setShowAll(false)
|
|
255
|
+
setSearchQuery("")
|
|
256
|
+
}
|
|
257
|
+
|
|
173
258
|
if (!isOpen) {
|
|
174
259
|
return (
|
|
175
260
|
<button onClick={() => setIsOpen(true)}>
|
|
@@ -179,24 +264,96 @@ export function ModifyWithAIButton() {
|
|
|
179
264
|
}
|
|
180
265
|
|
|
181
266
|
return (
|
|
182
|
-
<div>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
267
|
+
<div className="modal">
|
|
268
|
+
{showAll ? (
|
|
269
|
+
// "Show all" view: search bar + scrollable list
|
|
270
|
+
<>
|
|
271
|
+
<div className="modal-header">
|
|
272
|
+
<button onClick={handleBackToDefault}>← Back</button>
|
|
273
|
+
<h3>All Modifications</h3>
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<input
|
|
277
|
+
type="text"
|
|
278
|
+
value={searchQuery}
|
|
279
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
280
|
+
placeholder="Search modifications..."
|
|
281
|
+
/>
|
|
282
|
+
|
|
283
|
+
<div className="modifications-list scrollable">
|
|
284
|
+
{isLoadingList ? (
|
|
285
|
+
<p>Loading...</p>
|
|
286
|
+
) : filteredModifications.length === 0 ? (
|
|
287
|
+
<p>No modifications found</p>
|
|
288
|
+
) : (
|
|
289
|
+
filteredModifications.map((mod) => (
|
|
290
|
+
<div key={mod.id} className="modification-item">
|
|
291
|
+
<StatusDot status={mod.status} />
|
|
292
|
+
<span>{mod.title || "Untitled modification"}</span>
|
|
293
|
+
<button onClick={() => handleToggleModification(mod)}>
|
|
294
|
+
{mod.enabled ? "Disable" : "Enable"}
|
|
295
|
+
</button>
|
|
296
|
+
</div>
|
|
297
|
+
))
|
|
298
|
+
)}
|
|
299
|
+
</div>
|
|
300
|
+
</>
|
|
301
|
+
) : (
|
|
302
|
+
// Default view: textarea + buttons + limited modifications list
|
|
303
|
+
<>
|
|
304
|
+
<textarea
|
|
305
|
+
value={prompt}
|
|
306
|
+
onChange={(e) => setPrompt(e.target.value)}
|
|
307
|
+
placeholder="Describe the changes you want to make..."
|
|
308
|
+
disabled={isPending}
|
|
309
|
+
/>
|
|
310
|
+
|
|
311
|
+
<div className="button-group">
|
|
312
|
+
<button onClick={handleSubmit} disabled={isPending || !prompt.trim()}>
|
|
313
|
+
{isPending ? "Submitting..." : "Submit"}
|
|
314
|
+
</button>
|
|
315
|
+
<button onClick={handleClose} disabled={isPending}>
|
|
316
|
+
Cancel
|
|
317
|
+
</button>
|
|
318
|
+
</div>
|
|
319
|
+
|
|
320
|
+
{/* Modifications list (up to 3) */}
|
|
321
|
+
{sortedModifications.length > 0 && (
|
|
322
|
+
<div className="modifications-section">
|
|
323
|
+
<h4>Recent Modifications</h4>
|
|
324
|
+
<div className="modifications-list">
|
|
325
|
+
{displayedModifications.map((mod) => (
|
|
326
|
+
<div key={mod.id} className="modification-item">
|
|
327
|
+
<StatusDot status={mod.status} />
|
|
328
|
+
<span>{mod.title || "Untitled modification"}</span>
|
|
329
|
+
<button onClick={() => handleToggleModification(mod)}>
|
|
330
|
+
{mod.enabled ? "Disable" : "Enable"}
|
|
331
|
+
</button>
|
|
332
|
+
</div>
|
|
333
|
+
))}
|
|
334
|
+
</div>
|
|
335
|
+
|
|
336
|
+
{hasMoreThanThree && (
|
|
337
|
+
<button onClick={() => setShowAll(true)}>
|
|
338
|
+
Show all ({sortedModifications.length})
|
|
339
|
+
</button>
|
|
340
|
+
)}
|
|
341
|
+
</div>
|
|
342
|
+
)}
|
|
343
|
+
</>
|
|
344
|
+
)}
|
|
195
345
|
</div>
|
|
196
346
|
)
|
|
197
347
|
}
|
|
198
348
|
```
|
|
199
349
|
|
|
350
|
+
### Modal Views Summary
|
|
351
|
+
|
|
352
|
+
| View | Components Displayed |
|
|
353
|
+
|------|---------------------|
|
|
354
|
+
| **Default** | Textarea, Submit button, Cancel button, Up to 3 modifications, "Show all" button (if >3 mods) |
|
|
355
|
+
| **Show all** | Back button, Search bar, Scrollable list of all modifications |
|
|
356
|
+
|
|
200
357
|
### Styling Requirements
|
|
201
358
|
|
|
202
359
|
**Important:** The example above is a minimal reference implementation. You MUST:
|