Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 30 additions & 29 deletions src/lib/ai/system-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,42 +68,43 @@ function buildCanvasState(project: Project): string {
return 'Empty canvas — no layers yet.';
}

const halfHeight = project.height / 2;

// Group layers by rough vertical position
const topLayers = project.layers.filter((l) => l.transform.y < -halfHeight / 3);
const middleLayers = project.layers.filter(
(l) => l.transform.y >= -halfHeight / 3 && l.transform.y <= halfHeight / 3
);
const bottomLayers = project.layers.filter((l) => l.transform.y > halfHeight / 3);

let spatial = '';
if (topLayers.length > 0) {
spatial += `TOP: ${topLayers.map((l) => `[${l.name}]`).join(' ')}\n`;
}
if (middleLayers.length > 0) {
spatial += `CENTER: ${middleLayers.map((l) => `[${l.name}]`).join(' ')}\n`;
}
if (bottomLayers.length > 0) {
spatial += `BOTTOM: ${bottomLayers.map((l) => `[${l.name}]`).join(' ')}\n`;
}

const layerList = project.layers
.map((layer, index) => {
const animatedProps = [...new Set(layer.keyframes.map((k) => k.property))];
// Show ALL props
const propsPreview = Object.entries(layer.props)
.slice(0, 3)
.map(([k, v]) => `${k}=${JSON.stringify(v)}`)
.join(', ');

// Group keyframes by property and show all details
const keyframesByProp = new Map<string, typeof layer.keyframes>();
for (const kf of layer.keyframes) {
if (!keyframesByProp.has(kf.property)) {
keyframesByProp.set(kf.property, []);
}
keyframesByProp.get(kf.property)!.push(kf);
}

// Build detailed keyframe info
let keyframesDetail = '';
if (keyframesByProp.size > 0) {
keyframesDetail = '\n keyframes:';
for (const [prop, kfs] of keyframesByProp) {
const kfList = kfs
.sort((a, b) => a.time - b.time)
.map(
(kf) =>
`t=${kf.time}s: ${JSON.stringify(kf.value)}${kf.easing ? ` (${kf.easing})` : ''}`
)
.join(', ');
Comment on lines +94 to +98
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the keyframe type definition
echo "=== Searching for Keyframe type definition ==="
find . -name "*.ts" -o -name "*.d.ts" | xargs grep -l "Keyframe" | grep -v node_modules | head -10

Repository: epavanello/devmotion

Length of output: 356


🏁 Script executed:

#!/bin/bash
# Look at the types file to find keyframe definition
echo "=== Keyframe type in $lib/types ==="
fd -e ts "$lib/types" 2>/dev/null | head -20

# More direct search
echo ""
echo "=== Search for Keyframe type/interface ==="
rg -n "type Keyframe|interface Keyframe" --type=ts

Repository: epavanello/devmotion

Length of output: 227


🏁 Script executed:

#!/bin/bash
# Check the actual system-prompt.ts file around lines 94-98
echo "=== Context around lines 94-98 in system-prompt.ts ==="
cat -n src/lib/ai/system-prompt.ts | sed -n '85,105p'

echo ""
echo "=== Check full buildCanvasState function ==="
cat -n src/lib/ai/system-prompt.ts | sed -n '70,110p'

Repository: epavanello/devmotion

Length of output: 3046


🏁 Script executed:

#!/bin/bash
# Search for where keyframes are created/manipulated to see what properties they have
echo "=== Keyframe creation and property usage ==="
rg -n "\.easing|\.interpolation" -B2 -A2 --type=ts | head -50

echo ""
echo "=== Look at mutations.ts for keyframe creation ==="
if [ -f src/lib/mutations.ts ]; then
  cat -n src/lib/mutations.ts | sed -n '220,250p'
fi

Repository: epavanello/devmotion

Length of output: 2178


🏁 Script executed:

#!/bin/bash
# Find the animation types definition
echo "=== Looking for animation/keyframe types ==="
find . -path "*/lib/types*" -name "*.ts" 2>/dev/null

Repository: epavanello/devmotion

Length of output: 1267


🏁 Script executed:

#!/bin/bash
# Check the KeyframeSchema definition
echo "=== KeyframeSchema definition ==="
cat -n src/lib/schemas/animation.ts | sed -n '240,270p'

Repository: epavanello/devmotion

Length of output: 1385


🏁 Script executed:

#!/bin/bash
# Look for where rotationZ values are set/used to confirm units
echo "=== rotationZ unit verification ==="
rg -n "rotationZ" --type=ts -B3 -A3 | head -60

Repository: epavanello/devmotion

Length of output: 3835


🏁 Script executed:

#!/bin/bash
# Check the generateTransformCSS to see how rotationZ is used
echo "=== Transform CSS generation ==="
rg -n "generateTransformCSS|rotateZ|rotation" --type=ts -B2 -A2 | head -80

Repository: epavanello/devmotion

Length of output: 4984


🏁 Script executed:

#!/bin/bash
# Get the actual KeyframeSchema definition
echo "=== KeyframeSchema definition ==="
cat -n src/lib/schemas/animation.ts | sed -n '100,180p'

Repository: epavanello/devmotion

Length of output: 3316


🏁 Script executed:

#!/bin/bash
# Search for KeyframeSchema more specifically
echo "=== Searching for KeyframeSchema ==="
rg -n "KeyframeSchema\s*=" --type=ts -A20 src/lib/schemas/animation.ts

Repository: epavanello/devmotion

Length of output: 804


kf.easing doesn't exist on keyframe objects — easing info will never display.

The Keyframe type (defined in src/lib/schemas/animation.ts:119-125) has an interpolation property, not easing. Using kf.easing on line 96 will always be falsy, silently omitting the interpolation details you intended to include.

Additionally, on line 104, rotationZ is displayed with a ° symbol, but it's stored in radians throughout the system (see src/lib/schemas/base.ts:44 and src/lib/layers/base.ts:318). This is misleading to the AI.

Suggested fixes:

Line 96: Use interpolation instead of easing
             (kf) =>
-                `t=${kf.time}s: ${JSON.stringify(kf.value)}${kf.easing ? ` (${kf.easing})` : ''}`
+                `t=${kf.time}s: ${JSON.stringify(kf.value)}${kf.interpolation ? ` (${kf.interpolation.family}/${kf.interpolation.strategy})` : ''}`
Line 104: Fix rotationZ unit
   pos: (${layer.transform.x}, ${layer.transform.y}) | scale: (${layer.transform.scaleX}, ${layer.transform.scaleY}) | rotation: ${layer.transform.rotationZ}° | opacity: ${layer.style.opacity}
+  pos: (${layer.transform.x}, ${layer.transform.y}) | scale: (${layer.transform.scaleX}, ${layer.transform.scaleY}) | rotation: ${layer.transform.rotationZ}rad | opacity: ${layer.style.opacity}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.map(
(kf) =>
`t=${kf.time}s: ${JSON.stringify(kf.value)}${kf.easing ? ` (${kf.easing})` : ''}`
)
.join(', ');
.map(
(kf) =>
`t=${kf.time}s: ${JSON.stringify(kf.value)}${kf.interpolation ? ` (${kf.interpolation.family}/${kf.interpolation.strategy})` : ''}`
)
.join(', ');
🤖 Prompt for AI Agents
In `@src/lib/ai/system-prompt.ts` around lines 94 - 98, Replace the nonexistent
kf.easing with kf.interpolation when building the keyframe string so
interpolation details are shown (Keyframe -> interpolation); also correct
rotationZ unit display by not appending "°" — either show rotationZ as radians
with "rad" or convert the stored rad value to degrees before appending "°"
(update the mapping/formatting where rotationZ is rendered so the AI is given
the correct unit).

keyframesDetail += `\n ${prop}: [${kfList}]`;
}
}

return `${index}. "${layer.name}" (id: "${layer.id}", type: ${layer.type})
pos: (${layer.transform.x}, ${layer.transform.y}) | opacity: ${layer.style.opacity}
props: {${propsPreview}${Object.keys(layer.props).length > 3 ? ', ...' : ''}}
animated: [${animatedProps.join(', ') || 'none'}]`;
pos: (${layer.transform.x}, ${layer.transform.y}) | scale: (${layer.transform.scaleX}, ${layer.transform.scaleY}) | rotation: ${layer.transform.rotationZ}° | opacity: ${layer.style.opacity}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# First, find the file structure to understand the layout
echo "=== Repository structure ==="
git ls-files | grep -E "(base\.ts|system-prompt\.ts)" | head -20

echo ""
echo "=== Check system-prompt.ts around line 104 ==="
wc -l src/lib/ai/system-prompt.ts
head -110 src/lib/ai/system-prompt.ts | tail -20

echo ""
echo "=== Check generateTransformCSS in base.ts ==="
fd base.ts -x cat -n {} \; | grep -A10 -B2 "generateTransformCSS"

echo ""
echo "=== Search for rotationZ definitions ==="
rg -n "rotationZ" --type=ts -g '!node_modules/**' | head -40

Repository: epavanello/devmotion

Length of output: 2892


rotationZ is in radians, but the prompt labels it with ° (degrees).

Per the schema definition in src/lib/schemas/base.ts and generateTransformCSS in src/lib/layers/base.ts, rotationZ values are stored and processed in radians. Displaying them with a ° suffix will mislead the AI into treating stored radian values as degrees. Since the AI tool accepts degrees as input (as seen in src/lib/ai/mutations.ts where it converts input.updates.rotation * Math.PI / 180), the system prompt should clearly indicate the stored unit is radians.

Suggested fix
-   pos: (${layer.transform.x}, ${layer.transform.y}) | scale: (${layer.transform.scaleX}, ${layer.transform.scaleY}) | rotation: ${layer.transform.rotationZ}° | opacity: ${layer.style.opacity}
+   pos: (${layer.transform.x}, ${layer.transform.y}) | scale: (${layer.transform.scaleX}, ${layer.transform.scaleY}) | rotation: ${layer.transform.rotationZ}rad | opacity: ${layer.style.opacity}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pos: (${layer.transform.x}, ${layer.transform.y}) | scale: (${layer.transform.scaleX}, ${layer.transform.scaleY}) | rotation: ${layer.transform.rotationZ}° | opacity: ${layer.style.opacity}
pos: (${layer.transform.x}, ${layer.transform.y}) | scale: (${layer.transform.scaleX}, ${layer.transform.scaleY}) | rotation: ${layer.transform.rotationZ}rad | opacity: ${layer.style.opacity}
🤖 Prompt for AI Agents
In `@src/lib/ai/system-prompt.ts` at line 104, The prompt currently labels
rotationZ with a degree symbol but rotationZ is stored in radians (see rotationZ
and generateTransformCSS and the schema), so change the system prompt text in
system-prompt.ts to indicate radians (e.g., use "rad" or "radians" and remove
the "°" suffix) and ensure any example or phrasing makes clear the AI should
expect rotationZ in radians (matching the input.updates.rotation * Math.PI / 180
conversion used elsewhere).

props: {${propsPreview || 'none'}}${keyframesDetail || '\n keyframes: none'}`;
})
.join('\n');
.join('\n\n');

return `${spatial}
${project.layers.length} layer(s):
${layerList}`;
return `${project.layers.length} layer(s):\n${layerList}`;
}