motion-cart 0.1.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.
Files changed (3) hide show
  1. package/README.md +36 -0
  2. package/dist/index.js +1263 -0
  3. package/package.json +35 -0
package/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # motion-cart
2
+
3
+ Shop for animations and let an AI agent add them to **any** local project — React, Next, Vue, or vanilla JS.
4
+
5
+ ```bash
6
+ # point it at a project + its already-running dev server
7
+ npx motion-cart --target . --dev-url http://localhost:5173 --token <your-motion+-token>
8
+ # then open the control panel it prints (http://localhost:7100)
9
+ ```
10
+
11
+ Browse a catalogue of Motion animations, optionally click the elements on your live site to
12
+ target, and a Claude agent edits your real source while the preview hot-reloads.
13
+
14
+ ## Requirements
15
+
16
+ - A project that is a **clean git repo** (edits land on an isolated `motion-cart/session-*` branch).
17
+ - An **already-running local dev server** for that project; pass its URL via `--dev-url` (loopback only).
18
+ - The agent uses your existing **Claude Code login** (no separate API key needed). `--token`
19
+ (or `MOTION_PLUS_TOKEN`) enables the live Motion AI Kit MCP.
20
+
21
+ ## Flags
22
+
23
+ | Flag | Default | Purpose |
24
+ |---|---|---|
25
+ | `--dev-url` | (required) | URL of your running local dev server |
26
+ | `--target` | cwd | project directory the agent edits |
27
+ | `--port` | 7100 | control panel port |
28
+ | `--proxy-port` | 7101 | injecting proxy port |
29
+ | `--token` | `$MOTION_PLUS_TOKEN` | Motion+ token to enable the Motion MCP |
30
+
31
+ ## Safety
32
+
33
+ Edits are made on a `motion-cart/session-*` branch (branched from your current branch) and
34
+ auto-committed, so your base branch stays clean. Review with
35
+ `git diff <base>..motion-cart/session-*`, then merge or delete the branch. The proxy binds
36
+ loopback only and the control API requires a per-session token.
package/dist/index.js ADDED
@@ -0,0 +1,1263 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import path3 from "path";
5
+
6
+ // ../core/dist/chunk-YCZ2J2MA.js
7
+ var MENU_GROUPS = [
8
+ "Data & Numbers",
9
+ "Notifications",
10
+ "Loading States",
11
+ "Layout & Navigation",
12
+ "Lists & Tables",
13
+ "Scroll Animations",
14
+ "Gestures & Interactions",
15
+ "Text Animations",
16
+ "Micro-interactions",
17
+ "Cursor Effects",
18
+ "Motion+ Premium",
19
+ "Advanced Interactions",
20
+ "Special & Unique"
21
+ ];
22
+ var MENU = [
23
+ {
24
+ "id": "data-numbers__animated-counter",
25
+ "title": "Animated Counter",
26
+ "group": "Data & Numbers",
27
+ "sourcePath": "src/components/examples/data/AnimatedCounter.tsx"
28
+ },
29
+ {
30
+ "id": "data-numbers__revenue-card",
31
+ "title": "Revenue Card",
32
+ "group": "Data & Numbers",
33
+ "sourcePath": "src/components/examples/data/RevenueCard.tsx"
34
+ },
35
+ {
36
+ "id": "data-numbers__stats-grid",
37
+ "title": "Stats Grid",
38
+ "group": "Data & Numbers",
39
+ "sourcePath": "src/components/examples/data/StatsGrid.tsx"
40
+ },
41
+ {
42
+ "id": "data-numbers__mini-bar-chart",
43
+ "title": "Mini Bar Chart",
44
+ "group": "Data & Numbers",
45
+ "sourcePath": "src/components/examples/data/MiniChart.tsx"
46
+ },
47
+ {
48
+ "id": "data-numbers__activity-feed",
49
+ "title": "Activity Feed",
50
+ "group": "Data & Numbers",
51
+ "sourcePath": "src/components/examples/data/ActivityFeed.tsx"
52
+ },
53
+ {
54
+ "id": "data-numbers__counting-stats-row",
55
+ "title": "Counting Stats Row",
56
+ "group": "Data & Numbers",
57
+ "sourcePath": "src/components/examples/text/CountingText.tsx"
58
+ },
59
+ {
60
+ "id": "notifications__toast-stack",
61
+ "title": "Toast Stack",
62
+ "group": "Notifications",
63
+ "sourcePath": "src/components/examples/notifications/ToastStack.tsx"
64
+ },
65
+ {
66
+ "id": "notifications__notification-bell",
67
+ "title": "Notification Bell",
68
+ "group": "Notifications",
69
+ "sourcePath": "src/components/examples/notifications/NotificationBell.tsx"
70
+ },
71
+ {
72
+ "id": "notifications__notifications-panel",
73
+ "title": "Notifications Panel",
74
+ "group": "Notifications",
75
+ "sourcePath": "src/components/examples/notifications/NotificationsPanel.tsx"
76
+ },
77
+ {
78
+ "id": "loading-states__progress-bar",
79
+ "title": "Progress Bar",
80
+ "group": "Loading States",
81
+ "sourcePath": "src/components/examples/loading/ProgressBar.tsx"
82
+ },
83
+ {
84
+ "id": "loading-states__circular-progress",
85
+ "title": "Circular Progress",
86
+ "group": "Loading States",
87
+ "sourcePath": "src/components/examples/loading/CircularProgress.tsx"
88
+ },
89
+ {
90
+ "id": "loading-states__skeleton-shimmer",
91
+ "title": "Skeleton Shimmer",
92
+ "group": "Loading States",
93
+ "sourcePath": "src/components/examples/loading/SkeletonCard.tsx"
94
+ },
95
+ {
96
+ "id": "loading-states__pulsing-dots",
97
+ "title": "Pulsing Dots",
98
+ "group": "Loading States",
99
+ "sourcePath": "src/components/examples/loading/PulsingDots.tsx"
100
+ },
101
+ {
102
+ "id": "layout-navigation__animated-tabs",
103
+ "title": "Animated Tabs",
104
+ "group": "Layout & Navigation",
105
+ "sourcePath": "src/components/examples/layout/AnimatedTabs.tsx"
106
+ },
107
+ {
108
+ "id": "layout-navigation__expandable-cards",
109
+ "title": "Expandable Cards",
110
+ "group": "Layout & Navigation",
111
+ "sourcePath": "src/components/examples/layout/ExpandableCard.tsx"
112
+ },
113
+ {
114
+ "id": "layout-navigation__shared-layout-modal",
115
+ "title": "Shared Layout Modal",
116
+ "group": "Layout & Navigation",
117
+ "sourcePath": "src/components/examples/layout/SharedLayoutModal.tsx"
118
+ },
119
+ {
120
+ "id": "layout-navigation__page-transitions",
121
+ "title": "Page Transitions",
122
+ "group": "Layout & Navigation",
123
+ "sourcePath": "src/components/examples/layout/PageTransition.tsx"
124
+ },
125
+ {
126
+ "id": "layout-navigation__animated-sidebar",
127
+ "title": "Animated Sidebar",
128
+ "group": "Layout & Navigation",
129
+ "sourcePath": "src/components/examples/layout/AnimatedSidebar.tsx"
130
+ },
131
+ {
132
+ "id": "lists-tables__drag-to-reorder",
133
+ "title": "Drag to Reorder",
134
+ "group": "Lists & Tables",
135
+ "sourcePath": "src/components/examples/lists/ReorderableList.tsx"
136
+ },
137
+ {
138
+ "id": "lists-tables__staggered-list",
139
+ "title": "Staggered List",
140
+ "group": "Lists & Tables",
141
+ "sourcePath": "src/components/examples/lists/StaggeredList.tsx"
142
+ },
143
+ {
144
+ "id": "lists-tables__animated-table",
145
+ "title": "Animated Table",
146
+ "group": "Lists & Tables",
147
+ "sourcePath": "src/components/examples/lists/AnimatedTable.tsx"
148
+ },
149
+ {
150
+ "id": "scroll-animations__scroll-reveal",
151
+ "title": "Scroll Reveal",
152
+ "group": "Scroll Animations",
153
+ "sourcePath": "src/components/examples/scroll/ScrollReveal.tsx"
154
+ },
155
+ {
156
+ "id": "gestures-interactions__hover-cards",
157
+ "title": "Hover Cards",
158
+ "group": "Gestures & Interactions",
159
+ "sourcePath": "src/components/examples/gestures/HoverCards.tsx"
160
+ },
161
+ {
162
+ "id": "gestures-interactions__swipe-to-delete",
163
+ "title": "Swipe to Delete",
164
+ "group": "Gestures & Interactions",
165
+ "sourcePath": "src/components/examples/gestures/DragToDelete.tsx"
166
+ },
167
+ {
168
+ "id": "gestures-interactions__magnetic-button",
169
+ "title": "Magnetic Button",
170
+ "group": "Gestures & Interactions",
171
+ "sourcePath": "src/components/examples/gestures/MagneticButton.tsx"
172
+ },
173
+ {
174
+ "id": "text-animations__typewriter-effect",
175
+ "title": "Typewriter Effect",
176
+ "group": "Text Animations",
177
+ "sourcePath": "src/components/examples/text/TypewriterEffect.tsx"
178
+ },
179
+ {
180
+ "id": "text-animations__gradient-text",
181
+ "title": "Gradient Text",
182
+ "group": "Text Animations",
183
+ "sourcePath": "src/components/examples/text/AnimatedGradientText.tsx"
184
+ },
185
+ {
186
+ "id": "micro-interactions__toggle-switch",
187
+ "title": "Toggle Switch",
188
+ "group": "Micro-interactions",
189
+ "sourcePath": "src/components/examples/micro/ToggleSwitch.tsx"
190
+ },
191
+ {
192
+ "id": "micro-interactions__animated-checkboxes",
193
+ "title": "Animated Checkboxes",
194
+ "group": "Micro-interactions",
195
+ "sourcePath": "src/components/examples/micro/AnimatedCheckbox.tsx"
196
+ },
197
+ {
198
+ "id": "micro-interactions__floating-action-button",
199
+ "title": "Floating Action Button",
200
+ "group": "Micro-interactions",
201
+ "sourcePath": "src/components/examples/micro/FloatingActionButton.tsx"
202
+ },
203
+ {
204
+ "id": "micro-interactions__animated-badges",
205
+ "title": "Animated Badges",
206
+ "group": "Micro-interactions",
207
+ "sourcePath": "src/components/examples/micro/AnimatedBadges.tsx"
208
+ },
209
+ {
210
+ "id": "micro-interactions__command-palette",
211
+ "title": "Command Palette",
212
+ "group": "Micro-interactions",
213
+ "sourcePath": "src/components/examples/micro/CommandPalette.tsx"
214
+ },
215
+ {
216
+ "id": "micro-interactions__animated-search",
217
+ "title": "Animated Search",
218
+ "group": "Micro-interactions",
219
+ "sourcePath": "src/components/examples/micro/AnimatedSearch.tsx"
220
+ },
221
+ {
222
+ "id": "micro-interactions__confetti-burst",
223
+ "title": "Confetti Burst",
224
+ "group": "Micro-interactions",
225
+ "sourcePath": "src/components/examples/micro/Confetti.tsx"
226
+ },
227
+ {
228
+ "id": "micro-interactions__segmented-control",
229
+ "title": "Segmented Control",
230
+ "group": "Micro-interactions",
231
+ "sourcePath": "src/components/examples/micro/SegmentedControl.tsx"
232
+ },
233
+ {
234
+ "id": "micro-interactions__otp-input",
235
+ "title": "OTP Input",
236
+ "group": "Micro-interactions",
237
+ "sourcePath": "src/components/examples/micro/OTPInput.tsx"
238
+ },
239
+ {
240
+ "id": "micro-interactions__animated-accordion",
241
+ "title": "Animated Accordion",
242
+ "group": "Micro-interactions",
243
+ "sourcePath": "src/components/examples/micro/AnimatedAccordion.tsx"
244
+ },
245
+ {
246
+ "id": "cursor-effects__cursor-follower",
247
+ "title": "Cursor Follower",
248
+ "group": "Cursor Effects",
249
+ "sourcePath": "src/components/examples/special/FollowCursor.tsx"
250
+ },
251
+ {
252
+ "id": "cursor-effects__cursor-spotlight",
253
+ "title": "Cursor Spotlight",
254
+ "group": "Cursor Effects",
255
+ "sourcePath": "src/components/examples/special/CursorSpotlight.tsx"
256
+ },
257
+ {
258
+ "id": "cursor-effects__particle-trail",
259
+ "title": "Particle Trail",
260
+ "group": "Cursor Effects",
261
+ "sourcePath": "src/components/examples/special/CursorTrailParticles.tsx"
262
+ },
263
+ {
264
+ "id": "cursor-effects__click-ripples",
265
+ "title": "Click Ripples",
266
+ "group": "Cursor Effects",
267
+ "sourcePath": "src/components/examples/special/CursorRipple.tsx"
268
+ },
269
+ {
270
+ "id": "cursor-effects__magnetic-elements",
271
+ "title": "Magnetic Elements",
272
+ "group": "Cursor Effects",
273
+ "sourcePath": "src/components/examples/special/CursorMagnetic.tsx"
274
+ },
275
+ {
276
+ "id": "cursor-effects__cursor-distortion",
277
+ "title": "Cursor Distortion",
278
+ "group": "Cursor Effects",
279
+ "sourcePath": "src/components/examples/special/CursorDistortion.tsx"
280
+ },
281
+ {
282
+ "id": "motion-premium__animate-number",
283
+ "title": "Animate Number",
284
+ "group": "Motion+ Premium",
285
+ "sourcePath": "src/components/examples/motionplus/AnimateNumberDemo.tsx"
286
+ },
287
+ {
288
+ "id": "motion-premium__animate-text",
289
+ "title": "Animate Text",
290
+ "group": "Motion+ Premium",
291
+ "sourcePath": "src/components/examples/motionplus/AnimateTextDemo.tsx"
292
+ },
293
+ {
294
+ "id": "motion-premium__typewriter",
295
+ "title": "Typewriter",
296
+ "group": "Motion+ Premium",
297
+ "sourcePath": "src/components/examples/motionplus/TypewriterDemo.tsx"
298
+ },
299
+ {
300
+ "id": "motion-premium__scramble-text",
301
+ "title": "Scramble Text",
302
+ "group": "Motion+ Premium",
303
+ "sourcePath": "src/components/examples/motionplus/ScrambleTextDemo.tsx"
304
+ },
305
+ {
306
+ "id": "motion-premium__carousel",
307
+ "title": "Carousel",
308
+ "group": "Motion+ Premium",
309
+ "sourcePath": "src/components/examples/motionplus/CarouselDemo.tsx"
310
+ },
311
+ {
312
+ "id": "motion-premium__magnetic-pull",
313
+ "title": "Magnetic Pull",
314
+ "group": "Motion+ Premium",
315
+ "sourcePath": "src/components/examples/motionplus/MagneticPullDemo.tsx"
316
+ },
317
+ {
318
+ "id": "motion-premium__pointer-compass",
319
+ "title": "Pointer Compass",
320
+ "group": "Motion+ Premium",
321
+ "sourcePath": "src/components/examples/motionplus/PointerCompassDemo.tsx"
322
+ },
323
+ {
324
+ "id": "motion-premium__curtain-transitions",
325
+ "title": "Curtain Transitions",
326
+ "group": "Motion+ Premium",
327
+ "sourcePath": "src/components/examples/motionplus/CurtainsDemo.tsx"
328
+ },
329
+ {
330
+ "id": "motion-premium__magnetic-cursor-reticule",
331
+ "title": "Magnetic Cursor Reticule",
332
+ "group": "Motion+ Premium",
333
+ "sourcePath": "src/components/examples/motionplus/CursorReticuleDemo.tsx"
334
+ },
335
+ {
336
+ "id": "motion-premium__square-cursor-reticule",
337
+ "title": "Square Cursor Reticule",
338
+ "group": "Motion+ Premium",
339
+ "sourcePath": "src/components/examples/special/CursorMagneticSquare.tsx"
340
+ },
341
+ {
342
+ "id": "motion-premium__coverflow-carousel",
343
+ "title": "Coverflow Carousel",
344
+ "group": "Motion+ Premium",
345
+ "sourcePath": "src/components/examples/motionplus/CoverflowCarousel.tsx"
346
+ },
347
+ {
348
+ "id": "motion-premium__scramble-headline",
349
+ "title": "Scramble Headline",
350
+ "group": "Motion+ Premium",
351
+ "sourcePath": "src/components/examples/motionplus/ScrambleHeadline.tsx"
352
+ },
353
+ {
354
+ "id": "motion-premium__price-toggle",
355
+ "title": "Price Toggle",
356
+ "group": "Motion+ Premium",
357
+ "sourcePath": "src/components/examples/motionplus/PriceToggle.tsx"
358
+ },
359
+ {
360
+ "id": "motion-premium__ticker-marquee",
361
+ "title": "Ticker / Marquee",
362
+ "group": "Motion+ Premium",
363
+ "sourcePath": "src/components/examples/motionplus/TickerDemo.tsx"
364
+ },
365
+ {
366
+ "id": "advanced-interactions__scroll-velocity-3d-planes",
367
+ "title": "Scroll Velocity 3D Planes",
368
+ "group": "Advanced Interactions",
369
+ "sourcePath": "src/components/examples/advanced/ScrollVelocityPlanes.tsx"
370
+ },
371
+ {
372
+ "id": "advanced-interactions__magnetic-filings",
373
+ "title": "Magnetic Filings",
374
+ "group": "Advanced Interactions",
375
+ "sourcePath": "src/components/examples/advanced/MagneticFilings.tsx"
376
+ },
377
+ {
378
+ "id": "advanced-interactions__pointer-collision",
379
+ "title": "Pointer Collision",
380
+ "group": "Advanced Interactions",
381
+ "sourcePath": "src/components/examples/advanced/PointerCollision.tsx"
382
+ },
383
+ {
384
+ "id": "advanced-interactions__parallax-hover-card",
385
+ "title": "Parallax Hover Card",
386
+ "group": "Advanced Interactions",
387
+ "sourcePath": "src/components/examples/advanced/ParallaxHoverCard.tsx"
388
+ },
389
+ {
390
+ "id": "advanced-interactions__cursor-image-hover",
391
+ "title": "Cursor Image Hover",
392
+ "group": "Advanced Interactions",
393
+ "sourcePath": "src/components/examples/advanced/CursorImageHover.tsx"
394
+ },
395
+ {
396
+ "id": "advanced-interactions__ios-pointer",
397
+ "title": "iOS Pointer",
398
+ "group": "Advanced Interactions",
399
+ "sourcePath": "src/components/examples/advanced/IOSPointer.tsx"
400
+ },
401
+ {
402
+ "id": "advanced-interactions__context-menu",
403
+ "title": "Context Menu",
404
+ "group": "Advanced Interactions",
405
+ "sourcePath": "src/components/examples/advanced/ContextMenu.tsx"
406
+ },
407
+ {
408
+ "id": "advanced-interactions__swipe-actions",
409
+ "title": "Swipe Actions",
410
+ "group": "Advanced Interactions",
411
+ "sourcePath": "src/components/examples/advanced/SwipeActions.tsx"
412
+ },
413
+ {
414
+ "id": "advanced-interactions__ios-exposure-slider",
415
+ "title": "iOS Exposure Slider",
416
+ "group": "Advanced Interactions",
417
+ "sourcePath": "src/components/examples/advanced/IOSExposureSlider.tsx"
418
+ },
419
+ {
420
+ "id": "advanced-interactions__smooth-tabs",
421
+ "title": "Smooth Tabs",
422
+ "group": "Advanced Interactions",
423
+ "sourcePath": "src/components/examples/advanced/SmoothTabs.tsx"
424
+ },
425
+ {
426
+ "id": "advanced-interactions__add-to-cart",
427
+ "title": "Add to Cart",
428
+ "group": "Advanced Interactions",
429
+ "sourcePath": "src/components/examples/advanced/AddToCart.tsx"
430
+ },
431
+ {
432
+ "id": "advanced-interactions__dots-morph-button",
433
+ "title": "Dots Morph Button",
434
+ "group": "Advanced Interactions",
435
+ "sourcePath": "src/components/examples/advanced/DotsMorphButton.tsx"
436
+ },
437
+ {
438
+ "id": "special-unique__3d-coin-flip",
439
+ "title": "3D Coin Flip",
440
+ "group": "Special & Unique",
441
+ "sourcePath": "src/components/examples/special/CoinFlip3D.tsx"
442
+ },
443
+ {
444
+ "id": "special-unique__signature-draw",
445
+ "title": "Signature Draw",
446
+ "group": "Special & Unique",
447
+ "sourcePath": "src/components/examples/special/SignatureDraw.tsx"
448
+ },
449
+ {
450
+ "id": "special-unique__elastic-grid",
451
+ "title": "Elastic Grid",
452
+ "group": "Special & Unique",
453
+ "sourcePath": "src/components/examples/special/ElasticGrid.tsx"
454
+ },
455
+ {
456
+ "id": "special-unique__spotlight-card",
457
+ "title": "Spotlight Card",
458
+ "group": "Special & Unique",
459
+ "sourcePath": "src/components/examples/special/SpotlightCard.tsx"
460
+ },
461
+ {
462
+ "id": "special-unique__ripple-grid",
463
+ "title": "Ripple Grid",
464
+ "group": "Special & Unique",
465
+ "sourcePath": "src/components/examples/special/RippleGrid.tsx"
466
+ },
467
+ {
468
+ "id": "special-unique__aurora-background",
469
+ "title": "Aurora Background",
470
+ "group": "Special & Unique",
471
+ "sourcePath": "src/components/examples/special/AuroraBackground.tsx"
472
+ },
473
+ {
474
+ "id": "special-unique__morphing-blob",
475
+ "title": "Morphing Blob",
476
+ "group": "Special & Unique",
477
+ "sourcePath": "src/components/examples/special/MorphingBlob.tsx"
478
+ },
479
+ {
480
+ "id": "special-unique__3d-tilt-card",
481
+ "title": "3D Tilt Card",
482
+ "group": "Special & Unique",
483
+ "sourcePath": "src/components/examples/special/TiltCard.tsx"
484
+ },
485
+ {
486
+ "id": "special-unique__morphing-shapes",
487
+ "title": "Morphing Shapes",
488
+ "group": "Special & Unique",
489
+ "sourcePath": "src/components/examples/special/MorphingShapes.tsx"
490
+ },
491
+ {
492
+ "id": "special-unique__glowing-orbs",
493
+ "title": "Glowing Orbs",
494
+ "group": "Special & Unique",
495
+ "sourcePath": "src/components/examples/special/GlowingOrb.tsx"
496
+ },
497
+ {
498
+ "id": "special-unique__liquid-button",
499
+ "title": "Liquid Button",
500
+ "group": "Special & Unique",
501
+ "sourcePath": "src/components/examples/special/LiquidButton.tsx"
502
+ },
503
+ {
504
+ "id": "special-unique__particle-background",
505
+ "title": "Particle Background",
506
+ "group": "Special & Unique",
507
+ "sourcePath": "src/components/examples/special/ParticleBackground.tsx"
508
+ },
509
+ {
510
+ "id": "special-unique__elastic-slider",
511
+ "title": "Elastic Slider",
512
+ "group": "Special & Unique",
513
+ "sourcePath": "src/components/examples/special/ElasticSlider.tsx"
514
+ },
515
+ {
516
+ "id": "special-unique__flip-clock",
517
+ "title": "Flip Clock",
518
+ "group": "Special & Unique",
519
+ "sourcePath": "src/components/examples/special/FlipClock.tsx"
520
+ },
521
+ {
522
+ "id": "special-unique__voice-waveform",
523
+ "title": "Voice Waveform",
524
+ "group": "Special & Unique",
525
+ "sourcePath": "src/components/examples/special/VoiceWaveform.tsx"
526
+ },
527
+ {
528
+ "id": "special-unique__gravity-balls",
529
+ "title": "Gravity Balls",
530
+ "group": "Special & Unique",
531
+ "sourcePath": "src/components/examples/special/GravityBalls.tsx"
532
+ },
533
+ {
534
+ "id": "special-unique__tech-stack-orbit",
535
+ "title": "Tech Stack Orbit",
536
+ "group": "Special & Unique",
537
+ "sourcePath": "src/components/examples/special/InfiniteRotatingRing.tsx"
538
+ },
539
+ {
540
+ "id": "special-unique__split-flap-display",
541
+ "title": "Split-Flap Display",
542
+ "group": "Special & Unique",
543
+ "sourcePath": "src/components/examples/special/SplitFlapDisplay.tsx"
544
+ },
545
+ {
546
+ "id": "special-unique__radial-menu",
547
+ "title": "Radial Menu",
548
+ "group": "Special & Unique",
549
+ "sourcePath": "src/components/examples/special/RadialMenu.tsx"
550
+ },
551
+ {
552
+ "id": "special-unique__3d-card-stack",
553
+ "title": "3D Card Stack",
554
+ "group": "Special & Unique",
555
+ "sourcePath": "src/components/examples/special/StackedCards3D.tsx"
556
+ },
557
+ {
558
+ "id": "special-unique__matrix-rain",
559
+ "title": "Matrix Rain",
560
+ "group": "Special & Unique",
561
+ "sourcePath": "src/components/examples/special/MatrixRain.tsx"
562
+ },
563
+ {
564
+ "id": "special-unique__magnetic-dock",
565
+ "title": "Magnetic Dock",
566
+ "group": "Special & Unique",
567
+ "sourcePath": "src/components/examples/special/MagneticDock.tsx"
568
+ },
569
+ {
570
+ "id": "special-unique__springy-text",
571
+ "title": "Springy Text",
572
+ "group": "Special & Unique",
573
+ "sourcePath": "src/components/examples/special/SpringyText.tsx"
574
+ },
575
+ {
576
+ "id": "special-unique__heartbeat-monitor",
577
+ "title": "Heartbeat Monitor",
578
+ "group": "Special & Unique",
579
+ "sourcePath": "src/components/examples/special/HeartbeatMonitor.tsx"
580
+ },
581
+ {
582
+ "id": "special-unique__glitch-text",
583
+ "title": "Glitch Text",
584
+ "group": "Special & Unique",
585
+ "sourcePath": "src/components/examples/special/GlitchText.tsx"
586
+ },
587
+ {
588
+ "id": "special-unique__perspective-grid",
589
+ "title": "Perspective Grid",
590
+ "group": "Special & Unique",
591
+ "sourcePath": "src/components/examples/special/PerspectiveGrid.tsx"
592
+ }
593
+ ];
594
+ var MENU_BY_ID = Object.fromEntries(MENU.map((m) => [m.id, m]));
595
+
596
+ // ../core/dist/chunk-CF66VEUS.js
597
+ function overlayScript() {
598
+ return `(() => {
599
+ if (window.__mcPicker) return; window.__mcPicker = true;
600
+ var MC = "motion-cart";
601
+ var PANEL = window.__MC_PANEL_ORIGIN || "*";
602
+ var active = false, hi = null, banner = null;
603
+ function post(m){ try { (window.parent||window).postMessage(Object.assign({source:MC}, m), PANEL); } catch(e){} }
604
+ function stableClass(c){ return c && c.length <= 24 && !/[0-9a-f]{6,}|--[0-9a-f]{4,}|__/.test(c) && /^[a-z][a-z0-9-]*$/i.test(c); }
605
+ function selectorFor(el){
606
+ if (el.id) return "#" + CSS.escape(el.id);
607
+ var parts=[], node=el, depth=0;
608
+ while(node && node.nodeType===1 && depth<5){
609
+ if(node.id){ parts.unshift("#"+CSS.escape(node.id)); break; }
610
+ var part=node.tagName.toLowerCase();
611
+ var cls=Array.prototype.filter.call(node.classList, stableClass).slice(0,2);
612
+ if(cls.length) part += "." + cls.map(function(c){return CSS.escape(c);}).join(".");
613
+ var p=node.parentElement;
614
+ if(p){ var same=Array.prototype.filter.call(p.children, function(c){return c.tagName===node.tagName;});
615
+ if(same.length>1) part += ":nth-of-type(" + (same.indexOf(node)+1) + ")"; }
616
+ parts.unshift(part); node=node.parentElement; depth++;
617
+ }
618
+ return parts.join(" > ");
619
+ }
620
+ function elAt(x,y){ var l=document.elementsFromPoint(x,y); for(var i=0;i<l.length;i++){ if(!l[i].hasAttribute || !l[i].hasAttribute("data-mc-overlay")) return l[i]; } return null; }
621
+ function ensureHi(){
622
+ if(hi) return;
623
+ hi=document.createElement("div"); hi.setAttribute("data-mc-overlay","");
624
+ hi.style.cssText="position:fixed;z-index:2147483646;border:2px solid #22d3ee;background:rgba(34,211,238,.12);border-radius:4px;pointer-events:none;display:none";
625
+ document.body.appendChild(hi);
626
+ banner=document.createElement("div"); banner.setAttribute("data-mc-overlay","");
627
+ banner.textContent="Pick mode \u2014 click an element to target it";
628
+ banner.style.cssText="position:fixed;top:10px;left:50%;transform:translateX(-50%);z-index:2147483647;background:rgba(8,9,12,.92);color:#67e8f9;font:12px system-ui;padding:6px 12px;border-radius:999px;border:1px solid rgba(103,232,249,.35);pointer-events:none;display:none";
629
+ document.body.appendChild(banner);
630
+ }
631
+ function onMove(e){ if(!active) return; var el=elAt(e.clientX,e.clientY); if(!el){ hi.style.display="none"; return; } var r=el.getBoundingClientRect(); hi.style.display="block"; hi.style.left=r.x+"px"; hi.style.top=r.y+"px"; hi.style.width=r.width+"px"; hi.style.height=r.height+"px"; }
632
+ function onClick(e){ if(!active) return; var el=elAt(e.clientX,e.clientY); if(!el) return; e.preventDefault(); e.stopPropagation();
633
+ var id=el.getAttribute("data-anim-id"); if(!id){ id="mc-"+Math.random().toString(36).slice(2,8); el.setAttribute("data-anim-id",id); }
634
+ var r=el.getBoundingClientRect();
635
+ post({kind:"picker:selected", target:{ animId:id, selector:selectorFor(el), tag:el.tagName.toLowerCase(), text:(el.textContent||"").trim().slice(0,80), rect:{x:r.x,y:r.y,width:r.width,height:r.height} }});
636
+ }
637
+ function setActive(v){ active=v; ensureHi(); banner.style.display=v?"block":"none"; if(!v) hi.style.display="none"; }
638
+ window.addEventListener("message", function(e){
639
+ if (PANEL !== "*" && e.origin !== PANEL) return;
640
+ var d=e.data; if(!d||d.source!==MC) return;
641
+ if(d.kind==="picker:enable") setActive(true);
642
+ else if(d.kind==="picker:disable") setActive(false);
643
+ else if(d.kind==="picker:clear"){ var n=document.querySelectorAll("[data-anim-id]"); for(var i=0;i<n.length;i++) n[i].removeAttribute("data-anim-id"); }
644
+ });
645
+ window.addEventListener("pointermove", onMove, true);
646
+ window.addEventListener("click", onClick, true);
647
+ post({kind:"picker:ready"});
648
+ })();`;
649
+ }
650
+
651
+ // ../core/dist/index.js
652
+ import { promises as fs } from "fs";
653
+ import path from "path";
654
+ import path2 from "path";
655
+ import { realpathSync } from "fs";
656
+ import { query } from "@anthropic-ai/claude-agent-sdk";
657
+ import { execFile } from "child_process";
658
+ import { promisify } from "util";
659
+ async function detectFramework(targetDir) {
660
+ let pkg = {};
661
+ try {
662
+ pkg = JSON.parse(await fs.readFile(path.join(targetDir, "package.json"), "utf8"));
663
+ } catch {
664
+ return "vanilla";
665
+ }
666
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
667
+ if (deps.next) return "next";
668
+ if (deps.vue || deps.nuxt || deps["@vitejs/plugin-vue"]) return "vue";
669
+ if (deps.react || deps["react-dom"]) return "react";
670
+ return "vanilla";
671
+ }
672
+ function frameworkGuidance(fw) {
673
+ switch (fw) {
674
+ case "next":
675
+ return 'Next.js + React. Use `motion/react` (client components need "use client"); `motion/react-client` in server components. Motion+ from `motion-plus/react`.';
676
+ case "react":
677
+ return "React. Import from `motion/react`. Motion+ from `motion-plus/react`.";
678
+ case "vue":
679
+ return "Vue. Use `motion-v` (the Vue port of Motion) \u2014 e.g. `<motion.div>` via the motion-v plugin, or the `v-motion` directive. Adapt React reference techniques to Vue SFC idioms.";
680
+ case "vanilla":
681
+ return "Vanilla JS/DOM. Use the `motion` package's `animate()` / `scroll()` / `inView()` functions directly on elements; no JSX. Adapt React reference techniques to imperative DOM calls.";
682
+ }
683
+ }
684
+ function composeInstruction(framework, items) {
685
+ const lines = items.flatMap((ci) => {
686
+ const it = MENU_BY_ID[ci.id];
687
+ if (!it) return [];
688
+ const head = `- "${it.title}" (${it.group})`;
689
+ if (!ci.targets.length) return [head, ` \u2192 place where it naturally fits.`];
690
+ const targetLines = ci.targets.map(
691
+ (t) => ` \u2192 apply to the <${t.tag}>${t.text ? ` containing "${t.text}"` : ""} [selector: ${t.selector}; it carries data-anim-id="${t.animId}" in the live DOM \u2014 find this element in the source and keep/add that attribute]`
692
+ );
693
+ return [head, ...targetLines];
694
+ });
695
+ return [
696
+ `You are adding animations to an existing ${framework} project. ${frameworkGuidance(framework)}`,
697
+ ``,
698
+ `Add these animations (with where to apply each):`,
699
+ ...lines,
700
+ ``,
701
+ `Rules:`,
702
+ `- Make minimal, surgical edits to the project's real source. Match the project's existing conventions and style.`,
703
+ `- For a targeted animation: locate the exact element in source via its visible text + selector, animate THAT element, and ensure it carries the given data-anim-id attribute so the binding is durable.`,
704
+ `- If an animation has no target, place it where it fits naturally.`,
705
+ `- Preserve all existing content, layout, and behaviour. Only ADD animation.`,
706
+ `- Keep it performant (animate transform/opacity/filter; avoid layout thrash; respect prefers-reduced-motion).`,
707
+ `- If the Motion library isn't installed yet, note it clearly in your summary (do not run package installs).`,
708
+ `- The project must keep compiling.`,
709
+ ``,
710
+ `Explain briefly what you changed.`
711
+ ].join("\n");
712
+ }
713
+ var WRITE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
714
+ var READ_TOOLS = /* @__PURE__ */ new Set(["Read", "Glob", "Grep"]);
715
+ var BLOCKED_TOOLS = /* @__PURE__ */ new Set(["Bash", "WebFetch", "WebSearch", "Task", "KillShell"]);
716
+ var DANGEROUS_MCP = /(write|exec|shell|spawn|delete|remove|fs|file|run|install)/i;
717
+ function realResolve(root, rel) {
718
+ const abs = path2.resolve(root, rel);
719
+ let probe = abs;
720
+ for (; ; ) {
721
+ try {
722
+ return path2.join(realpathSync(probe), path2.relative(probe, abs));
723
+ } catch {
724
+ const parent = path2.dirname(probe);
725
+ if (parent === probe) return abs;
726
+ probe = parent;
727
+ }
728
+ }
729
+ }
730
+ function inside(root, abs) {
731
+ let realRoot = root;
732
+ try {
733
+ realRoot = realpathSync(root);
734
+ } catch {
735
+ }
736
+ return abs === realRoot || abs.startsWith(realRoot + path2.sep);
737
+ }
738
+ async function* runAnimationAgent(opts) {
739
+ const { root, framework, items, motionToken } = opts;
740
+ const guard = async (tool, input) => {
741
+ if (BLOCKED_TOOLS.has(tool)) return { behavior: "deny", message: `${tool} is disabled.` };
742
+ if (WRITE_TOOLS.has(tool)) {
743
+ const rel = input.file_path ?? input.path ?? input.notebook_path;
744
+ if (!rel) return { behavior: "deny", message: "write without a path" };
745
+ if (!inside(root, realResolve(root, rel))) {
746
+ return { behavior: "deny", message: `Refusing to write outside the project: ${rel}` };
747
+ }
748
+ return { behavior: "allow" };
749
+ }
750
+ if (READ_TOOLS.has(tool)) return { behavior: "allow" };
751
+ if (tool.startsWith("mcp__motion__")) {
752
+ return DANGEROUS_MCP.test(tool) ? { behavior: "deny", message: `Motion MCP tool ${tool} not permitted.` } : { behavior: "allow" };
753
+ }
754
+ if (tool.startsWith("mcp__")) return { behavior: "deny", message: `Unexpected MCP tool ${tool}.` };
755
+ return { behavior: "allow" };
756
+ };
757
+ const mcpVersion = process.env.MOTION_MCP_VERSION || "latest";
758
+ const MOTION_MCP_PKG = `https://api.motion.dev/registry.tgz?package=motion-studio-mcp&version=${mcpVersion}`;
759
+ const mcpServers = motionToken ? {
760
+ motion: {
761
+ type: "stdio",
762
+ command: "npx",
763
+ args: ["-y", MOTION_MCP_PKG],
764
+ env: { TOKEN: motionToken }
765
+ }
766
+ } : void 0;
767
+ yield {
768
+ type: "status",
769
+ message: `Agent starting on a ${framework} project with ${items.length} animation(s)\u2026 ${mcpServers ? "(Motion AI Kit MCP enabled)" : "(bundled Motion guidance)"}`
770
+ };
771
+ const response = query({
772
+ prompt: composeInstruction(framework, items),
773
+ options: {
774
+ cwd: root,
775
+ permissionMode: "default",
776
+ canUseTool: guard,
777
+ allowedTools: ["Read", "Write", "Edit", "Glob", "Grep"],
778
+ disallowedTools: [...BLOCKED_TOOLS],
779
+ ...mcpServers ? { mcpServers } : {},
780
+ settingSources: [],
781
+ maxTurns: 80
782
+ }
783
+ });
784
+ try {
785
+ for await (const message of response) {
786
+ if (message.type === "assistant") {
787
+ const content = message.message?.content ?? [];
788
+ for (const block of content) {
789
+ if (block.type === "tool_use") {
790
+ const input = block.input ?? {};
791
+ const p = input.file_path ?? input.path;
792
+ yield { type: "tool", tool: String(block.name), detail: p ? path2.relative(root, path2.resolve(root, p)) : void 0 };
793
+ } else if (block.type === "text") {
794
+ const text = String(block.text ?? "").trim();
795
+ if (text) yield { type: "assistant", text };
796
+ }
797
+ }
798
+ } else if (message.type === "result") {
799
+ if (message.subtype === "success") {
800
+ yield { type: "done", summary: message.result, cost: message.total_cost_usd, turns: message.num_turns, ms: message.duration_ms };
801
+ } else {
802
+ yield { type: "error", message: `Agent ended: ${message.subtype}` };
803
+ }
804
+ }
805
+ }
806
+ } catch (err) {
807
+ yield { type: "error", message: err instanceof Error ? err.message : String(err) };
808
+ }
809
+ }
810
+ var run = promisify(execFile);
811
+ async function git(cwd, args) {
812
+ const { stdout } = await run("git", args, { cwd, maxBuffer: 1024 * 1024 * 32 });
813
+ return stdout.trim();
814
+ }
815
+ async function isGitRepo(dir) {
816
+ try {
817
+ await git(dir, ["rev-parse", "--is-inside-work-tree"]);
818
+ return true;
819
+ } catch {
820
+ return false;
821
+ }
822
+ }
823
+ async function isClean(dir) {
824
+ return (await git(dir, ["status", "--porcelain"])).length === 0;
825
+ }
826
+ async function currentBranch(dir) {
827
+ try {
828
+ return await git(dir, ["rev-parse", "--abbrev-ref", "HEAD"]);
829
+ } catch {
830
+ return "HEAD";
831
+ }
832
+ }
833
+ async function checkoutNewBranch(dir, branch) {
834
+ await git(dir, ["checkout", "-b", branch]);
835
+ }
836
+ async function commitAll(dir, message) {
837
+ await git(dir, ["add", "-A"]);
838
+ try {
839
+ await git(dir, ["commit", "-q", "-m", message]);
840
+ return true;
841
+ } catch {
842
+ return false;
843
+ }
844
+ }
845
+ async function diffStat(dir) {
846
+ try {
847
+ return await git(dir, ["--no-pager", "diff", "--stat"]);
848
+ } catch {
849
+ return "";
850
+ }
851
+ }
852
+
853
+ // src/proxy.ts
854
+ import http from "http";
855
+ import httpProxy from "http-proxy";
856
+
857
+ // src/inject.ts
858
+ function injectOverlay(html, panelOrigin) {
859
+ const cfg = `window.__MC_PANEL_ORIGIN=${JSON.stringify(panelOrigin)};`;
860
+ const tag = `<script data-mc-overlay-script>
861
+ ${cfg}
862
+ ${overlayScript()}
863
+ </script>`;
864
+ if (html.includes("</body>")) return html.replace("</body>", `${tag}</body>`);
865
+ return html + tag;
866
+ }
867
+ function relaxFramingHeaders(headers) {
868
+ const h = { ...headers };
869
+ delete h["x-frame-options"];
870
+ delete h["cross-origin-opener-policy"];
871
+ delete h["cross-origin-embedder-policy"];
872
+ const stripFrameAncestors = (csp) => csp.split(";").map((d) => d.trim()).filter((d) => d && !/^frame-ancestors/i.test(d)).join("; ");
873
+ if (typeof h["content-security-policy"] === "string") {
874
+ h["content-security-policy"] = stripFrameAncestors(h["content-security-policy"]);
875
+ }
876
+ if (typeof h["content-security-policy-report-only"] === "string") {
877
+ h["content-security-policy-report-only"] = stripFrameAncestors(
878
+ h["content-security-policy-report-only"]
879
+ );
880
+ }
881
+ return h;
882
+ }
883
+
884
+ // src/proxy.ts
885
+ var MAX_HTML_BYTES = 8 * 1024 * 1024;
886
+ function startProxy(devUrl, port2, panelOrigin) {
887
+ const proxy = httpProxy.createProxyServer({
888
+ target: devUrl,
889
+ selfHandleResponse: true,
890
+ changeOrigin: true,
891
+ ws: true
892
+ });
893
+ proxy.on("proxyReq", (proxyReq) => proxyReq.setHeader("accept-encoding", "identity"));
894
+ proxy.on("proxyRes", (proxyRes, req, res) => {
895
+ const headers = relaxFramingHeaders(proxyRes.headers);
896
+ const ct = String(proxyRes.headers["content-type"] || "");
897
+ const encoded = !!proxyRes.headers["content-encoding"];
898
+ const streamed = proxyRes.headers["transfer-encoding"] === "chunked" || ct.includes("text/event-stream");
899
+ const isHtml = ct.includes("text/html");
900
+ if (!isHtml || encoded || streamed) {
901
+ res.writeHead(proxyRes.statusCode || 200, headers);
902
+ proxyRes.pipe(res);
903
+ return;
904
+ }
905
+ const chunks = [];
906
+ let size = 0;
907
+ let overflow = false;
908
+ proxyRes.on("data", (c) => {
909
+ size += c.length;
910
+ if (size > MAX_HTML_BYTES) {
911
+ overflow = true;
912
+ return;
913
+ }
914
+ chunks.push(c);
915
+ });
916
+ proxyRes.on("end", () => {
917
+ if (overflow) {
918
+ res.writeHead(proxyRes.statusCode || 200, headers);
919
+ res.end(Buffer.concat(chunks));
920
+ return;
921
+ }
922
+ const body = injectOverlay(Buffer.concat(chunks).toString("utf8"), panelOrigin);
923
+ delete headers["content-length"];
924
+ headers["content-length"] = String(Buffer.byteLength(body));
925
+ res.writeHead(proxyRes.statusCode || 200, headers);
926
+ res.end(body);
927
+ });
928
+ });
929
+ proxy.on("error", (err, _req, target) => {
930
+ if (target && "writeHead" in target) {
931
+ const res = target;
932
+ if (!res.headersSent) res.writeHead(502, { "content-type": "text/plain" });
933
+ res.end(`motion-cart proxy error: ${err.message}`);
934
+ } else if (target && "destroy" in target) {
935
+ ;
936
+ target.destroy();
937
+ }
938
+ });
939
+ const server = http.createServer((req, res) => proxy.web(req, res));
940
+ server.on("upgrade", (req, socket, head) => {
941
+ try {
942
+ proxy.ws(req, socket, head);
943
+ } catch {
944
+ socket.destroy();
945
+ }
946
+ });
947
+ server.listen(port2, "127.0.0.1");
948
+ return server;
949
+ }
950
+
951
+ // src/server.ts
952
+ import http2 from "http";
953
+ import { randomUUID } from "crypto";
954
+
955
+ // src/panel.ts
956
+ function panelHtml(opts) {
957
+ return `<!doctype html>
958
+ <html><head><meta charset="utf-8"><title>motion-cart</title>
959
+ <style>
960
+ :root{color-scheme:dark}
961
+ *{box-sizing:border-box}
962
+ body{margin:0;height:100vh;display:flex;flex-direction:column;background:#0a0b0e;color:#e7e7ea;font:13px system-ui}
963
+ header{display:flex;align-items:center;gap:12px;padding:10px 14px;border-bottom:1px solid #ffffff1a}
964
+ header b{font-size:14px}header small{color:#ffffff80}
965
+ .body{flex:1;display:flex;min-height:0}
966
+ iframe{flex:1;border:0;background:#fff}
967
+ aside{width:360px;border-left:1px solid #ffffff1a;display:flex;flex-direction:column;min-height:0}
968
+ input{width:100%;background:#ffffff0d;border:1px solid #ffffff26;border-radius:8px;color:#fff;padding:9px 11px;outline:none;font-size:13px}
969
+ input:focus{border-color:#22d3ee80}
970
+ .hint{color:#ffffff73;font-size:12px;margin:8px 2px 0}
971
+ .cat{flex:1;overflow:auto;padding:8px 10px}
972
+ .grp{font-size:12px;text-transform:uppercase;letter-spacing:.05em;color:#ffffff8c;margin:12px 2px 5px;font-weight:600}
973
+ .row{display:flex;justify-content:space-between;align-items:center;padding:8px 10px;border-radius:6px;cursor:pointer;color:#ffffffcc;font-size:13px}
974
+ .row:hover{background:#ffffff14;color:#fff}
975
+ .row.in{background:#22d3ee30;color:#a5f3fc;box-shadow:inset 0 0 0 1px #22d3ee66}
976
+ footer{border-top:1px solid #ffffff1a;padding:11px}
977
+ .item{border:1px solid #ffffff1a;border-radius:6px;padding:7px 9px;margin-bottom:6px;background:#ffffff0a}
978
+ .item.pick{border-color:#22d3eecc;background:#22d3ee26}
979
+ .item .top{display:flex;justify-content:space-between;align-items:center;gap:6px}
980
+ .btn{border:1px solid #ffffff33;border-radius:5px;background:#ffffff0d;color:#fff;padding:4px 9px;font-size:12px;cursor:pointer}
981
+ .btn.on{background:#22d3ee;color:#06202a;border-color:#22d3ee}
982
+ .chip{display:inline-flex;gap:5px;align-items:center;background:#00000080;border-radius:4px;padding:2px 7px;font-size:11px;color:#ffffffb3;margin:4px 4px 0 0}
983
+ .empty{border:1px dashed #ffffff26;border-radius:6px;padding:12px;text-align:center;color:#ffffff73;font-size:12px;margin-bottom:8px}
984
+ .row2{display:flex;gap:8px}
985
+ .apply{flex:1;padding:11px;border:0;border-radius:8px;font-weight:600;color:#fff;cursor:pointer;background:linear-gradient(90deg,#06b6d4,#6366f1);font-size:13px}
986
+ .apply:disabled{opacity:.35;cursor:not-allowed}
987
+ .stop{padding:11px 14px;border:1px solid #ffffff33;border-radius:8px;background:#ffffff0d;color:#fff;cursor:pointer}
988
+ .note{text-align:center;color:#ffffff66;font-size:11px;margin-top:7px}
989
+ pre{margin:7px 0 0;max-height:200px;overflow:auto;background:#000000a6;border:1px solid #ffffff26;border-radius:6px;padding:9px;font:12px ui-monospace;color:#ffffffd9;white-space:pre-wrap}
990
+ .x{color:#ffffff73;cursor:pointer}.x:hover{color:#fff}
991
+ </style></head>
992
+ <body>
993
+ <header><b>motion-cart</b><small>${opts.framework} &middot; ${opts.target}</small></header>
994
+ <div class="body">
995
+ <iframe id="site" src="${opts.proxyUrl}"></iframe>
996
+ <aside>
997
+ <div style="padding:11px;border-bottom:1px solid #ffffff1a">
998
+ <input id="q" placeholder="Search animations\u2026">
999
+ <div class="hint">Click an animation to add it &middot; then optionally pick the elements to target.</div>
1000
+ </div>
1001
+ <div class="cat" id="cat"></div>
1002
+ <footer><div id="cart"></div>
1003
+ <div class="row2">
1004
+ <button class="apply" id="apply" disabled>Apply with AI</button>
1005
+ <button class="stop" id="stop" style="display:none">Stop</button>
1006
+ </div>
1007
+ <div class="note">Runs a real Claude agent on your project &middot; ~30s &middot; uses AI credits<span id="sess"></span></div>
1008
+ <pre id="log" style="display:none"></pre>
1009
+ </footer>
1010
+ </aside>
1011
+ </div>
1012
+ <script>
1013
+ const MC="motion-cart", PROXY=${JSON.stringify(opts.proxyUrl)}, TOKEN=${JSON.stringify(opts.token)};
1014
+ let MENU=[],GROUPS=[],cart=[],pickingFor=null,running=false,q="",sessionCost=0,abort=null;
1015
+ const site=document.getElementById('site');
1016
+ function send(kind){ try{ site.contentWindow.postMessage({source:MC,kind},PROXY);}catch(e){} }
1017
+ window.addEventListener('message',e=>{
1018
+ if(e.origin!==PROXY) return;
1019
+ const d=e.data; if(!d||d.source!==MC) return;
1020
+ if(d.kind==='picker:ready' && pickingFor) send('picker:enable');
1021
+ if(d.kind==='picker:selected' && pickingFor){
1022
+ const it=cart.find(c=>c.id===pickingFor); if(!it) return;
1023
+ if(!it.targets.some(t=>t.animId===d.target.animId)) it.targets.push(d.target);
1024
+ renderCart();
1025
+ }
1026
+ });
1027
+ fetch('/api/catalogue',{headers:{'x-mc-token':TOKEN}}).then(r=>r.json()).then(d=>{MENU=d.items;GROUPS=d.groups;renderCat();});
1028
+ function inCart(id){return cart.some(c=>c.id===id)}
1029
+ function toggle(id){ cart=inCart(id)?cart.filter(c=>c.id!==id):cart.concat([{id,targets:[]}]); if(pickingFor===id)setPicking(null); renderCat();renderCart(); }
1030
+ function setPicking(id){ pickingFor=id; send(id?'picker:enable':'picker:disable'); renderCart(); }
1031
+ function pickFor(id){ if(!inCart(id))cart.push({id,targets:[]}); setPicking(pickingFor===id?null:id); }
1032
+ function title(id){const m=MENU.find(x=>x.id===id);return m?m.title:id}
1033
+ function renderCat(){
1034
+ const cat=document.getElementById('cat');cat.innerHTML='';
1035
+ const matched=GROUPS.map(g=>({g,items:MENU.filter(m=>m.group===g && (!q || (m.title+' '+g).toLowerCase().includes(q)))})).filter(x=>x.items.length);
1036
+ if(!matched.length){ cat.innerHTML='<p class="empty">No animations match "'+q+'".</p>'; return; }
1037
+ matched.forEach(({g,items})=>{
1038
+ const h=document.createElement('div');h.className='grp';h.textContent=g;cat.appendChild(h);
1039
+ items.forEach(m=>{const r=document.createElement('div');r.className='row'+(inCart(m.id)?' in':'');
1040
+ r.innerHTML='<span>'+m.title+'</span><span>'+(inCart(m.id)?'\\u2713':'+')+'</span>';
1041
+ r.onclick=()=>toggle(m.id);cat.appendChild(r);});
1042
+ });
1043
+ }
1044
+ function renderCart(){
1045
+ const c=document.getElementById('cart');c.innerHTML='';
1046
+ if(!cart.length){ c.innerHTML='<div class="empty">Your cart is empty. Add animations from the catalogue above.</div>'; }
1047
+ cart.forEach(it=>{const d=document.createElement('div');d.className='item'+(pickingFor===it.id?' pick':'');
1048
+ const picking=pickingFor===it.id;
1049
+ d.innerHTML='<div class="top"><span>'+title(it.id)+'</span><span>'+
1050
+ '<button class="btn '+(picking?'on':'')+'" data-pick="'+it.id+'">'+(picking?'Done picking':('Target'+(it.targets.length?' ('+it.targets.length+')':'')))+'</button> '+
1051
+ '<span class="x" data-rm="'+it.id+'">\\u2715</span></span></div>'+
1052
+ (it.targets.length?'<div>'+it.targets.map(t=>'<span class="chip">'+t.tag+(t.text?'\\u00b7'+t.text.slice(0,14):'')+'</span>').join('')+'</div>':'');
1053
+ c.appendChild(d);});
1054
+ c.querySelectorAll('[data-pick]').forEach(b=>b.onclick=()=>pickFor(b.getAttribute('data-pick')));
1055
+ c.querySelectorAll('[data-rm]').forEach(b=>b.onclick=()=>toggle(b.getAttribute('data-rm')));
1056
+ document.getElementById('apply').disabled=!cart.length||running;
1057
+ }
1058
+ document.getElementById('q').oninput=e=>{q=e.target.value.trim().toLowerCase();renderCat();};
1059
+ document.getElementById('stop').onclick=()=>{ if(abort) abort.abort(); };
1060
+ document.getElementById('apply').onclick=async()=>{
1061
+ if(!cart.length||running)return; running=true; setPicking(null);
1062
+ const log=document.getElementById('log');log.style.display='block';log.textContent='Sending cart to the agent\u2026\\n';
1063
+ document.getElementById('apply').disabled=true; document.getElementById('stop').style.display='inline-block';
1064
+ const started=Date.now();
1065
+ abort=new AbortController();
1066
+ try{
1067
+ const res=await fetch('/api/apply',{method:'POST',headers:{'content-type':'application/json','x-mc-token':TOKEN},body:JSON.stringify({items:cart}),signal:abort.signal});
1068
+ if(!res.ok||!res.body){ log.textContent+='Error: server returned '+res.status+'\\n'; return; }
1069
+ const rd=res.body.getReader();const dec=new TextDecoder();let buf='';
1070
+ for(;;){const{value,done}=await rd.read();
1071
+ buf+=done?dec.decode():dec.decode(value,{stream:true});
1072
+ const fr=buf.split('\\n\\n');buf=done?'':fr.pop();
1073
+ for(const f of fr){const l=f.replace(/^data: /,'').trim();if(!l||l[0]===':')continue;let ev;try{ev=JSON.parse(l)}catch{continue}
1074
+ if(ev.type==='status')log.textContent+=ev.message+'\\n';
1075
+ else if(ev.type==='tool')log.textContent+=(ev.tool==='Read'?'read ':(/Edit|Write/.test(ev.tool)?'edit ':ev.tool.toLowerCase()+' '))+(ev.detail||'')+'\\n';
1076
+ else if(ev.type==='assistant')log.textContent+='AI: '+ev.text.slice(0,200)+'\\n';
1077
+ else if(ev.type==='done'){log.textContent+='Done'+(ev.turns?' \\u00b7 '+ev.turns+' turns':'')+(ev.cost?' \\u00b7 $'+ev.cost.toFixed(3):'')+'\\n'; if(ev.cost){sessionCost+=ev.cost; document.getElementById('sess').textContent=' \\u00b7 session: $'+sessionCost.toFixed(3);}}
1078
+ else if(ev.type==='error')log.textContent+='Error: '+ev.message+'\\n';
1079
+ log.scrollTop=log.scrollHeight;}
1080
+ if(done)break;
1081
+ }
1082
+ }catch(err){ log.textContent+=(err&&err.name==='AbortError'?'Stopped':'Error: '+err)+'\\n'; }
1083
+ finally{
1084
+ running=false;abort=null;
1085
+ document.getElementById('apply').disabled=false; document.getElementById('stop').style.display='none';
1086
+ const secs=Math.round((Date.now()-started)/1000); log.textContent+='('+secs+'s)\\n';
1087
+ setTimeout(()=>{site.src=site.src;},400);
1088
+ }
1089
+ };
1090
+ </script>
1091
+ </body></html>`;
1092
+ }
1093
+
1094
+ // src/server.ts
1095
+ var MAX_BODY = 256 * 1024;
1096
+ function readBody(req) {
1097
+ return new Promise((resolve, reject) => {
1098
+ let b = "";
1099
+ let size = 0;
1100
+ req.on("data", (c) => {
1101
+ size += c.length;
1102
+ if (size > MAX_BODY) {
1103
+ reject(new Error("body too large"));
1104
+ req.destroy();
1105
+ return;
1106
+ }
1107
+ b += c;
1108
+ });
1109
+ req.on("end", () => resolve(b));
1110
+ req.on("error", reject);
1111
+ });
1112
+ }
1113
+ function startControlServer(cfg, port2) {
1114
+ const panelOrigin = `http://localhost:${port2}`;
1115
+ const sessionToken = randomUUID();
1116
+ let sessionBranch = null;
1117
+ let baseBranch = "HEAD";
1118
+ async function ensureSession() {
1119
+ if (sessionBranch) return;
1120
+ if (!await isGitRepo(cfg.targetDir)) throw new Error("target is not a git repository");
1121
+ if (!await isClean(cfg.targetDir))
1122
+ throw new Error("target git tree has uncommitted changes \u2014 commit or stash first");
1123
+ baseBranch = await currentBranch(cfg.targetDir);
1124
+ sessionBranch = `motion-cart/session-${Date.now()}`;
1125
+ await checkoutNewBranch(cfg.targetDir, sessionBranch);
1126
+ }
1127
+ function authed(req) {
1128
+ const origin = req.headers["origin"];
1129
+ if (origin && origin !== panelOrigin) return false;
1130
+ return req.headers["x-mc-token"] === sessionToken;
1131
+ }
1132
+ const server = http2.createServer(async (req, res) => {
1133
+ const url = new URL(req.url || "/", panelOrigin);
1134
+ if (req.method === "GET" && url.pathname === "/") {
1135
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
1136
+ return res.end(
1137
+ panelHtml({
1138
+ proxyUrl: cfg.proxyUrl,
1139
+ target: cfg.targetDir,
1140
+ framework: cfg.framework,
1141
+ token: sessionToken
1142
+ })
1143
+ );
1144
+ }
1145
+ if (url.pathname.startsWith("/api/")) {
1146
+ if (!authed(req)) {
1147
+ res.writeHead(403, { "content-type": "application/json" });
1148
+ return res.end(JSON.stringify({ error: "forbidden" }));
1149
+ }
1150
+ }
1151
+ if (req.method === "GET" && url.pathname === "/api/catalogue") {
1152
+ res.writeHead(200, { "content-type": "application/json" });
1153
+ return res.end(JSON.stringify({ groups: MENU_GROUPS, items: MENU }));
1154
+ }
1155
+ if (req.method === "POST" && url.pathname === "/api/apply") {
1156
+ let items = [];
1157
+ try {
1158
+ items = JSON.parse(await readBody(req)).items ?? [];
1159
+ } catch {
1160
+ }
1161
+ res.writeHead(200, {
1162
+ "content-type": "text/event-stream; charset=utf-8",
1163
+ "cache-control": "no-cache, no-transform",
1164
+ connection: "keep-alive"
1165
+ });
1166
+ const send = (e) => res.write(`data: ${JSON.stringify(e)}
1167
+
1168
+ `);
1169
+ const heartbeat = setInterval(() => res.write(`: ping
1170
+
1171
+ `), 15e3);
1172
+ try {
1173
+ if (!items.length) throw new Error("cart is empty");
1174
+ await ensureSession();
1175
+ send({
1176
+ type: "status",
1177
+ message: `Editing on branch ${sessionBranch} (branched from ${baseBranch}; your ${baseBranch} branch is untouched)`
1178
+ });
1179
+ for await (const ev of runAnimationAgent({
1180
+ root: cfg.targetDir,
1181
+ framework: cfg.framework,
1182
+ items,
1183
+ motionToken: cfg.token
1184
+ })) {
1185
+ send(ev);
1186
+ }
1187
+ const stat = await diffStat(cfg.targetDir);
1188
+ const committed = await commitAll(cfg.targetDir, `motion-cart: applied ${items.length} animation(s)`);
1189
+ if (committed && stat) send({ type: "status", message: `Committed:
1190
+ ${stat}` });
1191
+ send({ type: "status", message: `Review: git diff ${baseBranch}..${sessionBranch}` });
1192
+ } catch (err) {
1193
+ send({ type: "error", message: err instanceof Error ? err.message : String(err) });
1194
+ } finally {
1195
+ clearInterval(heartbeat);
1196
+ send({ type: "end" });
1197
+ res.end();
1198
+ }
1199
+ return;
1200
+ }
1201
+ res.writeHead(404);
1202
+ res.end();
1203
+ });
1204
+ server.listen(port2, "127.0.0.1");
1205
+ return { server, url: panelOrigin };
1206
+ }
1207
+
1208
+ // src/index.ts
1209
+ function arg(name, fallback) {
1210
+ const i = process.argv.indexOf(`--${name}`);
1211
+ if (i >= 0) {
1212
+ const v = process.argv[i + 1];
1213
+ if (v && !v.startsWith("--")) return v;
1214
+ }
1215
+ return fallback;
1216
+ }
1217
+ function port(name, def) {
1218
+ const v = Number(arg(name, String(def)));
1219
+ return Number.isInteger(v) && v > 0 && v < 65536 ? v : def;
1220
+ }
1221
+ var LOOPBACK = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "0.0.0.0", "[::1]", "::1"]);
1222
+ async function main() {
1223
+ const target = path3.resolve(arg("target", process.cwd()));
1224
+ const devUrl = arg("dev-url");
1225
+ const panelPort = port("port", 7100);
1226
+ const proxyPort = port("proxy-port", 7101);
1227
+ const token = arg("token", process.env.MOTION_PLUS_TOKEN);
1228
+ if (!devUrl) {
1229
+ console.error(
1230
+ "Usage: motion-cart --dev-url <url> [--target <dir>] [--port 7100] [--proxy-port 7101] [--token <motion+ token>]\n --dev-url URL of your already-running LOCAL dev server (e.g. http://localhost:5173)\n --target project directory the agent will edit (default: cwd)"
1231
+ );
1232
+ process.exit(1);
1233
+ }
1234
+ let parsed;
1235
+ try {
1236
+ parsed = new URL(devUrl);
1237
+ } catch {
1238
+ console.error(`Invalid --dev-url: ${devUrl}`);
1239
+ process.exit(1);
1240
+ }
1241
+ if (!/^https?:$/.test(parsed.protocol) || !LOOPBACK.has(parsed.hostname)) {
1242
+ console.error(
1243
+ `Refusing --dev-url ${devUrl}: motion-cart only proxies a local dev server (localhost/127.0.0.1).`
1244
+ );
1245
+ process.exit(1);
1246
+ }
1247
+ const framework = await detectFramework(target);
1248
+ const proxyUrl = `http://localhost:${proxyPort}`;
1249
+ startProxy(devUrl, proxyPort, `http://localhost:${panelPort}`);
1250
+ const { url } = startControlServer({ targetDir: target, devUrl, proxyUrl, framework, token }, panelPort);
1251
+ console.log(`
1252
+ motion-cart`);
1253
+ console.log(` target ${target} (${framework})`);
1254
+ console.log(` dev-url ${devUrl} -> proxied at ${proxyUrl}`);
1255
+ console.log(` MCP ${token ? "Motion AI Kit enabled" : "bundled guidance (set --token to enable)"}`);
1256
+ console.log(`
1257
+ Open the control panel: ${url}
1258
+ `);
1259
+ }
1260
+ main().catch((e) => {
1261
+ console.error(e);
1262
+ process.exit(1);
1263
+ });
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "motion-cart",
3
+ "version": "0.1.0",
4
+ "description": "Shop for animations and let an AI agent add them to any React/Next/Vue/JS project — via an injecting dev proxy + element picker.",
5
+ "keywords": ["motion", "animation", "ai", "agent", "framer-motion", "cli", "devtool"],
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/L-ubu/motion-cart",
8
+ "repository": { "type": "git", "url": "git+https://github.com/L-ubu/motion-cart.git" },
9
+ "type": "module",
10
+ "bin": {
11
+ "motion-cart": "./dist/index.js"
12
+ },
13
+ "files": ["dist", "README.md"],
14
+ "engines": { "node": ">=18" },
15
+ "publishConfig": { "access": "public" },
16
+ "scripts": {
17
+ "dev": "tsx --conditions=development src/index.ts",
18
+ "build": "tsup",
19
+ "start": "node dist/index.js",
20
+ "typecheck": "tsc -p tsconfig.json",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "dependencies": {
24
+ "@anthropic-ai/claude-agent-sdk": "^0.3.0",
25
+ "http-proxy": "^1.18.1"
26
+ },
27
+ "devDependencies": {
28
+ "@motion-cart/core": "workspace:*",
29
+ "@types/http-proxy": "^1.17.15",
30
+ "@types/node": "^22.10.0",
31
+ "tsup": "^8.3.5",
32
+ "tsx": "^4.19.2",
33
+ "typescript": "^5.6.3"
34
+ }
35
+ }