Skip to content
Open
Show file tree
Hide file tree
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
75 changes: 26 additions & 49 deletions src/components/APIExplorer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef } from 'react'
import React, { useState, useEffect } from 'react'
import { useQuery } from '@tanstack/react-query'
import Prism from 'prismjs'
import 'prismjs/components/prism-typescript.js'
Expand Down Expand Up @@ -36,13 +36,21 @@ import designerExtensionTypings from '@/designer-extension-typings/index.d.ts?ra

const examples: { [key: string]: any } = examplesImport as any

const APIExplorer: React.FC = () => {
const [selectedExampleCategory, setSelectedExampleCategory] = useState('')
const [selectedFunctionName, setSelectedFunctionName] = useState('')
interface APIExplorerProps {
selectedExampleCategory: string
setSelectedExampleCategory: (value: string) => void
selectedFunctionName: string
setSelectedFunctionName: (value: string) => void
}

const APIExplorer: React.FC<APIExplorerProps> = ({
selectedExampleCategory,
setSelectedExampleCategory,
selectedFunctionName,
setSelectedFunctionName,
}) => {
const [functionParameters, setFunctionParameters] = useState<ParameterMap>({})
const [apiOutput, setApiOutput] = useState('')
const hasAutoExecutedRef = useRef(false)
const hasInitializedRef = useRef(false)

// Fetch function code and parameters
const { functionCode, parameterNames, parameterTypes, setParameterNames } =
Expand Down Expand Up @@ -106,41 +114,6 @@ const APIExplorer: React.FC = () => {
setApiOutput('')
}, [selectedFunctionName, selectedExampleCategory])

// Auto-initialize first example and function
useEffect(() => {
if (!hasInitializedRef.current) {
hasInitializedRef.current = true
const firstCategory = Object.keys(examples)[0]
setSelectedExampleCategory(firstCategory)

const categoryContent = examples[firstCategory] || {}
const firstSubcategory = Object.keys(categoryContent)[0]

if (
typeof categoryContent[firstSubcategory] === 'object' &&
!('type' in (categoryContent[firstSubcategory] || {}))
) {
const firstFunction = Object.keys(categoryContent[firstSubcategory])[0]
setSelectedFunctionName(`${firstSubcategory}.${firstFunction}`)
} else {
setSelectedFunctionName(firstSubcategory)
}
}
}, [])

// Auto-run function if no parameters needed
useEffect(() => {
if (
selectedFunctionName &&
parameterNames.length === 0 &&
selectedExampleCategory &&
!hasAutoExecutedRef.current
) {
hasAutoExecutedRef.current = true
handleFunctionExecution()
}
}, [selectedFunctionName, selectedExampleCategory, parameterNames])

const exampleCategories: CategoryOption[] = Object.keys(examples).map(
(key) => ({
value: key,
Expand Down Expand Up @@ -181,7 +154,6 @@ const APIExplorer: React.FC = () => {

const handleFunctionChange = (value: string) => {
setSelectedFunctionName(value)
hasAutoExecutedRef.current = false
}

const handleParameterChange = (name: string, value: any) => {
Expand All @@ -202,8 +174,8 @@ const APIExplorer: React.FC = () => {
: topCategory[selectedFunctionName]

if (funcToExecute) {
// Save original console before try block so it's accessible in finally
const originalConsole = { ...console }

try {
setApiOutput('')

Expand Down Expand Up @@ -245,13 +217,19 @@ const APIExplorer: React.FC = () => {
if (typeof funcToExecute === 'function') {
const result = funcToExecute(...paramValues)
if (result && typeof result.then === 'function') {
result.catch(apiConsole.error)
// Restore console after the async function completes, not before
result
.catch(apiConsole.error)
.finally(() => Object.assign(console, originalConsole))
} else {
Object.assign(console, originalConsole)
}
} else {
Object.assign(console, originalConsole)
}
} catch (error) {
console.error('Error executing function:', error)
} finally {
Object.assign(console, originalConsole)
console.error('Error executing function:', error)
}
}
}
Expand Down Expand Up @@ -331,7 +309,7 @@ const APIExplorer: React.FC = () => {
{METHOD_DESCRIPTIONS[selectedExampleCategory][selectedFunctionName]}
</div>
)}
{parameterNames.length > 0 && (
{selectedFunctionName && (
<div className="flex-row items-end gap-2">
<div className="flex-1">
{parameterNames.map((name: string, index: number) =>
Expand Down Expand Up @@ -359,9 +337,8 @@ const APIExplorer: React.FC = () => {
<label className="w-form-label">Output</label>
</div>
<CodeBlock
onClear={() => setApiOutput('')}
code={apiOutput || '// Run the method to see output'}
language="javascript"
language="none"
/>
</div>
)}
Expand Down
37 changes: 36 additions & 1 deletion src/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import { loader } from '@monaco-editor/react'
import Playground from './components/Playground'
import TabNavigation from './components/TabNavigation'
import APIExplorer from './components/APIExplorer'
import examplesImport from './examples/examples'

const examples = examplesImport

const TABS = [
{ key: 'api', label: 'API Explorer' },
Expand All @@ -26,13 +29,38 @@ const queryClient = new QueryClient({

const App = () => {
const [activeTab, setActiveTab] = useState('api')
const [selectedExampleCategory, setSelectedExampleCategory] = useState('')
const [selectedFunctionName, setSelectedFunctionName] = useState('')
const containerRef = useRef(null)
const hasInitializedRef = useRef(false)

useEffect(() => {
// Set initial size
webflow.setExtensionSize({ height: 425, width: 500 })
}, [])

// Auto-initialize first example and function
useEffect(() => {
if (!hasInitializedRef.current) {
hasInitializedRef.current = true
const firstCategory = Object.keys(examples)[0]
setSelectedExampleCategory(firstCategory)

const categoryContent = examples[firstCategory] || {}
const firstSubcategory = Object.keys(categoryContent)[0]

if (
typeof categoryContent[firstSubcategory] === 'object' &&
!('type' in (categoryContent[firstSubcategory] || {}))
) {
const firstFunction = Object.keys(categoryContent[firstSubcategory])[0]
setSelectedFunctionName(`${firstSubcategory}.${firstFunction}`)
} else {
setSelectedFunctionName(firstSubcategory)
}
}
}, [])

// Global error handler for Monaco Editor cancellation errors
useEffect(() => {
// Pre-initialize Monaco to avoid first-load cancellation during StrictMode double-mount
Expand Down Expand Up @@ -133,7 +161,14 @@ const App = () => {
activeTab={activeTab}
setActiveTab={setActiveTab}
/>
{activeTab === 'api' && <APIExplorer />}
{activeTab === 'api' && (
<APIExplorer
selectedExampleCategory={selectedExampleCategory}
setSelectedExampleCategory={setSelectedExampleCategory}
selectedFunctionName={selectedFunctionName}
setSelectedFunctionName={setSelectedFunctionName}
/>
)}
{activeTab === 'code' && <Playground />}
<footer className="wf-footer">
<img
Expand Down