Skip to content

Commit a74c866

Browse files
authored
feat(assistant): added openai to assist in generating js/ts code and json schemas (#231)
* change hiddenFromSidebar to hideFromToolbar * added code generation for the function block's code * added ai code gen to custom tools and response format * optimize system prompts for js/json generation following our formatting * added history, fixed apply to replace instead of append * remove unused imports * updated styling of chat bar * refined system prompts for code generation, resolved PR comments
1 parent d97ecea commit a74c866

File tree

14 files changed

+1625
-154
lines changed

14 files changed

+1625
-154
lines changed

sim/app/api/codegen/route.ts

Lines changed: 432 additions & 0 deletions
Large diffs are not rendered by default.

sim/app/globals.css

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,42 @@
165165
.scrollbar-none::-webkit-scrollbar {
166166
display: none;
167167
}
168+
169+
/* Code Editor Streaming Effect */
170+
@keyframes code-shimmer {
171+
0% {
172+
transform: translateX(-100%);
173+
}
174+
100% {
175+
transform: translateX(100%);
176+
}
177+
}
178+
179+
.streaming-effect {
180+
@apply relative overflow-hidden;
181+
}
182+
183+
.streaming-effect::after {
184+
content: '';
185+
@apply pointer-events-none absolute left-0 top-0 h-full w-full;
186+
background: linear-gradient(
187+
90deg,
188+
rgba(128, 128, 128, 0) 0%,
189+
rgba(128, 128, 128, 0.1) 50%,
190+
rgba(128, 128, 128, 0) 100%
191+
);
192+
animation: code-shimmer 1.5s infinite;
193+
z-index: 10;
194+
}
195+
196+
.dark .streaming-effect::after {
197+
background: linear-gradient(
198+
90deg,
199+
rgba(180, 180, 180, 0) 0%,
200+
rgba(180, 180, 180, 0.1) 50%,
201+
rgba(180, 180, 180, 0) 100%
202+
);
203+
}
168204
}
169205

