From cf3c0ec470e68bf92d17ce0d163479e3fe18343b Mon Sep 17 00:00:00 2001 From: "lorenzo.neumann" <36760115+ln-12@users.noreply.github.com> Date: Thu, 28 Aug 2025 12:20:27 +0200 Subject: [PATCH 01/34] added copy functionality to JsonView component --- client/src/components/JsonView.tsx | 102 ++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 3 deletions(-) diff --git a/client/src/components/JsonView.tsx b/client/src/components/JsonView.tsx index e9ef0d2d..575b1375 100644 --- a/client/src/components/JsonView.tsx +++ b/client/src/components/JsonView.tsx @@ -1,4 +1,5 @@ import { useState, memo, useMemo, useCallback, useEffect } from "react"; +import type React from "react"; import type { JsonValue } from "@/utils/jsonUtils"; import clsx from "clsx"; import { Copy, CheckCheck } from "lucide-react"; @@ -114,6 +115,7 @@ const JsonNode = memo( initialExpandDepth, isError = false, }: JsonNodeProps) => { + const { toast } = useToast(); const [isExpanded, setIsExpanded] = useState(depth < initialExpandDepth); const [typeStyleMap] = useState>({ number: "text-blue-600", @@ -126,6 +128,52 @@ const JsonNode = memo( }); const dataType = getDataType(data); + const [copied, setCopied] = useState(false); + useEffect(() => { + let timeoutId: NodeJS.Timeout; + if (copied) { + timeoutId = setTimeout(() => setCopied(false), 500); + } + return () => { + if (timeoutId) clearTimeout(timeoutId); + }; + }, [copied]); + + const handleCopyValue = useCallback( + (value: JsonValue) => { + try { + let text: string; + const valueType = getDataType(value); + switch (valueType) { + case "string": + text = value as unknown as string; + break; + case "number": + case "boolean": + text = String(value); + break; + case "null": + text = "null"; + break; + case "undefined": + text = "undefined"; + break; + default: + text = JSON.stringify(value); + } + navigator.clipboard.writeText(text); + setCopied(true); + } catch (error) { + toast({ + title: "Error", + description: `There was an error coping result into the clipboard: ${error instanceof Error ? error.message : String(error)}`, + variant: "destructive", + }); + } + }, + [toast], + ); + const renderCollapsible = (isArray: boolean) => { const items = isArray ? (data as JsonValue[]) @@ -219,7 +267,7 @@ const JsonNode = memo( if (!isTooLong) { return ( -
+
{name && ( {name}: @@ -233,12 +281,28 @@ const JsonNode = memo( > "{value}" +
); } return ( -
+
{name && ( {name}: @@ -254,6 +318,22 @@ const JsonNode = memo( > {isExpanded ? `"${value}"` : `"${value.slice(0, maxLength)}..."`} +
); }; @@ -266,7 +346,7 @@ const JsonNode = memo( return renderString(data as string); default: return ( -
+
{name && ( {name}: @@ -275,6 +355,22 @@ const JsonNode = memo( {data === null ? "null" : String(data)} +
); } From 6b26dd3fda874d8beb7f641444d260d9f1134361 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Thu, 28 Aug 2025 16:09:04 -0700 Subject: [PATCH 02/34] Fix issue 766 --- client/src/components/PromptsTab.tsx | 15 ++++++++++++--- client/src/components/ui/combobox.tsx | 14 +++++++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/client/src/components/PromptsTab.tsx b/client/src/components/PromptsTab.tsx index fa590558..0df36bb8 100644 --- a/client/src/components/PromptsTab.tsx +++ b/client/src/components/PromptsTab.tsx @@ -63,9 +63,7 @@ const PromptsTab = ({ clearCompletions(); }, [clearCompletions, selectedPrompt]); - const handleInputChange = async (argName: string, value: string) => { - setPromptArgs((prev) => ({ ...prev, [argName]: value })); - + const triggerCompletions = (argName: string, value: string) => { if (selectedPrompt) { requestCompletions( { @@ -79,6 +77,16 @@ const PromptsTab = ({ } }; + const handleInputChange = async (argName: string, value: string) => { + setPromptArgs((prev) => ({ ...prev, [argName]: value })); + triggerCompletions(argName, value); + }; + + const handleFocus = async (argName: string) => { + const currentValue = promptArgs[argName] || ""; + triggerCompletions(argName, currentValue); + }; + const handleGetPrompt = () => { if (selectedPrompt) { getPrompt(selectedPrompt.name, promptArgs); @@ -143,6 +151,7 @@ const PromptsTab = ({ onInputChange={(value) => handleInputChange(arg.name, value) } + onFocus={() => handleFocus(arg.name)} options={completions[arg.name] || []} /> diff --git a/client/src/components/ui/combobox.tsx b/client/src/components/ui/combobox.tsx index 02624086..9e6227b1 100644 --- a/client/src/components/ui/combobox.tsx +++ b/client/src/components/ui/combobox.tsx @@ -19,6 +19,7 @@ interface ComboboxProps { value: string; onChange: (value: string) => void; onInputChange: (value: string) => void; + onFocus?: () => void; options: string[]; placeholder?: string; emptyMessage?: string; @@ -29,6 +30,7 @@ export function Combobox({ value, onChange, onInputChange, + onFocus, options = [], placeholder = "Select...", emptyMessage = "No results found.", @@ -36,6 +38,16 @@ export function Combobox({ }: ComboboxProps) { const [open, setOpen] = React.useState(false); + const handleOpenChange = React.useCallback( + (newOpen: boolean) => { + setOpen(newOpen); + if (newOpen && onFocus) { + onFocus(); + } + }, + [onFocus], + ); + const handleSelect = React.useCallback( (option: string) => { onChange(option); @@ -52,7 +64,7 @@ export function Combobox({ ); return ( - +