const ResponseView = { oninit() { // Which tab is visible? 0=Logs, 1=Preview, 2=Raw, 3=Diff this.tabIndex = 1; }, view(vnode) { const { response, // Full JSON from server responseTime, // #ms from request start to finish responseSize, // #bytes in the raw response envEditorValue, // The JSON environment from the editor (string) onClose, // Callback to clear or close this view isTimer, // Whether this is a timer response } = vnode.attrs; // If there's no response, nothing to show if (!response) return null; // If isTimer is true and we're on a hidden tab, switch to logs if (isTimer && (this.tabIndex === 1 || this.tabIndex === 2)) { this.tabIndex = 3; } return m("div", { class: "mt-2 p-1 rounded-md bg-gray-200 min-h-40" }, [ /* ───────────────────────────────────────────────────────────────── TAB HEADERS ─────────────────────────────────────────────────────────────────*/ m("div", { class: "flex justify-between items-center" }, [ // Left: 4 tab buttons m( "div", { class: "flex space-x-4 text-sm font-medium text-gray-600 ml-2 py-1", }, [ // Logs (tabIndex=0) m( "div", { class: "flex items-center space-x-1 cursor-pointer " + (this.tabIndex === 0 ? "border-b-2 text-blue-400 border-blue-400" : ""), onclick: () => (this.tabIndex = 0), }, [ m( "svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", "stroke-width": "1.5", stroke: "currentColor", class: "w-4 h-4", }, m("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M3.75 6.75h16.5M3.75 12H12m-8.25 5.25h16.5", }) ), m("span", `Logs(${response.logs.length})`), ] ), // Raw (tabIndex=2) !isTimer && m( "div", { class: "flex items-center space-x-1 cursor-pointer " + (this.tabIndex === 2 ? "border-b-2 text-blue-400 border-blue-400" : ""), onclick: () => (this.tabIndex = 2), }, [ m( "svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", "stroke-width": "1.5", stroke: "currentColor", class: "w-4 h-4", }, m("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M19.5 3 4.5 3m15 0v3.75c0 .621-.504 1.125-1.125 1.125h-3.75m4.875-4.875-4.875 4.875m-9.75 0A2.625 2.625 0 0 1 3 5.25v-.75c0-.621.504-1.125 1.125-1.125h.75c.621 0 1.125.504 1.125 1.125v.75c0 .621-.504 1.125-1.125 1.125H4.5Zm0 6.375h15m-15 6h15", }) ), m("span", "Raw"), ] ), // Diff (tabIndex=3) m( "div", { class: "flex items-center space-x-1 cursor-pointer " + (this.tabIndex === 3 ? "border-b-2 text-blue-400 border-blue-400" : ""), onclick: () => (this.tabIndex = 3), }, [ m( "svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", "stroke-width": "1.5", stroke: "currentColor", class: "w-4 h-4", }, m("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M16.5 3.75 7.5 20.25m9-16.5H9m7.5 0V12m-9 8.25h9m-9 0V12", }) ), m("span", "Diff"), ] ), // Preview (tabIndex=1) !isTimer && m( "div", { class: "flex items-center space-x-1 cursor-pointer " + (this.tabIndex === 1 ? "border-b-2 text-blue-400 border-blue-400" : ""), onclick: () => (this.tabIndex = 1), }, [ m( "svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", "stroke-width": "1.5", stroke: "currentColor", class: "w-4 h-4", }, [ m("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z", }), m("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z", }), ] ), m("span", "Preview"), ] ), ].filter(Boolean) ), // Right: Close icon m( "div", { class: "cursor-pointer text-gray-600 rounded-md hover:bg-gray-300", onclick: onClose, }, m( "svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", "stroke-width": "1.5", stroke: "currentColor", class: "w-6 h-6", }, m("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M6 18 18 6M6 6l12 12", }) ) ), ]), /* ───────────────────────────────────────────────────────────────── TAB CONTENT ─────────────────────────────────────────────────────────────────*/ m("div", { class: "p-2" }, [ // (0) LOGS this.tabIndex === 0 && m( "div", { class: "text-sm font-mono space-y-1" }, (response.logs || []).map((log) => m("div", [ m( "svg", { class: "inline-block w-4 h-4 mr-1", fill: "none", stroke: "currentColor", "stroke-width": "1.5", viewBox: "0 0 24 24", }, m("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "m8.25 4.5 7.5 7.5-7.5 7.5", }) ), log, ]) ) ), // (2) RAW this.tabIndex === 2 && m("div", { class: "space-y-2" }, [ // Show raw body m("div", { class: "bg-white p-2 rounded-md" }, [ m("div", { class: "font-bold text-gray-600 mb-1" }, "Raw Body"), m( "pre", { class: "text-sm overflow-auto" }, response.result?.body ?? "No body" ), ]), // Show headers m("div", { class: "bg-white p-2 rounded-md" }, [ m("div", { class: "font-bold text-gray-600 mb-1" }, "Headers"), response.result?.headers ? m( "table", { class: "text-sm w-full border-collapse" }, Object.entries(response.result.headers).map(([k, v]) => m("tr", [ m("td", { class: "border p-1 font-medium w-1/4" }, k), m("td", { class: "border p-1" }, v), ]) ) ) : m("div", "No headers"), ]), ]), // (3) DIFF this.tabIndex === 3 && m("div", { id: "env-diff-container", style: "position: relative; width: 100%; height: 300px;", oncreate: (vnode) => { // Build "left" from envEditorValue let leftText; try { // Attempt to format nicely const leftObj = JSON.parse(envEditorValue); leftText = JSON.stringify(leftObj, null, 2); } catch (e) { // Fallback to raw string leftText = envEditorValue; } // Build "right" from server environment let rightText = ""; let serverEnv = response?.environment || {}; try { rightText = JSON.stringify(serverEnv, null, 2); } catch (err) { rightText = String(serverEnv); } // Initialize AceDiff const aceDiffer = new AceDiff({ element: vnode.dom, mode: "ace/mode/json", // Or "ace/mode/javascript" theme: "ace/theme/github_dark", left: { content: leftText, editable: false, }, right: { content: rightText, editable: false, }, }); // Optional: set max lines, disable worker const ed = aceDiffer.getEditors(); ed.left.setOptions({ maxLines: 20 }); ed.left.session.setOption("useWorker", false); ed.right.setOptions({ maxLines: 20 }); ed.right.session.setOption("useWorker", false); }, }), // (1) PREVIEW this.tabIndex === 1 && m( "div", { class: "min-h-32 rounded-md bg-white p-2 " + (response.status !== "SUCCESS" ? "border-red-700 border-2" : ""), }, response.status === "SUCCESS" ? m.trust(response?.result?.body) : JSON.stringify(response?.result) ), ]), /* ───────────────────────────────────────────────────────────────── FOOTER (Status, Time, Size) ─────────────────────────────────────────────────────────────────*/ m("div", { class: "flex justify-end p-1" }, [ m("div", { class: "text-sm font-medium text-gray-600 space-x-4" }, [ // Status (hidden if isTimer) !isTimer && m("span", [ "Status: ", m( "span", { class: response.status === "SUCCESS" ? "text-green-600" : "text-red-700", }, response?.result?.status || "Error" ), ]), // Time (always shown) responseTime != null && m("span", [ "Time: ", m("span", { class: "text-green-600" }, `${responseTime}ms`), ]), // Size (hidden if isTimer) !isTimer && responseSize != null && m("span", [ "Size: ", m("span", { class: "text-green-600" }, `${responseSize} bytes`), ]), ]), ]), ]); }, };