170206
/* Dark mode error badge styling */
@@ -189,3 +225,23 @@ input[type='search']::-moz-search-cancel-button {
189225
input[type='search']::-ms-clear {
190226
display: none;
191227
}
228+
229+
/* Code Prompt Bar Placeholder Animation */
230+
@keyframes placeholder-pulse {
231+
0%,
232+
100% {
233+
opacity: 0.5;
234+
}
235+
50% {
236+
opacity: 0.8;
237+
}
238+
}
239+
240+
.loading-placeholder::placeholder {
241+
animation: placeholder-pulse 1.5s ease-in-out infinite;
242+
}
243+
244+
/* Dark mode error badge styling */
245+
.dark .bg-red-500 {
246+
@apply bg-red-700;
247+
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import { SendIcon, XIcon } from 'lucide-react'
2+
import { Button } from '@/components/ui/button'
3+
import { Input } from '@/components/ui/input'
4+
import { cn } from '@/lib/utils'
5+
6+
interface CodePromptBarProps {
7+
isVisible: boolean
8+
isLoading: boolean
9+
isStreaming: boolean
10+
promptValue: string
11+
onSubmit: (prompt: string) => void
12+
onCancel: () => void
13+
onChange: (value: string) => void
14+
placeholder?: string
15+
className?: string
16+
}
17+
18+
export function CodePromptBar({
19+
isVisible,
20+
isLoading,
21+
isStreaming,
22+
promptValue,
23+
onSubmit,
24+
onCancel,
25+
onChange,
26+
placeholder = 'Describe the JavaScript code to generate...',
27+
className,
28+
}: CodePromptBarProps) {
29+
if (!isVisible && !isStreaming) {
30+
return null
31+
}
32+
33+
return (
34+
<div
35+
className={cn(
36+
'absolute -top-20 left-0 right-0',
37+
'bg-background rounded-xl shadow-lg border',
38+
'transition-all duration-200 z-9999999',
39+
className
40+
)}
41+
>
42+
<div className="flex items-center gap-2 p-2">
43+
<div className={cn('status-indicator ml-1', isStreaming && 'streaming')} />
44+
45+
<div className="flex-1 relative">
46+
<Input
47+
value={isStreaming ? 'Generating...' : promptValue}
48+
onChange={(e) => !isStreaming && onChange(e.target.value)}
49+
placeholder={placeholder}
50+
className={cn(
51+
'rounded-xl border-0 focus-visible:ring-0 focus-visible:ring-offset-0 text-sm text-foreground placeholder:text-muted-foreground/50',
52+
isStreaming && 'text-primary',
53+
(isLoading || isStreaming) && 'loading-placeholder'
54+
)}
55+
onKeyDown={(e) => {
56+
if (e.key === 'Enter' && !isLoading && !isStreaming && promptValue.trim()) {
57+
onSubmit(promptValue)
58+
} else if (e.key === 'Escape') {
59+
onCancel()
60+
}
61+
}}
62+
disabled={isLoading || isStreaming}
63+
autoFocus={!isStreaming}
64+
/>
65+
{isStreaming && (
66+
<div className="absolute inset-0 w-full h-full overflow-hidden pointer-events-none">
67+
<div className="shimmer-effect" />
68+
</div>
69+
)}
70+
</div>
71+
72+
<Button
73+
variant="ghost"
74+
size="icon"
75+
onClick={onCancel}
76+
className="h-8 w-8 rounded-full text-muted-foreground hover:text-foreground hover:bg-accent/50"
77+
>
78+
<XIcon className="h-4 w-4" />
79+
</Button>
80+
81+
{!isStreaming && (
82+
<Button
83+
variant="ghost"
84+
size="icon"
85+
onClick={() => onSubmit(promptValue)}
86+
className="h-8 w-8 rounded-full text-primary hover:text-primary hover:bg-primary/10"
87+
disabled={isLoading || isStreaming || !promptValue.trim()}
88+
>
89+
<SendIcon className="h-4 w-4" />
90+
</Button>
91+
)}
92+
</div>
93+
94+
<style jsx global>{`
95+
@keyframes shimmer {
96+
0% {
97+
transform: translateX(-100%);
98+
}
99+
100% {
100+
transform: translateX(100%);
101+
}
102+
}
103+
104+
@keyframes smoke-pulse {
105+
0%,
106+
100% {
107+
transform: scale(0.8);
108+
opacity: 0.4;
109+
}
110+
50% {
111+
transform: scale(1.1);
112+
opacity: 0.8;
113+
}
114+
}
115+
116+
.status-indicator {
117+
position: relative;
118+
width: 16px;
119+
height: 16px;
120+
border-radius: 50%;
121+
overflow: hidden;
122+
background-color: hsl(var(--muted-foreground) / 0.5);
123+
transition: background-color 0.3s ease;
124+
}
125+
126+
.status-indicator.streaming {
127+
background-color: transparent;
128+
}
129+
130+
.status-indicator.streaming::before {
131+
content: '';
132+
position: absolute;
133+
inset: 0;
134+
border-radius: 50%;
135+
background: radial-gradient(
136+
circle,
137+
hsl(var(--primary) / 0.7) 0%,
138+
hsl(var(--primary) / 0.2) 60%,
139+
transparent 80%
140+
);
141+
animation: smoke-pulse 1.8s ease-in-out infinite;
142+
}
143+
144+
.shimmer-effect {
145+
position: absolute;
146+
top: 0;
147+
left: 0;
148+
width: 100%;
149+
height: 100%;
150+
background: linear-gradient(
151+
90deg,
152+
rgba(255, 255, 255, 0) 0%,
153+
rgba(255, 255, 255, 0.4) 50%,
154+
rgba(255, 255, 255, 0) 100%
155+
);
156+
animation: shimmer 2s infinite;
157+
}
158+
159+
.dark .shimmer-effect {
160+
background: linear-gradient(
161+
90deg,
162+
rgba(50, 50, 50, 0) 0%,
163+
rgba(80, 80, 80, 0.4) 50%,
164+
rgba(50, 50, 50, 0) 100%
165+
);
166+
}
167+
`}</style>
168+
</div>
169+
)
170+
}

sim/app/w/[id]/components/toolbar/toolbar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export function Toolbar() {
1919
const filteredBlocks = !searchQuery.trim() ? getBlocksByCategory(activeTab) : getAllBlocks()
2020

2121
return filteredBlocks.filter((block) => {
22-
if (block.type === 'starter' || block.hiddenFromSidebar) return false
22+
if (block.type === 'starter' || block.hideFromToolbar) return false
2323

2424
return (
2525
!searchQuery.trim() ||

0 commit comments

Comments
 (0)