185 lines
8.2 KiB
JavaScript
185 lines
8.2 KiB
JavaScript
// Postman spoof — static-but-believable HTTP client UI.
|
|
|
|
const RESPONSES = {
|
|
GET: `{
|
|
"id": 1,
|
|
"name": "Alice Freeman",
|
|
"email": "alice@meridian.dev",
|
|
"role": "developer",
|
|
"active": true
|
|
}`,
|
|
POST: `{
|
|
"id": 42,
|
|
"name": "New Resource",
|
|
"created": "2025-03-14T10:30:00Z",
|
|
"status": "created",
|
|
"message": "Resource created successfully"
|
|
}`,
|
|
PUT: `{
|
|
"id": 42,
|
|
"name": "Updated Resource",
|
|
"updated": "2025-03-14T10:32:00Z",
|
|
"status": "updated",
|
|
"message": "Resource updated successfully"
|
|
}`,
|
|
DELETE: `{
|
|
"id": 42,
|
|
"status": "deleted",
|
|
"message": "Resource deleted successfully"
|
|
}`,
|
|
};
|
|
|
|
const METHODS = ['GET', 'POST', 'PUT', 'DELETE'];
|
|
|
|
export default {
|
|
id: 'postman',
|
|
title: 'Postman',
|
|
render(container, api) {
|
|
let method = 'GET';
|
|
let sending = false;
|
|
|
|
const root = document.createElement('div');
|
|
root.style.cssText = 'height:100%;display:flex;flex-direction:column;background:var(--bg-1);';
|
|
|
|
// Request bar: method select + URL input + Send button
|
|
const reqBar = document.createElement('div');
|
|
reqBar.style.cssText = 'display:flex;align-items:center;gap:0;padding:8px 12px;border-bottom:1px solid var(--border);background:var(--bg-2);flex-shrink:0;';
|
|
|
|
const methodSelect = document.createElement('select');
|
|
methodSelect.style.cssText = 'padding:6px 10px;background:var(--bg-3);border:1px solid var(--border);border-right:none;border-radius:4px 0 0 4px;color:var(--accent);font-weight:600;font-size:13px;outline:none;cursor:pointer;min-width:70px;';
|
|
METHODS.forEach((m) => {
|
|
const opt = document.createElement('option');
|
|
opt.value = m;
|
|
opt.textContent = m;
|
|
if (m === method) opt.selected = true;
|
|
methodSelect.appendChild(opt);
|
|
});
|
|
methodSelect.addEventListener('change', () => { method = methodSelect.value; });
|
|
|
|
const urlInput = document.createElement('input');
|
|
urlInput.type = 'text';
|
|
urlInput.value = 'https://api.meridian.dev/v1/users';
|
|
urlInput.style.cssText = 'flex:1;padding:6px 12px;background:var(--bg-3);border:1px solid var(--border);color:var(--text);font-size:13px;font-family:monospace;outline:none;';
|
|
urlInput.placeholder = 'Enter request URL';
|
|
|
|
const sendBtn = document.createElement('button');
|
|
sendBtn.style.cssText = 'padding:6px 20px;background:var(--accent);color:#0f1114;border:none;border-radius:0 4px 4px 0;font-size:13px;font-weight:600;cursor:pointer;';
|
|
sendBtn.textContent = 'Send';
|
|
|
|
sendBtn.addEventListener('click', () => {
|
|
if (sending) return;
|
|
sending = true;
|
|
sendBtn.textContent = 'Sending...';
|
|
sendBtn.disabled = true;
|
|
|
|
setTimeout(() => {
|
|
sendBtn.textContent = 'Send';
|
|
sendBtn.disabled = false;
|
|
sending = false;
|
|
showResponse();
|
|
}, 200);
|
|
});
|
|
|
|
reqBar.appendChild(methodSelect);
|
|
reqBar.appendChild(urlInput);
|
|
reqBar.appendChild(sendBtn);
|
|
root.appendChild(reqBar);
|
|
|
|
// Tabs
|
|
const tabNames = ['Params', 'Headers', 'Body'];
|
|
let activeTab = 'Params';
|
|
const tabBtns = [];
|
|
|
|
const tabBar = document.createElement('div');
|
|
tabBar.style.cssText = 'display:flex;gap:0;border-bottom:1px solid var(--border);background:var(--bg-2);flex-shrink:0;';
|
|
|
|
tabNames.forEach((tab) => {
|
|
const btn = document.createElement('button');
|
|
const isActive = activeTab === tab;
|
|
btn.style.cssText = 'padding:8px 16px;background:transparent;border:none;border-right:1px solid var(--border);color:' + (isActive ? 'var(--text)' : 'var(--text-dim)') + ';font-size:12px;cursor:pointer;transition:color 150ms;';
|
|
btn.textContent = tab;
|
|
btn.addEventListener('click', () => {
|
|
activeTab = tab;
|
|
tabArea.innerHTML = contentPanels[tab];
|
|
tabBtns.forEach((b) => { b.style.color = 'var(--text-dim)'; });
|
|
btn.style.color = 'var(--text)';
|
|
});
|
|
tabBtns.push(btn);
|
|
tabBar.appendChild(btn);
|
|
});
|
|
|
|
root.appendChild(tabBar);
|
|
|
|
// Tab content panels
|
|
const contentPanels = {
|
|
Params: '<div style="font-size:12px;line-height:2.4;padding:4px 0;">' +
|
|
'<div style="display:flex;gap:8px;margin-bottom:4px;">' +
|
|
'<span style="color:var(--accent);font-weight:500;width:100px;flex-shrink:0;">Authorization</span>' +
|
|
'<input style="flex:1;background:var(--bg-1);border:1px solid var(--border);padding:5px 10px;border-radius:3px;color:var(--text);font-size:12px;font-family:monospace;" value="Bearer eyJhbGciOiJIUzI1NiJ9..." />' +
|
|
'</div>' +
|
|
'<div style="display:flex;gap:8px;margin-bottom:4px;">' +
|
|
'<span style="color:var(--accent);font-weight:500;width:100px;flex-shrink:0;">Content-Type</span>' +
|
|
'<input style="flex:1;background:var(--bg-1);border:1px solid var(--border);padding:5px 10px;border-radius:3px;color:var(--text);font-size:12px;font-family:monospace;" value="application/json" />' +
|
|
'</div>' +
|
|
'<div style="display:flex;gap:8px;">' +
|
|
'<span style="color:var(--accent);font-weight:500;width:100px;flex-shrink:0;">X-Request-ID</span>' +
|
|
'<input style="flex:1;background:var(--bg-1);border:1px solid var(--border);padding:5px 10px;border-radius:3px;color:var(--text);font-size:12px;font-family:monospace;" value="" placeholder="Auto-generated" />' +
|
|
'</div>' +
|
|
'</div>',
|
|
Headers: '<div style="font-size:12px;line-height:2.4;padding:4px 0;">' +
|
|
'<div style="display:flex;gap:8px;margin-bottom:4px;">' +
|
|
'<span style="color:var(--accent);font-weight:500;width:100px;flex-shrink:0;">User-Agent</span>' +
|
|
'<span style="color:var(--text);font-family:monospace;">MeridianClient/1.0</span>' +
|
|
'</div>' +
|
|
'<div style="display:flex;gap:8px;margin-bottom:4px;">' +
|
|
'<span style="color:var(--accent);font-weight:500;width:100px;flex-shrink:0;">Accept</span>' +
|
|
'<span style="color:var(--text);font-family:monospace;">application/json</span>' +
|
|
'</div>' +
|
|
'<div style="display:flex;gap:8px;">' +
|
|
'<span style="color:var(--accent);font-weight:500;width:100px;flex-shrink:0;">Cache-Control</span>' +
|
|
'<span style="color:var(--text);font-family:monospace;">no-cache</span>' +
|
|
'</div>' +
|
|
'</div>',
|
|
Body: '<div style="padding:4px 0;">' +
|
|
'<textarea id="postman-body" style="width:100%;height:140px;background:var(--bg-1);border:1px solid var(--border);padding:10px;border-radius:3px;color:var(--text);font-size:12px;font-family:monospace;resize:none;outline:none;"></textarea>' +
|
|
'</div>',
|
|
};
|
|
|
|
// Build body content separately since it has real JSON
|
|
const postmanBody = JSON.stringify({ name: 'Alice Freeman', email: 'alice@meridian.dev', role: 'developer' }, null, 2);
|
|
|
|
const tabArea = document.createElement('div');
|
|
tabArea.style.cssText = 'flex:1;overflow-y:auto;background:var(--bg-2);';
|
|
tabArea.innerHTML = contentPanels[activeTab];
|
|
root.appendChild(tabArea);
|
|
|
|
// Response label
|
|
const respLabel = document.createElement('div');
|
|
respLabel.style.cssText = 'padding:8px 12px;font-size:11px;font-weight:600;color:var(--text-dim);text-transform:uppercase;letter-spacing:0.05em;border-bottom:1px solid var(--border);background:var(--bg-2);flex-shrink:0;';
|
|
respLabel.textContent = 'Response';
|
|
|
|
// Response pre
|
|
const respArea = document.createElement('pre');
|
|
respArea.style.cssText = 'flex:1;overflow:auto;padding:12px 16px;margin:0;font-size:12px;line-height:1.6;color:var(--text);font-family:monospace;background:var(--bg-1);';
|
|
respArea.textContent = 'Click Send to execute the request';
|
|
|
|
// Status line
|
|
const statusLine = document.createElement('div');
|
|
statusLine.style.cssText = 'padding:4px 12px;font-size:11px;color:var(--text-dim);border-top:1px solid var(--border);background:var(--bg-2);flex-shrink:0;';
|
|
statusLine.textContent = 'Ready';
|
|
|
|
root.appendChild(respLabel);
|
|
root.appendChild(respArea);
|
|
root.appendChild(statusLine);
|
|
container.appendChild(root);
|
|
|
|
function showResponse() {
|
|
const resp = RESPONSES[method] || RESPONSES.GET;
|
|
const time = Math.floor(Math.random() * 60) + 40;
|
|
const size = resp.length + 2;
|
|
respArea.textContent = resp;
|
|
statusLine.textContent = 'Status: 200 OK \u{B7} Time: ' + time + 'ms \u{B7} Size: ' + size + ' B';
|
|
}
|
|
},
|
|
};
|