Skip to content

Commit 52b5329

Browse files
Fix env vars in promptfiddle (#2084)
Fix #1926 <!-- ELLIPSIS_HIDDEN --> ---- > [!IMPORTANT] > Refactor environment variable handling in BAML playground to improve proxy settings management and user environment variable editing. > > - **Atoms**: > - Add `userEnvVarsAtom` in `atoms.ts` for direct editing of user environment variables. > - Modify `envVarsAtom` to include proxy logic and delegate writes to `userEnvVarsAtom`. > - **UI Components**: > - Update `EnvVars` and `EnvironmentVariablesPanel` components in `env-vars-old.tsx` and `env-vars.tsx` to use `userEnvVarsAtom`. > - Add proxy settings toggle and error handling for proxy updates. > - **RPC and Proxy Logic**: > - Update `VSCodeAPIWrapper` in `vscode.ts` to handle proxy settings and add browser mode handling. > - Modify `WebviewPanelHost` in `WebviewPanelHost.ts` to send updated proxy settings back to the webview. > > <sup>This description was created by </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=BoundaryML%2Fbaml&utm_source=github&utm_medium=referral)<sup> for abeb470. You can [customize](https://app.ellipsis.dev/BoundaryML/settings/summaries) this summary. It will automatically update as commits are pushed.</sup> <!-- ELLIPSIS_HIDDEN -->
1 parent 85f6455 commit 52b5329

5 files changed

Lines changed: 171 additions & 66 deletions

File tree

typescript/playground-common/src/shared/baml-project-panel/atoms.ts

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -214,53 +214,71 @@ export const envKeyValuesAtom = atom(
214214
}
215215
},
216216
)
217+
218+
// Simple atom for user's environment variables (direct editing)
219+
export const userEnvVarsAtom = atom(
220+
(get) => {
221+
const envKeyValues = get(envKeyValuesAtom)
222+
return Object.fromEntries(envKeyValues.map(([k, v]) => [k, v]).filter(([k]) => k !== 'BOUNDARY_PROXY_URL'))
223+
},
224+
(get, set, newEnvVars: Record<string, string>) => {
225+
const envKeyValues = Object.entries(newEnvVars)
226+
set(envKeyValueStorage, envKeyValues)
227+
},
228+
)
229+
230+
// Computed atom that includes proxy logic (for runtime usage)
217231
export const envVarsAtom = atom(
218232
(get) => {
219233
if (typeof window === 'undefined') {
220234
return {}
221235
}
222-
if ((window as any).next?.version) {
223-
// NextJS environment doesnt have vscode settings, and proxy is always enabled
224-
return Object.fromEntries(defaultEnvKeyValues.map(([k, v]) => [k, v]))
225-
} else {
226-
const { proxyEnabled, proxyUrl } = get(proxyUrlAtom)
236+
237+
// Check for Next.js environment
238+
const isNextJs = !!(window as any).next?.version
239+
240+
if (isNextJs) {
241+
// NextJS environment - check proxy settings but use Next.js specific proxy URL
242+
const { proxyEnabled } = get(proxyUrlAtom)
243+
const userEnvVars = get(userEnvVarsAtom)
244+
227245
if (!proxyEnabled) {
228-
// if proxy is not enabled, remove the BOUNDARY_PROXY_URL
229-
const envKeyValues = get(envKeyValuesAtom)
230-
return Object.fromEntries(envKeyValues.map(([k, v]) => [k, v]).filter(([k]) => k !== 'BOUNDARY_PROXY_URL'))
246+
return userEnvVars
231247
}
232248

233-
const envKeyValues = get(envKeyValuesAtom)
234-
if (proxyUrl === undefined) {
235-
return Object.fromEntries(envKeyValues.map(([k, v]) => [k, v]).filter(([k]) => k !== 'BOUNDARY_PROXY_URL'))
236-
}
249+
// Proxy enabled - use Next.js specific proxy URL
250+
const nextJsProxyUrl = window?.location?.origin?.includes('localhost')
251+
? 'https://fiddle-proxy.fly.dev' // localhost development
252+
: 'https://fiddle-proxy.fly.dev' // production
237253

238-
// Check if BOUNDARY_PROXY_URL exists in the env vars.
239-
const hasBoundaryProxyUrl = envKeyValues.some(([k]) => k === 'BOUNDARY_PROXY_URL')
240-
241-
const entries = envKeyValues.map(([k, v]) => {
242-
if (k === 'BOUNDARY_PROXY_URL') {
243-
return [k, proxyUrl]
244-
}
245-
return [k, v]
246-
})
247-
248-
// If proxy is enabled and there's a proxyUrl but no BOUNDARY_PROXY_URL, add it
249-
// TODO: it's likely when proxy is updated we dont update our env vars properly again.
250-
// so we resort to this.
251-
if (proxyEnabled && proxyUrl && !hasBoundaryProxyUrl) {
252-
console.warn(
253-
'⚠️ WARNING: BOUNDARY_PROXY_URL was not found in env vars but proxy is enabled. Adding it automatically.',
254-
)
255-
entries.push(['BOUNDARY_PROXY_URL', proxyUrl])
254+
return {
255+
...userEnvVars,
256+
BOUNDARY_PROXY_URL: nextJsProxyUrl,
256257
}
258+
}
259+
260+
const { proxyEnabled, proxyUrl } = get(proxyUrlAtom)
261+
const userEnvVars = get(userEnvVarsAtom)
257262

258-
return Object.fromEntries(entries.filter((e) => e !== undefined))
263+
if (!proxyEnabled) {
264+
// if proxy is not enabled, just return user vars without BOUNDARY_PROXY_URL
265+
return userEnvVars
266+
}
267+
268+
if (proxyUrl === undefined) {
269+
return userEnvVars
270+
}
271+
272+
// Add or update BOUNDARY_PROXY_URL based on current proxy settings
273+
return {
274+
...userEnvVars,
275+
BOUNDARY_PROXY_URL: proxyUrl,
259276
}
260277
},
278+
// Delegate writes to userEnvVarsAtom to avoid interference
261279
(get, set, newEnvVars: Record<string, string>) => {
262-
const envKeyValues = Object.entries(newEnvVars)
263-
set(envKeyValueStorage, envKeyValues)
280+
const { BOUNDARY_PROXY_URL, ...userVars } = newEnvVars
281+
set(userEnvVarsAtom, userVars)
264282
},
265283
)
266284

typescript/playground-common/src/shared/baml-project-panel/playground-panel/side-bar/env-vars-old.tsx

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,25 @@ import {
2222
XCircle,
2323
} from 'lucide-react'
2424
import { useState } from 'react'
25-
import { envVarsAtom, proxyUrlAtom, requiredEnvVarsAtom } from '../../atoms'
25+
import { envVarsAtom, proxyUrlAtom, requiredEnvVarsAtom, userEnvVarsAtom } from '../../atoms'
26+
import { bamlConfig, type BamlConfigAtom } from '../../../../baml_wasm_web/bamlConfig'
2627
import { cn } from '@/lib/utils'
2728
import { Switch } from '@radix-ui/react-switch'
2829
import { QuestionMarkCircledIcon, QuestionMarkIcon } from '@radix-ui/react-icons'
2930
import { Checkbox } from '@/components/ui/checkbox'
3031
import { vscode } from '../../vscode'
3132

3233
const renderedEnvVarsAtom = atom((get) => {
33-
const envVars = get(envVarsAtom)
34+
const userEnvVars = get(userEnvVarsAtom)
3435
const requiredEnvVars = get(requiredEnvVarsAtom)
3536

36-
const vars = Object.entries(envVars).map(([key, value]) => ({
37+
const vars = Object.entries(userEnvVars).map(([key, value]) => ({
3738
key,
3839
value,
3940
required: requiredEnvVars.includes(key),
4041
}))
4142

42-
const missingVars = requiredEnvVars.filter((envVar) => !(envVar in envVars))
43+
const missingVars = requiredEnvVars.filter((envVar) => !(envVar in userEnvVars))
4344

4445
vars.push(
4546
...missingVars.map((envVar) => ({
@@ -60,9 +61,11 @@ const renderedEnvVarsAtom = atom((get) => {
6061

6162
export default function EnvVars() {
6263
const envVars = useAtomValue(renderedEnvVarsAtom)
63-
const setEnvVars = useSetAtom(envVarsAtom)
64-
const currentEnvVars = useAtomValue(envVarsAtom)
64+
const setUserEnvVars = useSetAtom(userEnvVarsAtom)
65+
const currentUserEnvVars = useAtomValue(userEnvVarsAtom)
6566
const proxySettings = useAtomValue(proxyUrlAtom)
67+
const setBamlConfig = useSetAtom(bamlConfig)
68+
6669
const [editingKey, setEditingKey] = useState<string | null>(null)
6770
const [editValue, setEditValue] = useState('')
6871
const [newKey, setNewKey] = useState('')
@@ -78,7 +81,7 @@ export default function EnvVars() {
7881
const handleSave = async (key: string) => {
7982
try {
8083
await new Promise((resolve) => setTimeout(resolve, 0))
81-
setEnvVars({ ...currentEnvVars, [key]: editValue })
84+
setUserEnvVars({ ...currentUserEnvVars, [key]: editValue })
8285
setEditingKey(null)
8386
toast({
8487
title: 'Environment variable updated',
@@ -110,7 +113,7 @@ export default function EnvVars() {
110113
})
111114
return
112115
}
113-
setEnvVars({ ...currentEnvVars, [newKey]: '' })
116+
setUserEnvVars({ ...currentUserEnvVars, [newKey]: '' })
114117
setNewKey('')
115118
toast({
116119
title: 'New variable added',
@@ -160,8 +163,25 @@ export default function EnvVars() {
160163
VSCode proxy is <b>{proxySettings.proxyEnabled ? 'enabled' : 'disabled'}</b>
161164
<Checkbox
162165
checked={proxySettings.proxyEnabled}
163-
onCheckedChange={() => {
164-
vscode.setProxySettings(!proxySettings.proxyEnabled)
166+
onCheckedChange={async (checked) => {
167+
try {
168+
const response = await vscode.setProxySettings(!!checked)
169+
// Update local config to reflect the change immediately
170+
setBamlConfig((prev: BamlConfigAtom) => ({
171+
...prev,
172+
config: {
173+
...prev.config,
174+
enablePlaygroundProxy: response.enablePlaygroundProxy,
175+
},
176+
}))
177+
} catch (error) {
178+
console.error('Failed to update proxy settings:', error)
179+
toast({
180+
title: 'Error updating proxy settings',
181+
description: 'Please try again',
182+
variant: 'destructive',
183+
})
184+
}
165185
}}
166186
/>
167187
</p>
@@ -202,9 +222,9 @@ export default function EnvVars() {
202222
className='p-0 w-4 h-4'
203223
onClick={(e) => {
204224
e.preventDefault()
205-
const newVars = { ...currentEnvVars }
225+
const newVars = { ...currentUserEnvVars }
206226
delete newVars[key]
207-
setEnvVars(newVars)
227+
setUserEnvVars(newVars)
208228
}}
209229
>
210230
<XCircle className='w-4 h-4 text-muted-foreground hover:text-destructive' />

typescript/playground-common/src/shared/baml-project-panel/playground-panel/side-bar/env-vars.tsx

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { atom, useAtomValue, useSetAtom } from 'jotai'
1717
import { AlertTriangle, Check, Circle, CircleDot, Eye, EyeOff, PlusCircle, Settings2, Trash2 } from 'lucide-react'
1818
import { QuestionMarkCircledIcon } from '@radix-ui/react-icons'
1919
import { useState, useRef, useEffect } from 'react'
20-
import { envVarsAtom, requiredEnvVarsAtom, proxyUrlAtom } from '../../atoms'
20+
import { envVarsAtom, requiredEnvVarsAtom, proxyUrlAtom, userEnvVarsAtom } from '../../atoms'
2121
import { Textarea } from '@/components/ui/textarea'
2222
import { Save, FileText } from 'lucide-react'
2323
import { Label } from '@/components/ui/label'
@@ -26,6 +26,7 @@ import { vscode } from '../../vscode'
2626
import { sortBy } from 'lodash'
2727
import { parse as parseDotenv } from 'dotenv'
2828
import { motion } from 'motion/react'
29+
import { bamlConfig, type BamlConfigAtom } from '../../../../baml_wasm_web/bamlConfig'
2930

3031
const envVarVisibilityAtom = atom<Record<string, boolean>>({})
3132

@@ -39,18 +40,18 @@ interface EnvVarEntry {
3940
}
4041

4142
const renderedEnvVarsAtom = atom<EnvVarEntry[]>((get) => {
42-
const envVars = get(envVarsAtom) as Record<string, string>
43+
const userEnvVars = get(userEnvVarsAtom) as Record<string, string>
4344
const requiredEnvVars = get(requiredEnvVarsAtom)
4445
const visibility = get(envVarVisibilityAtom)
4546

46-
const vars: EnvVarEntry[] = Object.entries(envVars).map(([key, value]) => ({
47+
const vars: EnvVarEntry[] = Object.entries(userEnvVars).map(([key, value]) => ({
4748
key,
4849
value,
4950
required: requiredEnvVars.includes(key),
5051
hidden: visibility[key] !== false, // hidden by default unless explicitly set to false
5152
}))
5253

53-
const missingVars = requiredEnvVars.filter((envVar) => !(envVar in envVars))
54+
const missingVars = requiredEnvVars.filter((envVar) => !(envVar in userEnvVars))
5455

5556
vars.push(
5657
...missingVars.map((envVar) => ({
@@ -130,10 +131,12 @@ function EnvVarStatus({ value, required }: { value?: string; required: boolean }
130131

131132
export const EnvironmentVariablesPanel: React.FC = () => {
132133
const envVars = useAtomValue(renderedEnvVarsAtom)
133-
const setEnvVars = useSetAtom(envVarsAtom)
134+
const setUserEnvVars = useSetAtom(userEnvVarsAtom)
134135
const setVisibility = useSetAtom(envVarVisibilityAtom)
135-
const currentEnvVars = useAtomValue(envVarsAtom)
136+
const currentUserEnvVars = useAtomValue(userEnvVarsAtom)
136137
const proxySettings = useAtomValue(proxyUrlAtom)
138+
const setBamlConfig = useSetAtom(bamlConfig)
139+
137140
const [newKey, setNewKey] = useState('')
138141
const [newValue, setNewValue] = useState('')
139142
const [envFileContent, setEnvFileContent] = useState('')
@@ -149,25 +152,25 @@ export const EnvironmentVariablesPanel: React.FC = () => {
149152

150153
// Update an environment variable immediately
151154
const updateEnvVar = (key: string, value: string) => {
152-
const newVars = { ...currentEnvVars }
155+
const newVars = { ...currentUserEnvVars }
153156
newVars[key] = value
154-
setEnvVars(newVars)
157+
setUserEnvVars(newVars)
155158
}
156159

157160
// Delete an environment variable
158161
const deleteEnvVar = (key: string) => {
159-
const newVars = { ...currentEnvVars }
162+
const newVars = { ...currentUserEnvVars }
160163
delete newVars[key]
161-
setEnvVars(newVars)
164+
setUserEnvVars(newVars)
162165
}
163166

164167
// Add a new environment variable
165168
const addEnvVar = () => {
166169
if (newKey.trim() === '') return
167170

168-
const newVars = { ...currentEnvVars }
171+
const newVars = { ...currentUserEnvVars }
169172
newVars[newKey] = newValue
170-
setEnvVars(newVars)
173+
setUserEnvVars(newVars)
171174

172175
// Reset form
173176
setNewKey('')
@@ -178,11 +181,11 @@ export const EnvironmentVariablesPanel: React.FC = () => {
178181
const parseAndSaveEnvFile = () => {
179182
try {
180183
const parsed = parseDotenv(envFileContent)
181-
const newVars = { ...currentEnvVars }
184+
const newVars = { ...currentUserEnvVars }
182185
Object.entries(parsed).forEach(([key, value]) => {
183186
newVars[key] = value
184187
})
185-
setEnvVars(newVars)
188+
setUserEnvVars(newVars)
186189
setEnvFileContent('')
187190
toast({
188191
title: 'Environment variables imported',
@@ -218,7 +221,7 @@ export const EnvironmentVariablesPanel: React.FC = () => {
218221
</div>
219222
<div className='text-left text-muted-foreground'>
220223
<div className='flex gap-2 items-center'>
221-
<p className='flex gap-2 items-center'>
224+
<div className='flex gap-2 items-center'>
222225
<TooltipProvider delayDuration={300}>
223226
<Tooltip>
224227
<TooltipTrigger asChild>
@@ -242,11 +245,28 @@ export const EnvironmentVariablesPanel: React.FC = () => {
242245
</p>
243246
<Checkbox
244247
checked={proxySettings.proxyEnabled}
245-
onCheckedChange={() => {
246-
vscode.setProxySettings(!proxySettings.proxyEnabled)
248+
onCheckedChange={async (checked) => {
249+
try {
250+
const response = await vscode.setProxySettings(!!checked)
251+
// Update local config to reflect the change immediately
252+
setBamlConfig((prev: BamlConfigAtom) => ({
253+
...prev,
254+
config: {
255+
...prev.config,
256+
enablePlaygroundProxy: response.enablePlaygroundProxy,
257+
},
258+
}))
259+
} catch (error) {
260+
console.error('Failed to update proxy settings:', error)
261+
toast({
262+
title: 'Error updating proxy settings',
263+
description: 'Please try again',
264+
variant: 'destructive',
265+
})
266+
}
247267
}}
248268
/>
249-
</p>
269+
</div>
250270
<p>{proxySettings.proxyUrl}</p>
251271
</div>
252272
</div>
@@ -409,6 +429,8 @@ export const EnvironmentVariablesDialog: React.FC<{
409429
return (
410430
<Dialog open={showEnvDialog} onOpenChange={setShowEnvDialog}>
411431
<DialogContent className='mt-12 max-h-[80vh] overflow-y-auto sm:max-w-none w-fit'>
432+
{/* DialogContent requires DialogTitle error */}
433+
<DialogTitle className='sr-only'>Environment Variables</DialogTitle>
412434
<EnvironmentVariablesPanel />
413435
</DialogContent>
414436
</Dialog>

0 commit comments

Comments
 (0)