improve response panel
This commit is contained in:
49
src/App.vue
49
src/App.vue
@@ -3,9 +3,41 @@ import RequestPanel from './components/RequestPanel.vue';
|
||||
import ResponsePanel from './components/ResponsePanel.vue';
|
||||
import Sidebar from './components/Sidebar.vue';
|
||||
import SettingsModal from './components/SettingsModal.vue';
|
||||
import { ref } from 'vue';
|
||||
import { ref, onUnmounted } from 'vue';
|
||||
|
||||
const showSettings = ref(false);
|
||||
const topPanelHeight = ref(50); // Percentage
|
||||
const isDragging = ref(false);
|
||||
|
||||
const startDrag = () => {
|
||||
isDragging.value = true;
|
||||
window.addEventListener('mousemove', onDrag);
|
||||
window.addEventListener('mouseup', stopDrag);
|
||||
};
|
||||
|
||||
const onDrag = (e: MouseEvent) => {
|
||||
if (!isDragging.value) return;
|
||||
|
||||
const containerHeight = window.innerHeight;
|
||||
let newPercentage = (e.clientY / containerHeight) * 100;
|
||||
|
||||
// Clamp between 20% and 80%
|
||||
if (newPercentage < 20) newPercentage = 20;
|
||||
if (newPercentage > 80) newPercentage = 80;
|
||||
|
||||
topPanelHeight.value = newPercentage;
|
||||
};
|
||||
|
||||
const stopDrag = () => {
|
||||
isDragging.value = false;
|
||||
window.removeEventListener('mousemove', onDrag);
|
||||
window.removeEventListener('mouseup', stopDrag);
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('mousemove', onDrag);
|
||||
window.removeEventListener('mouseup', stopDrag);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -14,11 +46,20 @@ const showSettings = ref(false);
|
||||
<Sidebar @open-settings="showSettings = true" />
|
||||
|
||||
<!-- Main Workspace -->
|
||||
<main class="flex-1 flex flex-col min-w-0 bg-slate-900">
|
||||
<div class="flex-1 min-h-0">
|
||||
<main class="flex-1 flex flex-col min-w-0 bg-slate-900 h-full relative">
|
||||
<!-- Request Panel (Top) -->
|
||||
<div class="min-h-0" :style="{ height: topPanelHeight + '%' }">
|
||||
<RequestPanel />
|
||||
</div>
|
||||
<div class="h-1/2 border-t border-slate-800 min-h-0">
|
||||
|
||||
<!-- Resizer Handle -->
|
||||
<div
|
||||
class="h-1 bg-slate-800 hover:bg-indigo-500 cursor-row-resize transition-colors z-10 flex-shrink-0"
|
||||
@mousedown="startDrag"
|
||||
></div>
|
||||
|
||||
<!-- Response Panel (Bottom) -->
|
||||
<div class="flex-1 min-h-0 overflow-hidden">
|
||||
<ResponsePanel />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useRequestStore } from '../stores/requestStore';
|
||||
import { useSettingsStore } from '../stores/settingsStore';
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { json } from '@codemirror/lang-json';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
import { EditorView } from '@codemirror/view'; // Import EditorView
|
||||
import { EditorView } from '@codemirror/view';
|
||||
import { Clipboard, Check } from 'lucide-vue-next';
|
||||
|
||||
const store = useRequestStore();
|
||||
const settings = useSettingsStore();
|
||||
const isCopied = ref(false);
|
||||
|
||||
const extensions = computed(() => {
|
||||
const theme = EditorView.theme({
|
||||
@@ -43,22 +45,46 @@ const statusColor = computed(() => {
|
||||
if (s >= 400) return 'text-rose-400';
|
||||
return 'text-amber-400';
|
||||
});
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
if (!formattedBody.value) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(formattedBody.value);
|
||||
isCopied.value = true;
|
||||
setTimeout(() => {
|
||||
isCopied.value = false;
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error('Failed to copy:', err);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col h-full border-t border-slate-800 bg-slate-900">
|
||||
<div v-if="store.activeRequest.response" class="flex flex-col h-full">
|
||||
<!-- Meta Bar -->
|
||||
<div class="px-4 py-2 border-b border-slate-800 flex gap-4 text-xs items-center bg-slate-950/50">
|
||||
<div class="font-mono">
|
||||
Status: <span :class="['font-bold', statusColor]">{{ store.activeRequest.response.status }}</span>
|
||||
</div>
|
||||
<div class="text-slate-500">
|
||||
Time: <span class="text-slate-300">{{ store.activeRequest.response.time }}ms</span>
|
||||
</div>
|
||||
<div class="text-slate-500">
|
||||
Size: <span class="text-slate-300">{{ (store.activeRequest.response.size / 1024).toFixed(2) }} KB</span>
|
||||
<div class="px-4 py-2 border-b border-slate-800 flex justify-between text-xs items-center bg-slate-950/50">
|
||||
<div class="flex gap-4 items-center">
|
||||
<div class="font-mono">
|
||||
Status: <span :class="['font-bold', statusColor]">{{ store.activeRequest.response.status }}</span>
|
||||
</div>
|
||||
<div class="text-slate-500">
|
||||
Time: <span class="text-slate-300">{{ store.activeRequest.response.time }}ms</span>
|
||||
</div>
|
||||
<div class="text-slate-500">
|
||||
Size: <span class="text-slate-300">{{ (store.activeRequest.response.size / 1024).toFixed(2) }} KB</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
@click="copyToClipboard"
|
||||
class="p-1.5 text-slate-400 hover:text-indigo-400 hover:bg-indigo-500/10 rounded transition-colors"
|
||||
title="Copy Body"
|
||||
>
|
||||
<Check v-if="isCopied" class="w-3.5 h-3.5" />
|
||||
<Clipboard v-else class="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Output -->
|
||||
|
||||
Reference in New Issue
Block a user