From 6376f361a1b5e7272968864beff11f5fd21f58fd Mon Sep 17 00:00:00 2001 From: Tim McMackin Date: Thu, 19 Mar 2026 12:44:59 -0400 Subject: [PATCH 1/5] Do not run automatically when selected; always show Run button --- src/components/APIExplorer.tsx | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/components/APIExplorer.tsx b/src/components/APIExplorer.tsx index 67a9a38..959da72 100644 --- a/src/components/APIExplorer.tsx +++ b/src/components/APIExplorer.tsx @@ -41,7 +41,6 @@ const APIExplorer: React.FC = () => { const [selectedFunctionName, setSelectedFunctionName] = useState('') const [functionParameters, setFunctionParameters] = useState({}) const [apiOutput, setApiOutput] = useState('') - const hasAutoExecutedRef = useRef(false) const hasInitializedRef = useRef(false) // Fetch function code and parameters @@ -128,19 +127,6 @@ const APIExplorer: React.FC = () => { } }, []) - // 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, @@ -181,7 +167,6 @@ const APIExplorer: React.FC = () => { const handleFunctionChange = (value: string) => { setSelectedFunctionName(value) - hasAutoExecutedRef.current = false } const handleParameterChange = (name: string, value: any) => { @@ -331,7 +316,7 @@ const APIExplorer: React.FC = () => { {METHOD_DESCRIPTIONS[selectedExampleCategory][selectedFunctionName]} )} - {parameterNames.length > 0 && ( + {selectedFunctionName && (
{parameterNames.map((name: string, index: number) => From 96699ef028b03c9d58c4265a3393eed645d53005 Mon Sep 17 00:00:00 2001 From: Tim McMackin Date: Thu, 19 Mar 2026 12:52:11 -0400 Subject: [PATCH 2/5] Show console output in extension --- src/components/APIExplorer.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/APIExplorer.tsx b/src/components/APIExplorer.tsx index 959da72..0bf6ac4 100644 --- a/src/components/APIExplorer.tsx +++ b/src/components/APIExplorer.tsx @@ -187,8 +187,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('') @@ -230,13 +230,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) } } } From a5d787c3bfc2353b9de274fbcf8ab143732a7956 Mon Sep 17 00:00:00 2001 From: Tim McMackin Date: Thu, 19 Mar 2026 12:59:28 -0400 Subject: [PATCH 3/5] This onClear does not do anything --- src/components/APIExplorer.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/APIExplorer.tsx b/src/components/APIExplorer.tsx index 0bf6ac4..b5e628d 100644 --- a/src/components/APIExplorer.tsx +++ b/src/components/APIExplorer.tsx @@ -350,7 +350,6 @@ const APIExplorer: React.FC = () => {
setApiOutput('')} code={apiOutput || '// Run the method to see output'} language="javascript" /> From 6b17604275b23dade4ba977b93b16669bbd6ca1c Mon Sep 17 00:00:00 2001 From: Tim McMackin Date: Thu, 19 Mar 2026 13:07:33 -0400 Subject: [PATCH 4/5] Don't format this console output, leave it as plain text --- src/components/APIExplorer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/APIExplorer.tsx b/src/components/APIExplorer.tsx index b5e628d..2589d27 100644 --- a/src/components/APIExplorer.tsx +++ b/src/components/APIExplorer.tsx @@ -351,7 +351,7 @@ const APIExplorer: React.FC = () => {
)} From d2bdda760edc1b2fa41ffeb6c3625f7c92aa7d83 Mon Sep 17 00:00:00 2001 From: Tim McMackin Date: Thu, 19 Mar 2026 13:57:00 -0400 Subject: [PATCH 5/5] Move selected example up a level so the app remembers it --- src/components/APIExplorer.tsx | 41 ++++++++++++---------------------- src/main.jsx | 37 +++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/src/components/APIExplorer.tsx b/src/components/APIExplorer.tsx index 2589d27..9e8cc25 100644 --- a/src/components/APIExplorer.tsx +++ b/src/components/APIExplorer.tsx @@ -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' @@ -36,12 +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 = ({ + selectedExampleCategory, + setSelectedExampleCategory, + selectedFunctionName, + setSelectedFunctionName, +}) => { const [functionParameters, setFunctionParameters] = useState({}) const [apiOutput, setApiOutput] = useState('') - const hasInitializedRef = useRef(false) // Fetch function code and parameters const { functionCode, parameterNames, parameterTypes, setParameterNames } = @@ -105,28 +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) - } - } - }, []) - const exampleCategories: CategoryOption[] = Object.keys(examples).map( (key) => ({ value: key, diff --git a/src/main.jsx b/src/main.jsx index 1bebbf9..cd9d814 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -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' }, @@ -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 @@ -133,7 +161,14 @@ const App = () => { activeTab={activeTab} setActiveTab={setActiveTab} /> - {activeTab === 'api' && } + {activeTab === 'api' && ( + + )} {activeTab === 'code' && }