From 36c276a6d7171f47bbfe8e39358ba8ec0fcff105 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Sat, 30 May 2026 12:54:58 -0300 Subject: [PATCH] feat(playground): Compare freeze fix, Chat provider/model selects, Build wizard (Phase 4) - Chat (StudioConfigPane): add Provider + Model selects reusing the translator hooks; order Endpoint -> Provider -> Model; ConfigState gains optional provider. - Compare (CompareTab): add a user-prompt input and include it in the request body; throttle per-column stream updates via requestAnimationFrame to stop the UI freeze (was setColumns per chunk x N columns with an empty user message). - Build (BuildTab + build/BuildWizard): redesign as a guided 3-step wizard (What to test -> Configure -> Run) reusing ToolsBuilder/StructuredOutputEditor; all run/tool-call handlers preserved. - i18n: playground.build.* (18 keys) in en + pt-BR. --- .../components/StudioConfigPane.tsx | 59 +++- .../playground/components/tabs/BuildTab.tsx | 250 ++++++--------- .../playground/components/tabs/CompareTab.tsx | 58 +++- .../components/tabs/build/BuildWizard.tsx | 294 ++++++++++++++++++ src/i18n/messages/en.json | 22 +- src/i18n/messages/pt-BR.json | 22 +- tests/unit/v388-phase4-playground.test.ts | 41 +++ 7 files changed, 570 insertions(+), 176 deletions(-) create mode 100644 src/app/(dashboard)/dashboard/playground/components/tabs/build/BuildWizard.tsx create mode 100644 tests/unit/v388-phase4-playground.test.ts diff --git a/src/app/(dashboard)/dashboard/playground/components/StudioConfigPane.tsx b/src/app/(dashboard)/dashboard/playground/components/StudioConfigPane.tsx index decb51ed73..20440a705a 100644 --- a/src/app/(dashboard)/dashboard/playground/components/StudioConfigPane.tsx +++ b/src/app/(dashboard)/dashboard/playground/components/StudioConfigPane.tsx @@ -8,11 +8,14 @@ import type { PlaygroundEndpoint } from "@/lib/playground/codeExport"; import { endpointToPath } from "@/lib/playground/codeExport"; import PresetPicker from "./PresetPicker"; import ImprovePromptButton from "./ImprovePromptButton"; +import { useProviderOptions } from "@/app/(dashboard)/dashboard/translator/hooks/useProviderOptions"; +import { useAvailableModels } from "@/app/(dashboard)/dashboard/translator/hooks/useAvailableModels"; export interface ConfigState { endpoint: PlaygroundEndpoint; baseUrl: string; model: string; + provider?: string; systemPrompt: string; params: PlaygroundParams; } @@ -46,6 +49,10 @@ const ENDPOINT_OPTIONS: Array<{ value: PlaygroundEndpoint; label: string }> = [ */ export default function StudioConfigPane({ configState, setConfigState }: StudioConfigPaneProps) { const [collapsed, setCollapsed] = useState(false); + const { provider, setProvider, providerOptions, loading: loadingProviders } = useProviderOptions( + configState.provider ?? "" + ); + const { availableModels, loading: loadingModels } = useAvailableModels(); function update(key: K, value: ConfigState[K]) { setConfigState({ ...configState, [key]: value }); @@ -108,18 +115,56 @@ export default function StudioConfigPane({ configState, setConfigState }: Studio + {/* Provider */} +
+ + +
+ {/* Model */}
- update("model", e.target.value)} - placeholder="e.g. openai/gpt-4o" - className="w-full text-xs bg-surface border border-border rounded px-2 py-1.5 focus:outline-none focus:ring-1 focus:ring-primary text-text-main" - /> + {availableModels.length > 0 ? ( + + ) : ( + update("model", e.target.value)} + placeholder="e.g. openai/gpt-4o" + className="w-full text-xs bg-surface border border-border rounded px-2 py-1.5 focus:outline-none focus:ring-1 focus:ring-primary text-text-main" + /> + )}
{/* System prompt */} diff --git a/src/app/(dashboard)/dashboard/playground/components/tabs/BuildTab.tsx b/src/app/(dashboard)/dashboard/playground/components/tabs/BuildTab.tsx index 40bfb78c98..a3a5c521a1 100644 --- a/src/app/(dashboard)/dashboard/playground/components/tabs/BuildTab.tsx +++ b/src/app/(dashboard)/dashboard/playground/components/tabs/BuildTab.tsx @@ -6,9 +6,8 @@ import { useRef, useState } from "react"; import { useTranslations } from "next-intl"; import { useToolsBuilder } from "../../hooks/useToolsBuilder"; import { useStructuredOutput } from "../../hooks/useStructuredOutput"; -import ToolsBuilder from "../ToolsBuilder"; -import StructuredOutputEditor from "../StructuredOutputEditor"; import MarkdownMessage from "../MarkdownMessage"; +import BuildWizard from "./build/BuildWizard"; import type { ConfigState } from "../StudioConfigPane"; interface BuildTabProps { @@ -225,177 +224,104 @@ export default function BuildTab({ configState }: BuildTabProps) { await runRequest(newMessages); } - function clearConversation() { - setMessages([]); - setToolCalls([]); - setToolResultDrafts([]); - setValidationResult(null); - setPrompt(""); - } - - return ( -
- {/* Left panel: conversation + run */} -
- {/* Toolbar */} -
- - - {messages.length > 0 && ( - - )} - -
- {toolsBuilder.tools.length > 0 && ( - - {toolsBuilder.tools.length} tool{toolsBuilder.tools.length !== 1 ? "s" : ""} - - )} - {structuredOutput.enabled && ( - - JSON mode - + {msg.role === "user" ? ( + {msg.content} + ) : ( + )}
+ ))} - {/* Conversation history */} -
- {messages.map((msg, idx) => ( -
+ {/* Tool call UI */} + {toolCalls.length > 0 && ( +
+ {toolCalls.map((tc) => { + const draft = toolResultDrafts.find((d) => d.toolCallId === tc.id); + return (
- {msg.role === "user" ? ( - {msg.content} - ) : ( - - )} -
-
- ))} - - {/* Tool call UI */} - {toolCalls.length > 0 && ( -
- {toolCalls.map((tc) => { - const draft = toolResultDrafts.find((d) => d.toolCallId === tc.id); - return ( -
+ + function + + + {tc.function.name} + +
+
+                  {tc.function.arguments}
+                
+
+ +