feat: polish

This commit is contained in:
Oleg Proskurin 2025-10-14 01:07:50 +07:00
parent 1d1a88d073
commit 658f1420db
5 changed files with 210 additions and 59 deletions

View File

@ -62,3 +62,40 @@ body {
html {
scroll-behavior: smooth;
}
/* Custom Scrollbars for Dark Theme */
/* Firefox */
* {
scrollbar-width: thin;
scrollbar-color: rgb(71, 85, 105) rgb(15, 23, 42); /* slate-600 on slate-950 */
}
/* Webkit browsers (Chrome, Safari, Edge) */
*::-webkit-scrollbar {
width: 8px;
height: 8px;
}
*::-webkit-scrollbar-track {
background: rgb(15, 23, 42); /* slate-950 */
border-radius: 4px;
}
*::-webkit-scrollbar-thumb {
background: rgb(71, 85, 105); /* slate-600 */
border-radius: 4px;
border: 2px solid rgb(15, 23, 42); /* slate-950 */
}
*::-webkit-scrollbar-thumb:hover {
background: rgb(100, 116, 139); /* slate-500 */
}
/* Code blocks - slightly brighter scrollbar for better visibility */
pre::-webkit-scrollbar-thumb {
background: rgb(100, 116, 139); /* slate-500 */
}
pre::-webkit-scrollbar-thumb:hover {
background: rgb(148, 163, 184); /* slate-400 */
}

View File

@ -146,7 +146,7 @@ export const DocsSidebarFinal = ({ currentPath }: DocsSidebarFinalProps) => {
<a
href={child.href}
className={`
block px-3 py-1.5 text-sm rounded-lg transition-colors ml-[-2px]
block px-3 py-1.5 text-sm transition-colors ml-[-2px]
${
childActive
? 'text-white font-medium border-l-2 border-purple-500 bg-transparent pl-4'

View File

@ -60,13 +60,20 @@ export const InteractiveAPIWidgetFinal = ({
const stored = localStorage.getItem(API_KEY_STORAGE);
if (stored) setApiKey(stored);
// Initialize parameter default values
const defaults: Record<string, string> = {};
// Initialize with proper defaults
const defaults: Record<string, string> = {
prompt: 'a futuristic city at sunset',
filename: 'demo',
aspectRatio: '16:9',
};
// Override with parameter defaults if provided
parameters.forEach((param) => {
if (param.defaultValue) {
defaults[param.name] = param.defaultValue;
}
});
setParamValues(defaults);
}, [parameters]);
@ -162,6 +169,80 @@ func main() {
}
};
// Generate all language codes at once
const generateAllCodes = (): Record<Language, string> => {
const url = `${API_BASE_URL}${endpoint}`;
return {
curl: `curl -X ${method} "${url}" \\
-H "X-API-Key: ${apiKey || 'YOUR_API_KEY'}" \\
-H "Content-Type: application/json" \\
-d '{
"prompt": "a futuristic city at sunset",
"filename": "city",
"aspectRatio": "16:9"
}'`,
javascript: `const response = await fetch('${url}', {
method: '${method}',
headers: {
'X-API-Key': '${apiKey || 'YOUR_API_KEY'}',
'Content-Type': 'application/json'
},
body: JSON.stringify({
prompt: 'a futuristic city at sunset',
filename: 'city',
aspectRatio: '16:9'
})
});
const data = await response.json();
console.log(data);`,
python: `import requests
url = '${url}'
headers = {
'X-API-Key': '${apiKey || 'YOUR_API_KEY'}',
'Content-Type': 'application/json'
}
data = {
'prompt': 'a futuristic city at sunset',
'filename': 'city',
'aspectRatio': '16:9'
}
response = requests.${method.toLowerCase()}(url, headers=headers, json=data)
print(response.json())`,
go: `package main
import (
"bytes"
"encoding/json"
"net/http"
)
func main() {
url := "${url}"
data := map[string]interface{}{
"prompt": "a futuristic city at sunset",
"filename": "city",
"aspectRatio": "16:9",
}
jsonData, _ := json.Marshal(data)
req, _ := http.NewRequest("${method}", url, bytes.NewBuffer(jsonData))
req.Header.Set("X-API-Key", "${apiKey || 'YOUR_API_KEY'}")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
}`,
};
};
// Execute API request
const executeRequest = async () => {
if (!apiKey) {
@ -171,13 +252,21 @@ func main() {
setIsExecuting(true);
setError(null);
setResponse(null);
try {
const body: Record<string, any> = {};
parameters.forEach((param) => {
if (paramValues[param.name]) {
body[param.name] = paramValues[param.name];
}
// Build proper request body
const body = {
prompt: paramValues.prompt || 'a futuristic city at sunset',
filename: paramValues.filename || 'demo',
aspectRatio: paramValues.aspectRatio || '16:9',
};
console.log('🔵 API Request:', {
url: `${API_BASE_URL}${endpoint}`,
method,
headers: { 'X-API-Key': apiKey.substring(0, 10) + '...' },
body,
});
const res = await fetch(`${API_BASE_URL}${endpoint}`, {
@ -189,10 +278,27 @@ func main() {
body: method !== 'GET' ? JSON.stringify(body) : undefined,
});
console.log('🟢 Response Status:', res.status, res.statusText);
// Handle non-OK responses
if (!res.ok) {
let errorMessage = `HTTP ${res.status}: ${res.statusText}`;
try {
const errorData = await res.json();
errorMessage = errorData.message || errorData.error || errorMessage;
} catch {
// Response is not JSON
}
throw new Error(errorMessage);
}
const data = await res.json();
console.log('✅ Response Data:', data);
setResponse(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to execute request');
console.error('❌ API Error:', err);
const errorMsg = err instanceof Error ? err.message : 'Failed to execute request';
setError(`Request failed: ${errorMsg}`);
} finally {
setIsExecuting(false);
}
@ -339,9 +445,8 @@ func main() {
<CodeBlockExpanded
isOpen={isExpanded}
onClose={() => setIsExpanded(false)}
code={generateCode()}
language={language}
filename={`example.${language === 'javascript' ? 'js' : language === 'python' ? 'py' : language}`}
codes={generateAllCodes()}
initialLanguage={language}
/>
</>
);

View File

@ -7,39 +7,52 @@
* Features:
* - Full-screen overlay with dark backdrop
* - Centered modal with max-width constraint
* - Language tabs for switching between languages
* - Close button (X) in top-right
* - Escape key support
* - Larger code text in expanded mode
* - Copy button still visible
* - Copy button with feedback
* - Language badge
*
* Usage:
* <CodeBlockExpanded
* isOpen={isOpen}
* onClose={() => setIsOpen(false)}
* code={codeString}
* language="javascript"
* codes={{ curl: '...', javascript: '...', python: '...', go: '...' }}
* initialLanguage="curl"
* filename="example.js"
* />
*/
import { useEffect } from 'react';
import { useState, useEffect } from 'react';
type Language = 'curl' | 'javascript' | 'python' | 'go';
interface CodeBlockExpandedProps {
isOpen: boolean;
onClose: () => void;
code: string;
language: string;
codes: Record<Language, string>;
initialLanguage: Language;
filename?: string;
}
export const CodeBlockExpanded = ({
isOpen,
onClose,
code,
language,
codes,
initialLanguage,
filename,
}: CodeBlockExpandedProps) => {
const [currentLanguage, setCurrentLanguage] = useState<Language>(initialLanguage);
const [copied, setCopied] = useState(false);
// Update language when modal opens
useEffect(() => {
if (isOpen) {
setCurrentLanguage(initialLanguage);
}
}, [isOpen, initialLanguage]);
// Handle escape key
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
@ -62,7 +75,9 @@ export const CodeBlockExpanded = ({
// Copy to clipboard
const copyCode = () => {
navigator.clipboard.writeText(code);
navigator.clipboard.writeText(codes[currentLanguage]);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
if (!isOpen) return null;
@ -77,27 +92,43 @@ export const CodeBlockExpanded = ({
>
{/* Modal Content */}
<div
className="relative w-full max-w-6xl max-h-[90vh] overflow-hidden rounded-2xl bg-slate-950 border border-slate-700 shadow-2xl"
className="relative w-full max-w-7xl max-h-[90vh] overflow-hidden rounded-2xl bg-slate-950 border border-slate-700 shadow-2xl"
onClick={(e) => e.stopPropagation()}
>
{/* Header */}
<div className="flex items-center justify-between px-6 py-4 border-b border-slate-700 bg-slate-900/80">
{/* Left: Language Tabs */}
<div className="flex items-center gap-3">
{filename && (
<span className="text-sm font-medium text-white">{filename}</span>
)}
<span className="px-2 py-1 text-xs bg-purple-500/20 text-purple-400 rounded">
{language}
</span>
<span className="text-sm font-medium text-gray-400 mr-2">Language:</span>
<div className="flex gap-2">
{(['curl', 'javascript', 'python', 'go'] as Language[]).map((lang) => (
<button
key={lang}
onClick={() => setCurrentLanguage(lang)}
className={`px-3 py-1.5 text-xs rounded-lg transition-colors ${
currentLanguage === lang
? 'bg-purple-500/20 text-purple-300 font-semibold'
: 'text-gray-400 hover:text-white hover:bg-slate-800'
}`}
>
{lang === 'javascript' ? 'JavaScript' : lang.charAt(0).toUpperCase() + lang.slice(1)}
</button>
))}
</div>
</div>
{/* Right: Actions */}
<div className="flex items-center gap-2">
{/* Copy Button */}
<button
onClick={copyCode}
className="px-3 py-1.5 text-xs bg-purple-600/20 hover:bg-purple-600/30 text-purple-400 rounded-lg transition-colors"
className={`px-4 py-1.5 text-xs rounded-lg transition-colors ${
copied
? 'bg-green-500/20 text-green-400'
: 'bg-purple-600/20 hover:bg-purple-600/30 text-purple-400'
}`}
>
Copy
{copied ? '✓ Copied!' : 'Copy'}
</button>
{/* Close Button */}
@ -123,10 +154,10 @@ export const CodeBlockExpanded = ({
</div>
</div>
{/* Code Content */}
<div className="overflow-auto max-h-[calc(90vh-5rem)] p-6 bg-slate-950/50">
<pre className="text-sm text-gray-300 leading-relaxed">
<code>{code}</code>
{/* Code Content - Larger text, better spacing */}
<div className="overflow-auto max-h-[calc(90vh-6rem)] p-8 bg-slate-950/50">
<pre className="text-base text-gray-300 leading-relaxed">
<code>{codes[currentLanguage]}</code>
</pre>
</div>

View File

@ -50,7 +50,7 @@ export const SubsectionNav = ({
<nav className="sticky top-0 z-40 bg-slate-950/80 backdrop-blur-sm border-b border-white/10">
{/* Main Nav Container */}
<div className="max-w-7xl mx-auto px-6">
<div className="flex items-center justify-between h-16">
<div className="flex items-center h-12">
{/* Desktop Navigation */}
<div className="hidden md:flex items-center gap-8">
{items.map((item) => {
@ -60,40 +60,18 @@ export const SubsectionNav = ({
key={item.href}
href={item.href}
className={`
relative py-5 text-sm font-medium transition-colors
py-3 text-sm font-medium transition-colors
${active ? 'text-purple-400' : 'text-gray-400 hover:text-white'}
`}
>
{item.label}
{/* Active Indicator */}
{active && (
<div className="absolute bottom-0 left-0 right-0 h-0.5 bg-purple-500 rounded-t-full"></div>
)}
</a>
);
})}
</div>
{/* CTA Button - Desktop */}
<div className="hidden md:block">
<a
href={ctaHref}
onClick={onCtaClick}
className="px-6 py-2.5 rounded-lg bg-gradient-to-r from-amber-600 to-orange-600 text-white text-sm font-semibold hover:from-amber-500 hover:to-orange-500 transition-all shadow-lg"
>
{ctaText}
</a>
</div>
{/* Mobile Menu Button */}
<div className="md:hidden flex items-center gap-3">
<a
href={ctaHref}
onClick={onCtaClick}
className="px-4 py-2 rounded-lg bg-gradient-to-r from-amber-600 to-orange-600 text-white text-xs font-semibold"
>
{ctaText}
</a>
<div className="md:hidden flex items-center ml-auto">
<button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="p-2 text-gray-400 hover:text-white transition-colors"