Skip to content

Commit 66f6566

Browse files
authored
fix: issue with some ids for embed prompt fiddle (#2279)
<!-- ELLIPSIS_HIDDEN --> > [!IMPORTANT] > Fixes URL generation and file handling in embed prompt by preferring existing IDs and updating file structure. > > - **Behavior**: > - `EmbedDialog.tsx`: Modifies URL generation to prefer existing ID from `pathname` over creating a new one. > - `page.tsx`: Loads project using `loadProject` and handles missing project ID or project gracefully. > - **File Handling**: > - `clientwrapper.tsx`: Changes `EmbedComponent` to accept `files` array and updates `EmbedComponentInner` to populate `filesAtom` with project files. > - Replaces `bamlContent` with `files` in `EmbedComponent` and `EmbedComponentInner`. > - **Misc**: > - Updates URL format in `EmbedDialog.tsx` to use `?id=` instead of path-based ID. > > <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 f0701fa. 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 d77bdef commit 66f6566

3 files changed

Lines changed: 52 additions & 78 deletions

File tree

typescript/apps/fiddle-web-app/app/[project_id]/_components/EmbedDialog.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { useAtomValue } from 'jotai';
1919
import { currentEditorFilesAtom } from '../_atoms/atoms';
2020
import { createUrl } from '../../../app/actions';
2121
import type { BAMLProject } from '../../../lib/exampleProjects';
22+
import { usePathname } from 'next/navigation';
2223

2324
const ProjectView = dynamic(() => import('./ProjectView'), { ssr: false });
2425

@@ -40,27 +41,33 @@ export function EmbedDialog({
4041
const [activeTab, setActiveTab] = useState('link');
4142
const [generatedUrl, setGeneratedUrl] = useState('');
4243
const editorFiles = useAtomValue(currentEditorFilesAtom);
44+
const pathname = usePathname();
4345

4446
useEffect(() => {
4547
if (!open) return;
4648
let cancelled = false;
4749
(async () => {
4850
try {
4951
if (typeof window === 'undefined') return;
50-
// Always create a fresh share URL based on current editor state
51-
const urlId = await createUrl({
52-
...project,
53-
name: projectName,
54-
files: editorFiles,
55-
} as BAMLProject);
52+
53+
// Prefer existing id from URL, otherwise create a new one like the Share button
54+
let urlId = pathname?.split('/')[1];
55+
if (!urlId || urlId === 'new-project') {
56+
urlId = await createUrl({
57+
...project,
58+
name: projectName,
59+
files: editorFiles,
60+
} as BAMLProject);
61+
}
62+
5663
if (!cancelled) {
57-
setGeneratedUrl(`${window.location.origin}/embed/${urlId}`);
64+
setGeneratedUrl(`${window.location.origin}/embed?id=${urlId}`);
5865
}
5966
} catch (e) {
6067
// Fallback to provided shareId if creation fails
6168
if (!cancelled) {
6269
if (shareId && typeof window !== 'undefined') {
63-
setGeneratedUrl(`${window.location.origin}/embed/${shareId}`);
70+
setGeneratedUrl(`${window.location.origin}/embed?id=${shareId}`);
6471
} else {
6572
setGeneratedUrl('');
6673
}
@@ -70,7 +77,7 @@ export function EmbedDialog({
7077
return () => {
7178
cancelled = true;
7279
};
73-
}, [open, project, projectName, editorFiles, shareId]);
80+
}, [open, project, projectName, editorFiles, shareId, pathname]);
7481

7582
return (
7683
<Dialog open={open} onOpenChange={onOpenChange}>

typescript/apps/fiddle-web-app/app/embed/clientwrapper.tsx

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -77,34 +77,39 @@ const EventListener: React.FC = () => {
7777
};
7878

7979

80+
type EditorFile = { path: string; content: string };
81+
8082
interface EmbedComponentProps {
81-
bamlContent: string;
83+
files: EditorFile[];
8284
}
8385

84-
export default function EmbedComponent({ bamlContent }: EmbedComponentProps) {
86+
export default function EmbedComponent({ files }: EmbedComponentProps) {
8587
return (
8688
<JotaiProvider>
87-
<EmbedComponentInner bamlContent={bamlContent} />
89+
<EmbedComponentInner files={files} />
8890
</JotaiProvider>
8991
);
9092
}
9193

92-
function EmbedComponentInner({ bamlContent }: EmbedComponentProps) {
93-
const [files, setFiles] = useAtom(filesAtom);
94+
function EmbedComponentInner({ files }: EmbedComponentProps) {
95+
const [editorFiles, setEditorFiles] = useAtom(filesAtom);
9496
const [isLoading, setIsLoading] = useState(true);
9597
const isWasmReady = useWaitForWasm();
9698
const activeFileNameAtomValue = useAtomValue(activeFileNameAtom);
9799

98100
// Use fallback active file name when WASM is not ready
99-
const activeFileName = isWasmReady ? activeFileNameAtomValue : 'main.baml';
101+
const fallbackFileName = files.find((f) => f.path.endsWith('.baml'))?.path || 'main.baml';
102+
const activeFileName = isWasmReady ? activeFileNameAtomValue : fallbackFileName;
100103

101104
useEffect(() => {
102-
// Set the files with the BAML content passed from the server
103-
setFiles({
104-
'main.baml': bamlContent,
105-
});
105+
// Populate files atom from provided project files
106+
const record: Record<string, string> = {};
107+
for (const f of files) {
108+
record[f.path] = f.content;
109+
}
110+
setEditorFiles(record);
106111
setIsLoading(false);
107-
}, [bamlContent, setFiles]);
112+
}, [files, setEditorFiles]);
108113

109114
// Wait for WASM to be ready before rendering
110115
if (isLoading || !isWasmReady) {
@@ -127,20 +132,19 @@ function EmbedComponentInner({ bamlContent }: EmbedComponentProps) {
127132
{activeFileName && (
128133
<CodeMirrorViewer
129134
lang="baml"
130-
fileContent={{
131-
code: files[activeFileName] || '',
135+
fileContent={{
136+
code: editorFiles[activeFileName] || '',
132137
language: 'baml',
133138
id: activeFileName,
134139
}}
135140
hideLineNumbers={true}
136141
shouldScrollDown={false}
137-
onContentChange={(v: string) => {
142+
onContentChange={(v: string) => {
138143
const newFiles: Record<string, string> = {};
139-
Object.entries(files).map(([key, value]) => {
140-
const newVal = key === activeFileName ? v : value;
141-
newFiles[key] = newVal;
144+
Object.entries(editorFiles).forEach(([key, value]) => {
145+
newFiles[key] = key === activeFileName ? v : value;
142146
});
143-
setFiles(newFiles);
147+
setEditorFiles(newFiles);
144148
}}
145149
/>
146150
)}

typescript/apps/fiddle-web-app/app/embed/page.tsx

Lines changed: 14 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,30 @@
11
import dynamic from 'next/dynamic'
2-
import fs from 'fs/promises'
3-
import path from 'path'
2+
import { loadProject } from '../../lib/loadProject'
43

5-
const PromptPreview = dynamic(() => import('./clientwrapper'), {})
6-
7-
// Function to load BAML file content from the file system
8-
async function loadBamlFile(exampleName: string): Promise<string> {
9-
try {
10-
// Sanitize the example name to prevent directory traversal attacks
11-
const sanitizedExampleName = exampleName.replace(/[^a-zA-Z0-9-_]/g, '')
12-
if (sanitizedExampleName !== exampleName) {
13-
console.warn(`Example name was sanitized from ${exampleName} to ${sanitizedExampleName}`)
14-
}
15-
16-
const filePath = path.join(process.cwd(), 'public', '_docs', sanitizedExampleName, 'baml_src', 'main.baml')
17-
18-
// Check if the file exists
19-
try {
20-
await fs.access(filePath)
21-
} catch (error) {
22-
console.warn(`BAML file not found for example ${sanitizedExampleName}, falling back to default example`)
23-
return loadBamlFile('default-example')
24-
}
25-
26-
return await fs.readFile(filePath, 'utf-8')
27-
} catch (error) {
28-
console.error(`Error loading BAML file for example ${exampleName}:`, error)
29-
// Return default BAML content if all else fails
30-
return `
31-
function Hi() -> string {
32-
client "openai/gpt-4o"
33-
prompt #"
34-
hi there
35-
"#
36-
}
37-
38-
test HiTest {
39-
functions [Hi]
40-
args {
41-
42-
}
43-
}
44-
`
45-
}
46-
}
4+
const ClientWrapper = dynamic(() => import('./clientwrapper'), {})
475

486
export default async function EmbedComponent({
497
searchParams,
508
}: {
51-
searchParams: Promise<{ id: string }>
9+
searchParams: Promise<{ id?: string }>
5210
}) {
5311
const params = await searchParams
54-
// Get example name from URL parameters, default to 'default-example' if not provided
55-
const exampleName = typeof params.id === 'string' ? params.id : 'default-example'
56-
console.log('exampleName', exampleName)
12+
const id = typeof params.id === 'string' ? params.id : undefined
13+
14+
if (!id) {
15+
return <div className='flex items-center justify-center w-screen h-screen'>No project id provided</div>
16+
}
5717

58-
// Load the BAML file content
59-
const bamlContent = await loadBamlFile(exampleName)
18+
const project = await loadProject(Promise.resolve({ project_id: id }))
19+
20+
if (!project) {
21+
return <div className='flex items-center justify-center w-screen h-screen'>No project found</div>
22+
}
6023

6124
return (
6225
<div className='flex justify-center items-center h-screen rounded-lg border-2 border-purple-900/30 overflow-y-clip'>
6326
<div className='flex w-full h-full'>
64-
<PromptPreview bamlContent={bamlContent} />
27+
<ClientWrapper files={project.files} />
6528
</div>
6629
</div>
6730
)

0 commit comments

Comments
 (0)