fix 1
This commit is contained in:
@@ -9,6 +9,14 @@ const props = defineProps<{
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const updateRow = (index: number, key: keyof KeyValue, value: string | boolean) => {
|
||||
const newData = props.modelValue.map((item, itemIndex) => {
|
||||
if (itemIndex !== index) return item;
|
||||
return { ...item, [key]: value };
|
||||
});
|
||||
emit('update:modelValue', newData);
|
||||
};
|
||||
|
||||
const addRow = () => {
|
||||
const newData = [...props.modelValue, {
|
||||
id: crypto.randomUUID(),
|
||||
@@ -47,14 +55,16 @@ const removeRow = (index: number) => {
|
||||
<td class="py-1 pl-2 align-middle">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="item.enabled"
|
||||
:checked="item.enabled"
|
||||
@change="updateRow(index, 'enabled', ($event.target as HTMLInputElement).checked)"
|
||||
class="rounded bg-slate-800 border-slate-700 text-indigo-500 focus:ring-indigo-500/50 focus:ring-offset-0"
|
||||
>
|
||||
</td>
|
||||
<td class="py-1 pr-2 align-middle">
|
||||
<input
|
||||
type="text"
|
||||
v-model="item.key"
|
||||
:value="item.key"
|
||||
@input="updateRow(index, 'key', ($event.target as HTMLInputElement).value)"
|
||||
placeholder="Key"
|
||||
class="w-full bg-transparent border-b border-transparent hover:border-slate-700 focus:border-indigo-500 focus:outline-none py-1 px-1 text-sm text-slate-300 transition-colors"
|
||||
>
|
||||
@@ -62,7 +72,8 @@ const removeRow = (index: number) => {
|
||||
<td class="py-1 pr-2 align-middle">
|
||||
<input
|
||||
type="text"
|
||||
v-model="item.value"
|
||||
:value="item.value"
|
||||
@input="updateRow(index, 'value', ($event.target as HTMLInputElement).value)"
|
||||
placeholder="Value"
|
||||
class="w-full bg-transparent border-b border-transparent hover:border-slate-700 focus:border-indigo-500 focus:outline-none py-1 px-1 text-sm text-slate-300 transition-colors"
|
||||
>
|
||||
|
||||
@@ -37,7 +37,7 @@ const extensions = computed(() => {
|
||||
const methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];
|
||||
|
||||
const executeRequest = async () => {
|
||||
if (!store.activeRequest.url) return;
|
||||
if (isLoading.value || !store.activeRequest.url) return;
|
||||
|
||||
isLoading.value = true;
|
||||
|
||||
@@ -73,7 +73,6 @@ const executeRequest = async () => {
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
// Simple alert for now, or could use a toast
|
||||
alert('Request Failed: ' + error);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
|
||||
@@ -12,6 +12,23 @@ const store = useRequestStore();
|
||||
const settings = useSettingsStore();
|
||||
const isCopied = ref(false);
|
||||
|
||||
const isJsonResponse = computed(() => {
|
||||
const response = store.activeRequest.response;
|
||||
if (!response) return false;
|
||||
|
||||
const contentType = response.headers['content-type'] || response.headers['Content-Type'] || '';
|
||||
if (contentType.toLowerCase().includes('json')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
JSON.parse(response.body);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
const extensions = computed(() => {
|
||||
const theme = EditorView.theme({
|
||||
"&": {
|
||||
@@ -26,15 +43,20 @@ const extensions = computed(() => {
|
||||
}
|
||||
});
|
||||
|
||||
return [json(), oneDark, theme, EditorView.editable.of(false)];
|
||||
return [
|
||||
oneDark,
|
||||
theme,
|
||||
EditorView.editable.of(false),
|
||||
...(isJsonResponse.value ? [json()] : []),
|
||||
];
|
||||
});
|
||||
|
||||
const formattedBody = computed(() => {
|
||||
if (!store.activeRequest.response) return '';
|
||||
if (!isJsonResponse.value) return store.activeRequest.response.body;
|
||||
try {
|
||||
// Auto pretty print
|
||||
return JSON.stringify(JSON.parse(store.activeRequest.response.body), null, 2);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
return store.activeRequest.response.body;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -68,7 +68,6 @@ const displayFontOptions = computed(() => {
|
||||
placeholder="Select or enter font family"
|
||||
/>
|
||||
<input
|
||||
v-if="!fontOptions.includes(settings.editorFontFamily)"
|
||||
type="text"
|
||||
v-model="settings.editorFontFamily"
|
||||
placeholder="e.g. 'JetBrains Mono', monospace"
|
||||
|
||||
@@ -33,34 +33,110 @@ export interface RequestData {
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
const createEmptyKeyValue = (): KeyValue => ({
|
||||
id: crypto.randomUUID(),
|
||||
key: '',
|
||||
value: '',
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
const createDefaultAuthState = (): AuthState => ({
|
||||
type: 'none',
|
||||
basic: { username: '', password: '' },
|
||||
bearer: { token: '' },
|
||||
apiKey: { key: '', value: '', addTo: 'header' },
|
||||
});
|
||||
|
||||
const createDefaultRequest = (): RequestData => ({
|
||||
id: crypto.randomUUID(),
|
||||
method: 'GET',
|
||||
url: '',
|
||||
params: [createEmptyKeyValue()],
|
||||
headers: [createEmptyKeyValue()],
|
||||
body: '',
|
||||
auth: createDefaultAuthState(),
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
const cloneKeyValueList = (items: KeyValue[] | undefined): KeyValue[] => {
|
||||
if (!Array.isArray(items) || items.length === 0) return [];
|
||||
return items.map((item) => ({
|
||||
id: item.id || crypto.randomUUID(),
|
||||
key: item.key || '',
|
||||
value: item.value || '',
|
||||
enabled: item.enabled ?? true,
|
||||
}));
|
||||
};
|
||||
|
||||
const cloneAuthState = (auth: Partial<AuthState> | undefined): AuthState => ({
|
||||
type: auth?.type || 'none',
|
||||
basic: {
|
||||
username: auth?.basic?.username || '',
|
||||
password: auth?.basic?.password || '',
|
||||
},
|
||||
bearer: {
|
||||
token: auth?.bearer?.token || '',
|
||||
},
|
||||
apiKey: {
|
||||
key: auth?.apiKey?.key || '',
|
||||
value: auth?.apiKey?.value || '',
|
||||
addTo: auth?.apiKey?.addTo || 'header',
|
||||
},
|
||||
});
|
||||
|
||||
const cloneRequest = (req: Partial<RequestData>): RequestData => ({
|
||||
id: typeof req.id === 'string' && req.id ? req.id : crypto.randomUUID(),
|
||||
method: req.method || 'GET',
|
||||
url: req.url || '',
|
||||
params: cloneKeyValueList(req.params),
|
||||
headers: cloneKeyValueList(req.headers),
|
||||
body: req.body || '',
|
||||
auth: cloneAuthState(req.auth),
|
||||
response: req.response
|
||||
? {
|
||||
status: req.response.status,
|
||||
headers: { ...req.response.headers },
|
||||
body: req.response.body,
|
||||
time: req.response.time,
|
||||
size: req.response.size,
|
||||
}
|
||||
: undefined,
|
||||
timestamp: typeof req.timestamp === 'number' ? req.timestamp : Date.now(),
|
||||
});
|
||||
|
||||
const normalizeHistoryItem = (item: Partial<RequestData>): RequestData => {
|
||||
const cloned = cloneRequest(item);
|
||||
|
||||
return {
|
||||
id: cloned.id,
|
||||
method: cloned.method,
|
||||
url: cloned.url,
|
||||
params: cloned.params.length > 0
|
||||
? cloned.params
|
||||
: [createEmptyKeyValue()],
|
||||
headers: cloned.headers.length > 0
|
||||
? cloned.headers
|
||||
: [createEmptyKeyValue()],
|
||||
body: cloned.body,
|
||||
auth: cloned.auth,
|
||||
response: cloned.response,
|
||||
timestamp: cloned.timestamp,
|
||||
};
|
||||
};
|
||||
|
||||
export const useRequestStore = defineStore('request', () => {
|
||||
// State
|
||||
const history = ref<RequestData[]>([]);
|
||||
const activeRequest = ref<RequestData>({
|
||||
id: crypto.randomUUID(),
|
||||
method: 'GET',
|
||||
url: '',
|
||||
params: [
|
||||
{ id: crypto.randomUUID(), key: '', value: '', enabled: true }
|
||||
],
|
||||
headers: [
|
||||
{ id: crypto.randomUUID(), key: '', value: '', enabled: true }
|
||||
],
|
||||
body: '',
|
||||
auth: {
|
||||
type: 'none',
|
||||
basic: { username: '', password: '' },
|
||||
bearer: { token: '' },
|
||||
apiKey: { key: '', value: '', addTo: 'header' }
|
||||
},
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
const activeRequest = ref<RequestData>(createDefaultRequest());
|
||||
|
||||
// Load history from local storage
|
||||
const savedHistory = localStorage.getItem('request_history');
|
||||
if (savedHistory) {
|
||||
try {
|
||||
history.value = JSON.parse(savedHistory);
|
||||
const parsed = JSON.parse(savedHistory);
|
||||
history.value = Array.isArray(parsed)
|
||||
? parsed.map((item) => normalizeHistoryItem(item))
|
||||
: [];
|
||||
} catch (e) {
|
||||
console.error('Failed to parse history', e);
|
||||
}
|
||||
@@ -68,13 +144,12 @@ export const useRequestStore = defineStore('request', () => {
|
||||
|
||||
// Actions
|
||||
const addToHistory = (req: RequestData) => {
|
||||
// Add to beginning, limit to 50
|
||||
const newEntry = { ...req, timestamp: Date.now() };
|
||||
// Don't store the empty trailing rows in history to save space?
|
||||
// Actually keep them for consistency or filter them. Let's filter empty keys.
|
||||
newEntry.params = newEntry.params.filter(p => p.key.trim() !== '');
|
||||
newEntry.headers = newEntry.headers.filter(h => h.key.trim() !== '');
|
||||
|
||||
const newEntry = cloneRequest(req);
|
||||
newEntry.id = crypto.randomUUID();
|
||||
newEntry.timestamp = Date.now();
|
||||
newEntry.params = newEntry.params.filter((p) => p.key.trim() !== '');
|
||||
newEntry.headers = newEntry.headers.filter((h) => h.key.trim() !== '');
|
||||
|
||||
history.value.unshift(newEntry);
|
||||
if (history.value.length > 50) {
|
||||
history.value.pop();
|
||||
@@ -93,46 +168,14 @@ export const useRequestStore = defineStore('request', () => {
|
||||
};
|
||||
|
||||
const resetActiveRequest = () => {
|
||||
activeRequest.value = {
|
||||
id: crypto.randomUUID(),
|
||||
method: 'GET',
|
||||
url: '',
|
||||
params: [
|
||||
{ id: crypto.randomUUID(), key: '', value: '', enabled: true }
|
||||
],
|
||||
headers: [
|
||||
{ id: crypto.randomUUID(), key: '', value: '', enabled: true }
|
||||
],
|
||||
body: '',
|
||||
auth: {
|
||||
type: 'none',
|
||||
basic: { username: '', password: '' },
|
||||
bearer: { token: '' },
|
||||
apiKey: { key: '', value: '', addTo: 'header' }
|
||||
},
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
activeRequest.value = createDefaultRequest();
|
||||
};
|
||||
|
||||
const loadRequest = (req: RequestData) => {
|
||||
// Deep copy
|
||||
const loaded = JSON.parse(JSON.stringify(req));
|
||||
const loaded = normalizeHistoryItem(cloneRequest(req));
|
||||
loaded.id = crypto.randomUUID();
|
||||
// loaded.response = undefined; // Keep response for history viewing
|
||||
|
||||
// Ensure at least one empty row for editing
|
||||
if (loaded.params.length === 0) loaded.params.push({ id: crypto.randomUUID(), key: '', value: '', enabled: true });
|
||||
if (loaded.headers.length === 0) loaded.headers.push({ id: crypto.randomUUID(), key: '', value: '', enabled: true });
|
||||
|
||||
// Ensure auth object exists (migration for old history)
|
||||
if (!loaded.auth) {
|
||||
loaded.auth = {
|
||||
type: 'none',
|
||||
basic: { username: '', password: '' },
|
||||
bearer: { token: '' },
|
||||
apiKey: { key: '', value: '', addTo: 'header' }
|
||||
};
|
||||
}
|
||||
if (loaded.params.length === 0) loaded.params.push(createEmptyKeyValue());
|
||||
if (loaded.headers.length === 0) loaded.headers.push(createEmptyKeyValue());
|
||||
|
||||
activeRequest.value = loaded;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user