Skip to content

Commit f0170ea

Browse files
committed
Enhance Playground components with output handling improvements and integrate SynthLang execution in documentation
1 parent 074986b commit f0170ea

11 files changed

+416
-186
lines changed

src/components/Documentation/CodeExample.tsx

+56-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import { Copy, Play, Check, AlertTriangle } from "lucide-react";
1+
import { Copy, Play, Check, AlertTriangle, Loader2 } from "lucide-react";
22
import { useState, useCallback } from "react";
33
import { usePlaygroundContext } from "./PlaygroundContext";
44
import { useSynthLang } from "./useSynthLang";
5+
import { PlaygroundOutput } from "./PlaygroundContext";
6+
import { useSettingsContext } from "../../services/settingsService";
7+
import { callOpenRouter } from "../../services/openRouterService";
58

69
interface CodeExampleProps {
710
code: string;
@@ -18,8 +21,11 @@ export const CodeExample = ({
1821
}: CodeExampleProps) => {
1922
const [copied, setCopied] = useState(false);
2023
const [error, setError] = useState<string | null>(null);
24+
const [isRunning, setIsRunning] = useState(false);
25+
const [exampleOutput, setExampleOutput] = useState<string | null>(null);
2126
const { loadExample, isLoading } = usePlaygroundContext();
2227
const { highlightSyntax, validateSynthLang } = useSynthLang();
28+
const { settings } = useSettingsContext();
2329

2430
const handleCopy = useCallback(async () => {
2531
try {
@@ -35,6 +41,8 @@ export const CodeExample = ({
3541
const handleTryIt = useCallback(async () => {
3642
try {
3743
setError(null);
44+
setIsRunning(true);
45+
setExampleOutput("");
3846

3947
// Only validate if it's SynthLang code
4048
if (language === "synthlang") {
@@ -51,12 +59,38 @@ export const CodeExample = ({
5159
}
5260
}
5361

62+
// Call OpenRouter if API key is available
63+
if (settings.openRouterApiKey) {
64+
await callOpenRouter(
65+
code,
66+
{
67+
model: settings.defaultModel,
68+
temperature: 0.7,
69+
maxTokens: 1000,
70+
autoFormat: true,
71+
syntaxHighlighting: true,
72+
showLineNumbers: true
73+
},
74+
settings.openRouterApiKey,
75+
'interpreter',
76+
undefined,
77+
(chunk) => {
78+
setExampleOutput(prev => (prev || "") + chunk);
79+
}
80+
);
81+
} else {
82+
throw new Error("OpenRouter API key not found. Please add your API key in Settings.");
83+
}
84+
85+
// Also load into playground for reference
5486
await loadExample(code);
5587
} catch (err) {
5688
setError(err instanceof Error ? err.message : "Failed to load example");
5789
setTimeout(() => setError(null), 5000);
90+
} finally {
91+
setIsRunning(false);
5892
}
59-
}, [code, language, loadExample, validateSynthLang]);
93+
}, [code, language, loadExample, validateSynthLang, settings]);
6094

6195
return (
6296
<div className="space-y-2">
@@ -74,48 +108,57 @@ export const CodeExample = ({
74108
{description && (
75109
<p className="text-sm text-muted-foreground">{description}</p>
76110
)}
77-
<div className="relative group">
111+
<div className="relative group pt-8">
78112
<div className="absolute top-2 right-2 flex items-center gap-2 opacity-0
79113
group-hover:opacity-100 transition-opacity">
80114
<button
81115
onClick={handleTryIt}
82-
className="p-1.5 bg-black/20 hover:bg-black/30 rounded-md
83-
transition-colors flex items-center gap-1.5"
116+
className={`p-1.5 rounded-md transition-colors flex items-center gap-1.5
117+
${isRunning || isLoading ? 'bg-purple-500/30 cursor-not-allowed' : 'bg-black/20 hover:bg-black/30'}`}
84118
title="Try in playground"
85-
disabled={isLoading}
119+
disabled={isRunning || isLoading}
86120
>
87-
<Play className={`w-3.5 h-3.5 ${isLoading ? 'text-muted-foreground' : 'text-purple-400'}`} />
121+
{isRunning ? (
122+
<Loader2 className="w-3.5 h-3.5 text-purple-400 animate-spin" />
123+
) : (
124+
<Play className={`w-3.5 h-3.5 ${isLoading ? 'text-muted-foreground' : 'text-purple-400'}`} />
125+
)}
88126
<span className={`text-xs ${isLoading ? 'text-muted-foreground' : 'text-purple-400'}`}>
89-
{isLoading ? 'Loading...' : 'Try it'}
127+
{isRunning ? 'Running...' : isLoading ? 'Loading...' : 'Try it'}
90128
</span>
91129
</button>
92130
<button
93131
onClick={handleCopy}
94132
className="p-1.5 bg-black/20 hover:bg-black/30 rounded-md transition-colors"
95133
title="Copy code"
96-
disabled={isLoading}
134+
disabled={isRunning || isLoading}
97135
>
98136
{copied ? (
99137
<Check className="w-3.5 h-3.5 text-green-400" />
100138
) : (
101-
<Copy className={`w-3.5 h-3.5 ${isLoading ? 'text-muted-foreground' : 'text-muted-foreground hover:text-purple-400'}`} />
139+
<Copy className={`w-3.5 h-3.5 ${isRunning || isLoading ? 'text-muted-foreground' : 'text-muted-foreground hover:text-purple-400'}`} />
102140
)}
103141
</button>
104142
</div>
105-
<div className="rounded-lg border border-border/40 bg-black/20 overflow-hidden">
143+
<div className="rounded-lg border border-border/40 bg-black/20">
106144
{language === "synthlang" ? (
107145
<pre
108-
className="p-4 font-mono text-sm overflow-x-auto"
146+
className="p-4 font-mono text-sm whitespace-pre-wrap break-words"
109147
dangerouslySetInnerHTML={{
110148
__html: highlightSyntax(code)
111149
}}
112150
/>
113151
) : (
114-
<pre className="p-4 font-mono text-sm overflow-x-auto">
152+
<pre className="p-4 font-mono text-sm whitespace-pre-wrap break-words">
115153
<code>{code}</code>
116154
</pre>
117155
)}
118156
</div>
157+
{exampleOutput && (
158+
<div className="mt-4">
159+
<PlaygroundOutput>{exampleOutput}</PlaygroundOutput>
160+
</div>
161+
)}
119162
</div>
120163
</div>
121164
);

src/components/Documentation/Playground.tsx

+28-26
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export const Playground = () => {
9393
</div>
9494

9595
<div className="grid grid-cols-1 md:grid-cols-2 divide-y md:divide-y-0 md:divide-x divide-border/40">
96-
<div className="relative p-4">
96+
<div className="relative p-4 min-h-[800px]">
9797
<div
9898
className="absolute inset-0 pointer-events-none p-4 font-mono text-sm"
9999
dangerouslySetInnerHTML={{ __html: highlightedCode }}
@@ -103,7 +103,7 @@ export const Playground = () => {
103103
value={code}
104104
onChange={(e) => setCode(e.target.value)}
105105
onKeyDown={handleKeyDown}
106-
className="w-full h-full min-h-[400px] bg-transparent border-none
106+
className="w-full h-full min-h-[800px] bg-transparent border-none
107107
outline-none resize-none font-mono text-sm text-transparent
108108
caret-white disabled:cursor-not-allowed"
109109
spellCheck={false}
@@ -113,33 +113,35 @@ export const Playground = () => {
113113
/>
114114
</div>
115115

116-
<div className="p-4 bg-black/10 min-h-[400px]">
117-
{isLoading ? (
118-
<PlaygroundLoading />
119-
) : errors.length > 0 ? (
120-
<div className="space-y-2">
121-
<div className="flex items-center gap-2 text-red-400">
116+
<div className="relative p-4 bg-black/10 min-h-[800px]">
117+
<div className="absolute inset-0 p-4 overflow-y-auto">
118+
{isLoading ? (
119+
<PlaygroundLoading />
120+
) : errors.length > 0 ? (
121+
<div className="space-y-2">
122+
<div className="flex items-center gap-2 text-red-400">
123+
<AlertTriangle className="w-4 h-4" />
124+
<span className="font-medium">Validation Errors</span>
125+
</div>
126+
<div className="space-y-1">
127+
{errors.map((error, index) => (
128+
<PlaygroundError key={index} error={error} />
129+
))}
130+
</div>
131+
</div>
132+
) : output ? (
133+
<PlaygroundOutput>{output}</PlaygroundOutput>
134+
) : !hasOpenRouterKey ? (
135+
<div className="flex items-center gap-2 text-yellow-500 text-sm">
122136
<AlertTriangle className="w-4 h-4" />
123-
<span className="font-medium">Validation Errors</span>
137+
<span>Configure OpenRouter API key in settings to run code</span>
124138
</div>
125-
<div className="space-y-1">
126-
{errors.map((error, index) => (
127-
<PlaygroundError key={index} error={error} />
128-
))}
139+
) : (
140+
<div className="text-sm text-muted-foreground italic">
141+
Click "Run" or press ⌘↵ to see the output
129142
</div>
130-
</div>
131-
) : output ? (
132-
<PlaygroundOutput>{output}</PlaygroundOutput>
133-
) : !hasOpenRouterKey ? (
134-
<div className="flex items-center gap-2 text-yellow-500 text-sm">
135-
<AlertTriangle className="w-4 h-4" />
136-
<span>Configure OpenRouter API key in settings to run code</span>
137-
</div>
138-
) : (
139-
<div className="text-sm text-muted-foreground italic">
140-
Click "Run" or press ⌘↵ to see the output
141-
</div>
142-
)}
143+
)}
144+
</div>
143145
</div>
144146
</div>
145147
</div>

src/components/Documentation/PlaygroundContext.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export const PlaygroundOutput = ({
9898
children: ReactNode;
9999
}) => (
100100
<pre className="font-mono text-sm text-muted-foreground whitespace-pre-wrap
101-
bg-black/10 p-4 rounded-lg">
101+
bg-black/10 p-4 rounded-lg max-h-[200px] overflow-y-auto">
102102
{children}
103103
</pre>
104104
);

src/components/Documentation/PlaygroundSettings.tsx

+47-16
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020
import { Slider } from "../ui/slider";
2121
import { Input } from "../ui/input";
2222
import { Label } from "../ui/label";
23+
import { Textarea } from "../ui/textarea";
24+
import { SYSTEM_PROMPT } from "../../services/openRouterService";
2325

2426
interface PlaygroundSettingsProps {
2527
onSettingsChange: (settings: PlaygroundSettings) => void;
@@ -32,38 +34,47 @@ export interface PlaygroundSettings {
3234
autoFormat: boolean;
3335
syntaxHighlighting: boolean;
3436
showLineNumbers: boolean;
37+
systemPrompt: string;
3538
}
3639

40+
const DEFAULT_PLAYGROUND_SETTINGS: PlaygroundSettings = {
41+
model: "openai/gpt-3.5-turbo",
42+
temperature: 0.7,
43+
maxTokens: 1000,
44+
autoFormat: true,
45+
syntaxHighlighting: true,
46+
showLineNumbers: true,
47+
systemPrompt: SYSTEM_PROMPT.interpreter
48+
};
49+
3750
export const PlaygroundSettings: React.FC<PlaygroundSettingsProps> = ({ onSettingsChange }) => {
3851
const { settings: globalSettings } = useSettingsContext();
3952
const [isOpen, setIsOpen] = useState(false);
4053
const [settings, setSettings] = useState<PlaygroundSettings>({
41-
model: "openai/gpt-3.5-turbo",
42-
temperature: 0.7,
43-
maxTokens: 1000,
44-
autoFormat: true,
45-
syntaxHighlighting: true,
46-
showLineNumbers: true
54+
...DEFAULT_PLAYGROUND_SETTINGS,
55+
model: globalSettings.defaultModel
4756
});
4857

4958
const hasOpenRouterKey = !!globalSettings?.openRouterApiKey;
5059

51-
const models = hasOpenRouterKey ? [
52-
{ value: "openai/gpt-4", label: "GPT-4" },
53-
{ value: "openai/gpt-3.5-turbo", label: "GPT-3.5 Turbo" },
54-
{ value: "anthropic/claude-2", label: "Claude 2" },
55-
{ value: "anthropic/claude-instant-v1", label: "Claude Instant" }
56-
] : [
57-
{ value: "openai/gpt-3.5-turbo", label: "GPT-3.5 Turbo (OpenRouter API key required)" }
58-
];
59-
60+
const models = hasOpenRouterKey ?
61+
globalSettings.models.models.map(model => ({
62+
value: model,
63+
label: model.split('/')[1].toUpperCase().replace(/-/g, ' ')
64+
})) : [
65+
{ value: "openai/gpt-3.5-turbo", label: "GPT-3.5 Turbo (OpenRouter API key required)" }
66+
];
6067

6168
const handleSettingChange = (key: keyof PlaygroundSettings, value: any) => {
6269
const newSettings = { ...settings, [key]: value };
6370
setSettings(newSettings);
6471
onSettingsChange(newSettings);
6572
};
6673

74+
const handleResetSystemPrompt = () => {
75+
handleSettingChange("systemPrompt", SYSTEM_PROMPT.interpreter);
76+
};
77+
6778
return (
6879
<div className="relative">
6980
<TooltipProvider>
@@ -90,7 +101,7 @@ export const PlaygroundSettings: React.FC<PlaygroundSettingsProps> = ({ onSettin
90101
</TooltipProvider>
91102

92103
{isOpen && (
93-
<div className="absolute right-0 top-10 w-80 p-4 rounded-lg border border-border/40
104+
<div className="absolute right-0 top-10 w-[500px] max-h-[800px] overflow-y-auto p-4 rounded-lg border border-border/40
94105
bg-card/95 backdrop-blur-sm shadow-xl z-50">
95106
<h3 className="text-sm font-medium mb-4">Playground Settings</h3>
96107

@@ -120,6 +131,26 @@ export const PlaygroundSettings: React.FC<PlaygroundSettingsProps> = ({ onSettin
120131
)}
121132
</div>
122133

134+
<div className="space-y-2">
135+
<div className="flex items-center justify-between">
136+
<Label>System Prompt</Label>
137+
<Button
138+
variant="ghost"
139+
size="sm"
140+
onClick={handleResetSystemPrompt}
141+
className="text-xs"
142+
>
143+
Reset to Default
144+
</Button>
145+
</div>
146+
<Textarea
147+
value={settings.systemPrompt}
148+
onChange={(e) => handleSettingChange("systemPrompt", e.target.value)}
149+
className="h-[200px] font-mono text-xs"
150+
placeholder="Enter system prompt..."
151+
/>
152+
</div>
153+
123154
<div className="space-y-2">
124155
<Label>Temperature</Label>
125156
<div className="pt-2">

0 commit comments

Comments
 (0)