diff --git a/src/plays/api-request-builder/ApiRequestBuilder.jsx b/src/plays/api-request-builder/ApiRequestBuilder.jsx
new file mode 100644
index 000000000..3f939357b
--- /dev/null
+++ b/src/plays/api-request-builder/ApiRequestBuilder.jsx
@@ -0,0 +1,218 @@
+import PlayHeader from 'common/playlists/PlayHeader';
+import { useState, useEffect } from 'react';
+import RequestPanel from './components/RequestPanel';
+import ResponsePanel from './components/ResponsePanel';
+import HistoryPanel from './components/HistoryPanel';
+import './styles.css';
+
+function ApiRequestBuilder(props) {
+ const [method, setMethod] = useState('GET');
+ const [url, setUrl] = useState('https://jsonplaceholder.typicode.com/posts/1');
+ const [headers, setHeaders] = useState([
+ { key: 'Content-Type', value: 'application/json', enabled: true }
+ ]);
+ const [body, setBody] = useState('');
+ const [response, setResponse] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
+ const [history, setHistory] = useState([]);
+ const [activeTab, setActiveTab] = useState('body');
+ const [bodyType, setBodyType] = useState('json');
+ const [showHistory, setShowHistory] = useState(false);
+
+ // Load history from localStorage on mount
+ useEffect(() => {
+ const savedHistory = localStorage.getItem('api-request-history');
+ if (savedHistory) {
+ try {
+ setHistory(JSON.parse(savedHistory));
+ } catch (error) {
+ console.error('Failed to load history:', error);
+ }
+ }
+ }, []);
+
+ // Save history to localStorage
+ const saveToHistory = (request, response) => {
+ const historyItem = {
+ id: Date.now(),
+ timestamp: new Date().toISOString(),
+ method: request.method,
+ url: request.url,
+ headers: request.headers,
+ body: request.body,
+ response: {
+ status: response.status,
+ statusText: response.statusText,
+ data: response.data,
+ time: response.time,
+ size: response.size
+ }
+ };
+
+ const updatedHistory = [historyItem, ...history].slice(0, 50); // Keep last 50 requests
+ setHistory(updatedHistory);
+ localStorage.setItem('api-request-history', JSON.stringify(updatedHistory));
+ };
+
+ const handleSendRequest = async () => {
+ if (!url.trim()) {
+ setResponse({
+ error: true,
+ message: 'Please enter a valid URL',
+ status: 0
+ });
+
+ return;
+ }
+
+ setIsLoading(true);
+ const startTime = Date.now();
+
+ try {
+ // Prepare headers
+ const requestHeaders = {};
+ headers.forEach((header) => {
+ if (header.enabled && header.key.trim()) {
+ requestHeaders[header.key] = header.value;
+ }
+ });
+
+ // Prepare request options
+ const options = {
+ method: method,
+ headers: requestHeaders
+ };
+
+ // Add body for methods that support it
+ if (['POST', 'PUT', 'PATCH'].includes(method) && body.trim()) {
+ if (bodyType === 'json') {
+ try {
+ JSON.parse(body); // Validate JSON
+ options.body = body;
+ } catch (e) {
+ throw new Error('Invalid JSON in request body');
+ }
+ } else {
+ options.body = body;
+ }
+ }
+
+ const response = await fetch(url, options);
+ const endTime = Date.now();
+ const responseTime = endTime - startTime;
+
+ let responseData;
+ const contentType = response.headers.get('content-type');
+
+ if (contentType && contentType.includes('application/json')) {
+ responseData = await response.json();
+ } else {
+ responseData = await response.text();
+ }
+
+ const responseSize = new Blob([JSON.stringify(responseData)]).size;
+
+ const responseObj = {
+ status: response.status,
+ statusText: response.statusText,
+ data: responseData,
+ headers: Object.fromEntries(response.headers.entries()),
+ time: responseTime,
+ size: responseSize,
+ error: false
+ };
+
+ setResponse(responseObj);
+
+ // Save to history
+ saveToHistory({ method, url, headers, body }, responseObj);
+ } catch (error) {
+ setResponse({
+ error: true,
+ message: error.message,
+ status: 0,
+ time: Date.now() - startTime
+ });
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const loadFromHistory = (item) => {
+ setMethod(item.method);
+ setUrl(item.url);
+ setHeaders(item.headers);
+ setBody(item.body || '');
+ setResponse(item.response);
+ setShowHistory(false);
+ };
+
+ const clearHistory = () => {
+ setHistory([]);
+ localStorage.removeItem('api-request-history');
+ };
+
+ const formatJSON = () => {
+ try {
+ const parsed = JSON.parse(body);
+ setBody(JSON.stringify(parsed, null, 2));
+ } catch (error) {
+ alert('Invalid JSON format');
+ }
+ };
+
+ return (
+
+
+
+
+
+
đ API Request Builder & Tester
+
+
+
+
+
+
+
+
+
+
+ {showHistory && (
+
setShowHistory(false)}
+ onLoadRequest={loadFromHistory}
+ />
+ )}
+
+
+
+
+ );
+}
+
+export default ApiRequestBuilder;
diff --git a/src/plays/api-request-builder/components/HistoryPanel.jsx b/src/plays/api-request-builder/components/HistoryPanel.jsx
new file mode 100644
index 000000000..43d26560b
--- /dev/null
+++ b/src/plays/api-request-builder/components/HistoryPanel.jsx
@@ -0,0 +1,114 @@
+function HistoryPanel({ history, onLoadRequest, onClearHistory, onClose }) {
+ const formatDate = (isoString) => {
+ const date = new Date(isoString);
+ const now = new Date();
+ const diffMs = now - date;
+ const diffMins = Math.floor(diffMs / 60000);
+ const diffHours = Math.floor(diffMs / 3600000);
+ const diffDays = Math.floor(diffMs / 86400000);
+
+ if (diffMins < 1) return 'Just now';
+ if (diffMins < 60) return `${diffMins}m ago`;
+ if (diffHours < 24) return `${diffHours}h ago`;
+ if (diffDays < 7) return `${diffDays}d ago`;
+
+ return date.toLocaleDateString();
+ };
+
+ const getStatusEmoji = (status) => {
+ if (status >= 200 && status < 300) return 'â
';
+ if (status >= 300 && status < 400) return 'âŠī¸';
+ if (status >= 400 && status < 500) return 'â ī¸';
+
+ return 'â';
+ };
+
+ const getMethodColor = (method) => {
+ const colors = {
+ GET: '#28a745',
+ POST: '#ffc107',
+ PUT: '#17a2b8',
+ PATCH: '#6f42c1',
+ DELETE: '#dc3545',
+ HEAD: '#6c757d',
+ OPTIONS: '#343a40'
+ };
+
+ return colors[method] || '#6c757d';
+ };
+
+ return (
+
+
+
đ Request History
+
+ {history.length > 0 && (
+
+ )}
+
+
+
+
+
+ {history.length === 0 ? (
+
+
đ
+
No requests yet
+
Your request history will appear here
+
+ ) : (
+
+ {history.map((item) => (
+
onLoadRequest(item)}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ onLoadRequest(item);
+ }
+ }}
+ >
+
+
+ {item.method}
+
+
+ {getStatusEmoji(item.response.status)} {item.response.status}
+
+ {formatDate(item.timestamp)}
+
+
+ {item.url}
+
+
+ ⥠{item.response.time}ms
+ {item.response.size && đĻ {Math.round(item.response.size / 1024)}KB}
+
+
+ ))}
+
+ )}
+
+
+
+ đĄ Click on any request to load it
+
+
+ );
+}
+
+export default HistoryPanel;
diff --git a/src/plays/api-request-builder/components/RequestPanel.jsx b/src/plays/api-request-builder/components/RequestPanel.jsx
new file mode 100644
index 000000000..95e65e8ff
--- /dev/null
+++ b/src/plays/api-request-builder/components/RequestPanel.jsx
@@ -0,0 +1,188 @@
+const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];
+
+function RequestPanel({
+ method,
+ setMethod,
+ url,
+ setUrl,
+ headers,
+ setHeaders,
+ body,
+ setBody,
+ activeTab,
+ setActiveTab,
+ bodyType,
+ setBodyType,
+ onSend,
+ isLoading,
+ formatJSON
+}) {
+ const addHeader = () => {
+ setHeaders([...headers, { key: '', value: '', enabled: true }]);
+ };
+
+ const updateHeader = (index, field, value) => {
+ const newHeaders = [...headers];
+ newHeaders[index][field] = value;
+ setHeaders(newHeaders);
+ };
+
+ const removeHeader = (index) => {
+ setHeaders(headers.filter((_, i) => i !== index));
+ };
+
+ const toggleHeader = (index) => {
+ const newHeaders = [...headers];
+ newHeaders[index].enabled = !newHeaders[index].enabled;
+ setHeaders(newHeaders);
+ };
+
+ const handleKeyDown = (e) => {
+ if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
+ onSend();
+ }
+ };
+
+ return (
+
+
+
+
+ setUrl(e.target.value)}
+ onKeyDown={handleKeyDown}
+ />
+
+
+
+
+
+
+
+
+
+
+ {activeTab === 'headers' && (
+
+
+ Headers
+
+
+
+
+ )}
+
+ {activeTab === 'body' && (
+
+ )}
+
+
+ );
+}
+
+export default RequestPanel;
diff --git a/src/plays/api-request-builder/components/ResponsePanel.jsx b/src/plays/api-request-builder/components/ResponsePanel.jsx
new file mode 100644
index 000000000..21771c7ac
--- /dev/null
+++ b/src/plays/api-request-builder/components/ResponsePanel.jsx
@@ -0,0 +1,205 @@
+import { useState } from 'react';
+
+function ResponsePanel({ response, isLoading }) {
+ const [activeTab, setActiveTab] = useState('body');
+ const [searchTerm, setSearchTerm] = useState('');
+
+ if (!response && !isLoading) {
+ return (
+
+
+
đ¯
+
Ready to test APIs
+
Enter a URL above and click Send to see the response here
+
+
Quick Tips:
+
+ - Try: https://jsonplaceholder.typicode.com/posts
+ - Use Ctrl+Enter to send request
+ - View response time and size
+ - Access request history
+
+
+
+
+ );
+ }
+
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ const getStatusClass = (status) => {
+ if (status === 0) return 'status-error';
+ if (status >= 200 && status < 300) return 'status-success';
+ if (status >= 300 && status < 400) return 'status-redirect';
+ if (status >= 400 && status < 500) return 'status-client-error';
+
+ return 'status-server-error';
+ };
+
+ const formatBytes = (bytes) => {
+ if (bytes === 0) return '0 Bytes';
+ const k = 1024;
+ const sizes = ['Bytes', 'KB', 'MB'];
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
+
+ return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
+ };
+
+ const copyToClipboard = (text) => {
+ navigator.clipboard
+ .writeText(text)
+ .then(() => {
+ alert('Copied to clipboard!');
+ })
+ .catch((err) => {
+ console.error('Failed to copy:', err);
+ });
+ };
+
+ const downloadResponse = () => {
+ const dataStr = JSON.stringify(response.data, null, 2);
+ const dataBlob = new Blob([dataStr], { type: 'application/json' });
+ const url = URL.createObjectURL(dataBlob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = `response-${Date.now()}.json`;
+ link.click();
+ URL.revokeObjectURL(url);
+ };
+
+ const highlightSearchTerm = (text) => {
+ if (!searchTerm) return text;
+ const regex = new RegExp(`(${searchTerm})`, 'gi');
+
+ return text.replace(regex, '$1');
+ };
+
+ const formatResponseData = () => {
+ if (response.error) {
+ return response.message;
+ }
+
+ if (typeof response.data === 'object') {
+ return JSON.stringify(response.data, null, 2);
+ }
+
+ return response.data;
+ };
+
+ return (
+
+
+
+
+ {response.error ? 'â Error' : `${response.status} ${response.statusText}`}
+
+ ⥠{response.time}ms
+ {response.size && đĻ {formatBytes(response.size)}}
+
+
+
+ {!response.error && typeof response.data === 'object' && (
+
+ )}
+
+
+
+ {!response.error && (
+
+
+
+
+ )}
+
+
+ {response.error ? (
+
+
â ī¸
+
Request Failed
+
{response.message}
+
+
Common issues:
+
+ - Check if the URL is correct
+ - Verify network connection
+ - Check CORS settings for cross-origin requests
+ - Ensure the API endpoint is accessible
+
+
+
+ ) : (
+ <>
+ {activeTab === 'body' && (
+
+
+ setSearchTerm(e.target.value)}
+ />
+
+
+
+
+
+ )}
+
+ {activeTab === 'headers' && (
+
+
+
+
+ | Header |
+ Value |
+
+
+
+ {Object.entries(response.headers || {}).map(([key, value]) => (
+
+ | {key} |
+ {value} |
+
+ ))}
+
+
+
+ )}
+ >
+ )}
+
+
+ );
+}
+
+export default ResponsePanel;
diff --git a/src/plays/api-request-builder/readme.md b/src/plays/api-request-builder/readme.md
new file mode 100644
index 000000000..ddd3ac277
--- /dev/null
+++ b/src/plays/api-request-builder/readme.md
@@ -0,0 +1,34 @@
+# API Request Builder & Tester
+
+A React-based API testing tool for building and testing HTTP requests.
+
+## Features
+
+- Multiple HTTP Methods (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS)
+- Custom Headers with enable/disable
+- JSON & Text body editor with formatting
+- Response viewer with status, headers, and timing
+- Request History (last 50 saved)
+- Search in response data
+- Copy/Download responses
+- Keyboard shortcut (Ctrl+Enter)
+
+## Usage
+
+1. Select HTTP method
+2. Enter API URL
+3. Add headers (optional)
+4. Add body for POST/PUT/PATCH (optional)
+5. Click Send or press Ctrl+Enter
+6. View response, timing, and status
+
+## React Concepts
+
+- useState/useEffect hooks
+- localStorage persistence
+- Fetch API
+- Component composition
+- Controlled components
+- Error handling
+
+[@Abhrxdip](https://github.com/Abhrxdip)
diff --git a/src/plays/api-request-builder/styles.css b/src/plays/api-request-builder/styles.css
new file mode 100644
index 000000000..7ab2dc94e
--- /dev/null
+++ b/src/plays/api-request-builder/styles.css
@@ -0,0 +1,829 @@
+.api-builder-container {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
+ padding: 1.5rem;
+ max-width: 1400px;
+ margin: 0 auto;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ border-radius: 12px;
+ min-height: 80vh;
+}
+
+.api-builder-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 1.5rem;
+ padding: 1rem;
+ background: rgba(255, 255, 255, 0.95);
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.api-builder-title {
+ margin: 0;
+ color: #2d3748;
+ font-size: 1.75rem;
+ font-weight: 700;
+}
+
+.history-toggle-btn {
+ padding: 0.5rem 1rem;
+ background: #667eea;
+ color: white;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-weight: 600;
+ transition: all 0.3s ease;
+}
+
+.history-toggle-btn:hover {
+ background: #5568d3;
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
+}
+
+.api-builder-layout {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 1rem;
+ transition: all 0.3s ease;
+}
+
+.api-builder-layout.show-history {
+ grid-template-columns: 1fr 350px;
+}
+
+.api-builder-main {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+/* Request Panel */
+.request-panel {
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ overflow: hidden;
+}
+
+.request-url-section {
+ display: flex;
+ gap: 0.5rem;
+ padding: 1rem;
+ background: #f7fafc;
+ border-bottom: 2px solid #e2e8f0;
+}
+
+.method-select {
+ padding: 0.75rem;
+ border: 2px solid #e2e8f0;
+ border-radius: 6px;
+ font-weight: 600;
+ font-size: 0.9rem;
+ cursor: pointer;
+ background: white;
+ min-width: 100px;
+ transition: all 0.2s ease;
+}
+
+.method-select:focus {
+ outline: none;
+ border-color: #667eea;
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
+}
+
+.url-input {
+ flex: 1;
+ padding: 0.75rem;
+ border: 2px solid #e2e8f0;
+ border-radius: 6px;
+ font-size: 0.95rem;
+ transition: all 0.2s ease;
+}
+
+.url-input:focus {
+ outline: none;
+ border-color: #667eea;
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
+}
+
+.send-btn {
+ padding: 0.75rem 2rem;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: white;
+ border: none;
+ border-radius: 6px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ min-width: 120px;
+}
+
+.send-btn:hover:not(:disabled) {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
+}
+
+.send-btn:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+
+.send-btn.loading {
+ animation: pulse 1.5s ease-in-out infinite;
+}
+
+@keyframes pulse {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.7; }
+}
+
+.request-tabs {
+ display: flex;
+ border-bottom: 2px solid #e2e8f0;
+ background: #f7fafc;
+}
+
+.tab-btn {
+ padding: 0.75rem 1.5rem;
+ border: none;
+ background: transparent;
+ cursor: pointer;
+ font-weight: 600;
+ color: #718096;
+ transition: all 0.2s ease;
+ position: relative;
+}
+
+.tab-btn:hover:not(:disabled) {
+ color: #667eea;
+ background: rgba(102, 126, 234, 0.05);
+}
+
+.tab-btn.active {
+ color: #667eea;
+}
+
+.tab-btn.active::after {
+ content: '';
+ position: absolute;
+ bottom: -2px;
+ left: 0;
+ right: 0;
+ height: 2px;
+ background: #667eea;
+}
+
+.tab-btn:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.request-content {
+ padding: 1rem;
+ min-height: 250px;
+}
+
+/* Headers Section */
+.headers-section {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.headers-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.headers-header span {
+ font-weight: 600;
+ color: #2d3748;
+}
+
+.add-header-btn {
+ padding: 0.5rem 1rem;
+ background: #48bb78;
+ color: white;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-weight: 600;
+ transition: all 0.2s ease;
+}
+
+.add-header-btn:hover {
+ background: #38a169;
+ transform: translateY(-1px);
+}
+
+.headers-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.header-row {
+ display: grid;
+ grid-template-columns: auto 1fr 2fr auto;
+ gap: 0.5rem;
+ align-items: center;
+ padding: 0.5rem;
+ background: #f7fafc;
+ border-radius: 6px;
+}
+
+.header-row input[type="checkbox"] {
+ width: 18px;
+ height: 18px;
+ cursor: pointer;
+}
+
+.header-key,
+.header-value {
+ padding: 0.5rem;
+ border: 1px solid #e2e8f0;
+ border-radius: 4px;
+ font-size: 0.9rem;
+}
+
+.header-key:focus,
+.header-value:focus {
+ outline: none;
+ border-color: #667eea;
+}
+
+.remove-header-btn {
+ padding: 0.25rem 0.5rem;
+ background: #fc8181;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 1rem;
+ transition: all 0.2s ease;
+}
+
+.remove-header-btn:hover {
+ background: #f56565;
+}
+
+/* Body Section */
+.body-section {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.body-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.body-type-selector {
+ display: flex;
+ gap: 1rem;
+}
+
+.body-type-selector label {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ cursor: pointer;
+ font-weight: 600;
+ color: #4a5568;
+}
+
+.body-type-selector input[type="radio"] {
+ cursor: pointer;
+}
+
+.format-btn {
+ padding: 0.5rem 1rem;
+ background: #4299e1;
+ color: white;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-weight: 600;
+ transition: all 0.2s ease;
+}
+
+.format-btn:hover {
+ background: #3182ce;
+ transform: translateY(-1px);
+}
+
+.body-textarea {
+ width: 100%;
+ min-height: 200px;
+ padding: 1rem;
+ border: 2px solid #e2e8f0;
+ border-radius: 6px;
+ font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
+ font-size: 0.9rem;
+ resize: vertical;
+ transition: all 0.2s ease;
+}
+
+.body-textarea:focus {
+ outline: none;
+ border-color: #667eea;
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
+}
+
+.body-footer {
+ text-align: right;
+ color: #718096;
+}
+
+/* Response Panel */
+.response-panel {
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ overflow: hidden;
+ min-height: 400px;
+}
+
+.response-placeholder,
+.response-loading,
+.error-display {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 3rem;
+ text-align: center;
+ min-height: 400px;
+}
+
+.placeholder-icon,
+.error-icon {
+ font-size: 4rem;
+ margin-bottom: 1rem;
+}
+
+.response-placeholder h3 {
+ color: #2d3748;
+ margin-bottom: 0.5rem;
+}
+
+.response-placeholder p {
+ color: #718096;
+ margin-bottom: 2rem;
+}
+
+.placeholder-tips {
+ background: #f7fafc;
+ padding: 1.5rem;
+ border-radius: 8px;
+ text-align: left;
+ max-width: 500px;
+}
+
+.placeholder-tips h4 {
+ margin-top: 0;
+ margin-bottom: 1rem;
+ color: #2d3748;
+}
+
+.placeholder-tips ul {
+ margin: 0;
+ padding-left: 1.5rem;
+ color: #4a5568;
+}
+
+.placeholder-tips li {
+ margin-bottom: 0.5rem;
+}
+
+.spinner {
+ width: 50px;
+ height: 50px;
+ border: 4px solid #e2e8f0;
+ border-top-color: #667eea;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+ margin-bottom: 1rem;
+}
+
+@keyframes spin {
+ to { transform: rotate(360deg); }
+}
+
+.response-loading p {
+ color: #4a5568;
+ font-weight: 600;
+}
+
+.response-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1rem;
+ background: #f7fafc;
+ border-bottom: 2px solid #e2e8f0;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+}
+
+.response-status {
+ display: flex;
+ gap: 1rem;
+ align-items: center;
+ flex-wrap: wrap;
+}
+
+.status-badge {
+ padding: 0.4rem 0.8rem;
+ border-radius: 6px;
+ font-weight: 700;
+ font-size: 0.9rem;
+}
+
+.status-success {
+ background: #c6f6d5;
+ color: #22543d;
+}
+
+.status-redirect {
+ background: #bee3f8;
+ color: #2c5282;
+}
+
+.status-client-error {
+ background: #fed7d7;
+ color: #742a2a;
+}
+
+.status-server-error {
+ background: #feb2b2;
+ color: #742a2a;
+}
+
+.status-error {
+ background: #fed7d7;
+ color: #742a2a;
+}
+
+.response-time,
+.response-size {
+ padding: 0.4rem 0.8rem;
+ background: white;
+ border-radius: 6px;
+ font-weight: 600;
+ color: #4a5568;
+ font-size: 0.85rem;
+}
+
+.response-actions {
+ display: flex;
+ gap: 0.5rem;
+}
+
+.action-btn {
+ padding: 0.5rem 1rem;
+ background: #667eea;
+ color: white;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-weight: 600;
+ transition: all 0.2s ease;
+ font-size: 0.85rem;
+}
+
+.action-btn:hover {
+ background: #5568d3;
+ transform: translateY(-1px);
+}
+
+.response-content {
+ padding: 1rem;
+ max-height: 500px;
+ overflow-y: auto;
+}
+
+.search-bar {
+ margin-bottom: 1rem;
+}
+
+.search-input {
+ width: 100%;
+ padding: 0.75rem;
+ border: 2px solid #e2e8f0;
+ border-radius: 6px;
+ font-size: 0.9rem;
+}
+
+.search-input:focus {
+ outline: none;
+ border-color: #667eea;
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
+}
+
+.response-data {
+ background: #1a202c;
+ color: #68d391;
+ padding: 1.5rem;
+ border-radius: 8px;
+ overflow-x: auto;
+ font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
+ font-size: 0.9rem;
+ line-height: 1.6;
+ margin: 0;
+}
+
+.response-data code {
+ color: inherit;
+}
+
+.response-data mark {
+ background: #fbbf24;
+ color: #1a202c;
+ padding: 0.2rem;
+ border-radius: 2px;
+}
+
+.error-message {
+ color: #e53e3e;
+ font-weight: 600;
+ font-size: 1.1rem;
+ margin: 1rem 0;
+}
+
+.error-tips {
+ background: #fff5f5;
+ padding: 1.5rem;
+ border-radius: 8px;
+ border-left: 4px solid #fc8181;
+ text-align: left;
+ max-width: 500px;
+}
+
+.error-tips h4 {
+ margin-top: 0;
+ color: #742a2a;
+}
+
+.error-tips ul {
+ margin: 0;
+ padding-left: 1.5rem;
+ color: #742a2a;
+}
+
+.error-tips li {
+ margin-bottom: 0.5rem;
+}
+
+.headers-table {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+.headers-table th {
+ background: #f7fafc;
+ padding: 0.75rem;
+ text-align: left;
+ font-weight: 700;
+ color: #2d3748;
+ border-bottom: 2px solid #e2e8f0;
+}
+
+.headers-table td {
+ padding: 0.75rem;
+ border-bottom: 1px solid #e2e8f0;
+}
+
+.headers-table .header-key {
+ font-weight: 600;
+ color: #667eea;
+ font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
+ font-size: 0.9rem;
+}
+
+.headers-table .header-value {
+ color: #4a5568;
+ font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
+ font-size: 0.85rem;
+}
+
+/* History Panel */
+.history-panel {
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ display: flex;
+ flex-direction: column;
+ max-height: calc(100vh - 200px);
+}
+
+.history-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1rem;
+ background: #f7fafc;
+ border-bottom: 2px solid #e2e8f0;
+}
+
+.history-header h3 {
+ margin: 0;
+ color: #2d3748;
+ font-size: 1.1rem;
+}
+
+.history-actions {
+ display: flex;
+ gap: 0.5rem;
+}
+
+.clear-history-btn,
+.close-history-btn {
+ padding: 0.4rem 0.8rem;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-weight: 600;
+ transition: all 0.2s ease;
+ font-size: 0.85rem;
+}
+
+.clear-history-btn {
+ background: #fc8181;
+ color: white;
+}
+
+.clear-history-btn:hover {
+ background: #f56565;
+}
+
+.close-history-btn {
+ background: #e2e8f0;
+ color: #4a5568;
+}
+
+.close-history-btn:hover {
+ background: #cbd5e0;
+}
+
+.history-content {
+ flex: 1;
+ overflow-y: auto;
+ padding: 1rem;
+}
+
+.history-empty {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 3rem 1rem;
+ text-align: center;
+ color: #718096;
+}
+
+.empty-icon {
+ font-size: 3rem;
+ margin-bottom: 1rem;
+}
+
+.history-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+}
+
+.history-item {
+ padding: 1rem;
+ background: #f7fafc;
+ border: 2px solid #e2e8f0;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.history-item:hover {
+ border-color: #667eea;
+ background: white;
+ box-shadow: 0 2px 8px rgba(102, 126, 234, 0.2);
+ transform: translateY(-2px);
+}
+
+.history-item-header {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin-bottom: 0.5rem;
+ flex-wrap: wrap;
+}
+
+.history-method {
+ padding: 0.25rem 0.5rem;
+ border-radius: 4px;
+ color: white;
+ font-weight: 700;
+ font-size: 0.75rem;
+}
+
+.history-status {
+ font-weight: 600;
+ font-size: 0.85rem;
+ color: #4a5568;
+}
+
+.history-time {
+ margin-left: auto;
+ font-size: 0.75rem;
+ color: #718096;
+}
+
+.history-item-url {
+ color: #2d3748;
+ font-size: 0.85rem;
+ margin-bottom: 0.5rem;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ font-weight: 500;
+}
+
+.history-item-footer {
+ display: flex;
+ gap: 1rem;
+ font-size: 0.75rem;
+ color: #718096;
+}
+
+.history-footer {
+ padding: 0.75rem 1rem;
+ background: #f7fafc;
+ border-top: 1px solid #e2e8f0;
+ text-align: center;
+ color: #718096;
+}
+
+/* Responsive Design */
+@media (max-width: 1024px) {
+ .api-builder-layout.show-history {
+ grid-template-columns: 1fr;
+ }
+
+ .history-panel {
+ max-height: 400px;
+ }
+}
+
+@media (max-width: 768px) {
+ .api-builder-container {
+ padding: 1rem;
+ }
+
+ .request-url-section {
+ flex-direction: column;
+ }
+
+ .method-select,
+ .send-btn {
+ width: 100%;
+ }
+
+ .response-header {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .header-row {
+ grid-template-columns: auto 1fr;
+ gap: 0.5rem;
+ }
+
+ .header-row input[type="text"]:last-of-type {
+ grid-column: 1 / -1;
+ }
+}
+
+/* Scrollbar Styling */
+::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+}
+
+::-webkit-scrollbar-track {
+ background: #f1f1f1;
+ border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb {
+ background: #cbd5e0;
+ border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: #a0aec0;
+}