drf-to-mkdoc 0.3.1__py3-none-any.whl → 0.3.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of drf-to-mkdoc might be problematic. Click here for more details.
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/settings-modal.js +361 -0
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/form-manager.js +136 -17
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/filter-section.css +10 -5
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/settings-modal.css +555 -0
- drf_to_mkdoc/templates/endpoints/list/base.html +1 -0
- drf_to_mkdoc/templates/endpoints/list/filter_section.html +3 -1
- drf_to_mkdoc/templates/endpoints/list/settings_modal.html +138 -0
- drf_to_mkdoc/templates/try-out/form.html +1 -1
- drf_to_mkdoc/utils/endpoint_list_generator.py +2 -0
- {drf_to_mkdoc-0.3.1.dist-info → drf_to_mkdoc-0.3.2.dist-info}/METADATA +1 -1
- {drf_to_mkdoc-0.3.1.dist-info → drf_to_mkdoc-0.3.2.dist-info}/RECORD +14 -11
- {drf_to_mkdoc-0.3.1.dist-info → drf_to_mkdoc-0.3.2.dist-info}/WHEEL +0 -0
- {drf_to_mkdoc-0.3.1.dist-info → drf_to_mkdoc-0.3.2.dist-info}/licenses/LICENSE +0 -0
- {drf_to_mkdoc-0.3.1.dist-info → drf_to_mkdoc-0.3.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
// Settings Modal Management
|
|
2
|
+
const SettingsManager = {
|
|
3
|
+
storageKey: 'drfToMkdocSettings',
|
|
4
|
+
headersStorageKey: 'drfToMkdocHeaders', // Separate key for headers
|
|
5
|
+
usePersistentHeaders: false, // Track whether user opted in to persistent storage
|
|
6
|
+
|
|
7
|
+
// Open settings modal
|
|
8
|
+
openSettingsModal: function() {
|
|
9
|
+
const modal = document.getElementById('settingsModal');
|
|
10
|
+
if (!modal) return;
|
|
11
|
+
|
|
12
|
+
// Load and populate current settings
|
|
13
|
+
this.loadSettings();
|
|
14
|
+
|
|
15
|
+
// Show modal
|
|
16
|
+
modal.classList.add('show');
|
|
17
|
+
document.body.style.overflow = 'hidden';
|
|
18
|
+
|
|
19
|
+
// Focus on first input after modal transition
|
|
20
|
+
const firstInput = modal.querySelector('#settingsHost');
|
|
21
|
+
if (firstInput) {
|
|
22
|
+
// Wait for CSS transition to complete before focusing
|
|
23
|
+
modal.addEventListener('transitionend', () => firstInput.focus(), { once: true });
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
// Close settings modal
|
|
28
|
+
closeSettingsModal: function() {
|
|
29
|
+
const modal = document.getElementById('settingsModal');
|
|
30
|
+
if (!modal) return;
|
|
31
|
+
|
|
32
|
+
modal.classList.remove('show');
|
|
33
|
+
document.body.style.overflow = '';
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// Get current browser host
|
|
37
|
+
getDefaultHost: function() {
|
|
38
|
+
return window.location.origin;
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
// Helper to load host and headers from storage
|
|
42
|
+
_loadStoredSettings: function() {
|
|
43
|
+
// Load host from localStorage (non-sensitive)
|
|
44
|
+
let host = this.getDefaultHost();
|
|
45
|
+
let usePersistentHeaders = false;
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const savedHost = localStorage.getItem(this.storageKey);
|
|
49
|
+
if (savedHost) {
|
|
50
|
+
const parsed = JSON.parse(savedHost);
|
|
51
|
+
host = parsed.host || this.getDefaultHost();
|
|
52
|
+
usePersistentHeaders = parsed.persistHeaders === true;
|
|
53
|
+
this.usePersistentHeaders = usePersistentHeaders;
|
|
54
|
+
}
|
|
55
|
+
} catch (e) {
|
|
56
|
+
console.warn('Failed to parse saved host settings:', e);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Load headers from sessionStorage by default (sensitive)
|
|
60
|
+
// Fall back to localStorage if persistHeaders was enabled
|
|
61
|
+
let headers = {};
|
|
62
|
+
try {
|
|
63
|
+
const sessionHeaders = sessionStorage.getItem(this.headersStorageKey);
|
|
64
|
+
if (sessionHeaders) {
|
|
65
|
+
headers = JSON.parse(sessionHeaders);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Also check localStorage if persistHeaders was enabled
|
|
69
|
+
if (usePersistentHeaders) {
|
|
70
|
+
const saved = localStorage.getItem(this.storageKey);
|
|
71
|
+
if (saved) {
|
|
72
|
+
const parsed = JSON.parse(saved);
|
|
73
|
+
if (parsed.headers) {
|
|
74
|
+
// Merge: sessionStorage takes precedence, then localStorage
|
|
75
|
+
headers = { ...parsed.headers, ...headers };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} catch (e) {
|
|
80
|
+
console.warn('Failed to parse saved headers:', e);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
host: host,
|
|
85
|
+
headers: headers,
|
|
86
|
+
usePersistentHeaders: usePersistentHeaders
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// Load settings from storage and populate UI
|
|
91
|
+
loadSettings: function() {
|
|
92
|
+
// Load stored settings using helper
|
|
93
|
+
const settings = this._loadStoredSettings();
|
|
94
|
+
|
|
95
|
+
// Populate host input
|
|
96
|
+
const hostInput = document.getElementById('settingsHost');
|
|
97
|
+
if (hostInput) {
|
|
98
|
+
hostInput.value = settings.host;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Populate persist headers checkbox
|
|
102
|
+
const persistCheckbox = document.getElementById('persistHeaders');
|
|
103
|
+
if (persistCheckbox) {
|
|
104
|
+
persistCheckbox.checked = settings.usePersistentHeaders;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Clear all header items
|
|
108
|
+
const headerList = document.querySelector('#settingsHeaders .header-list');
|
|
109
|
+
if (headerList) {
|
|
110
|
+
headerList.innerHTML = '';
|
|
111
|
+
|
|
112
|
+
// Add saved headers or one empty item
|
|
113
|
+
const headerEntries = Object.entries(settings.headers);
|
|
114
|
+
if (headerEntries.length > 0) {
|
|
115
|
+
headerEntries.forEach(([name, value]) => {
|
|
116
|
+
const headerItem = this.createHeaderItem(name, value);
|
|
117
|
+
headerList.appendChild(headerItem);
|
|
118
|
+
});
|
|
119
|
+
} else {
|
|
120
|
+
// Add one empty header item
|
|
121
|
+
headerList.appendChild(this.createHeaderItem('', ''));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return settings;
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
// Save settings to storage
|
|
129
|
+
saveSettings: function() {
|
|
130
|
+
const hostInput = document.getElementById('settingsHost');
|
|
131
|
+
const host = hostInput?.value.trim() || this.getDefaultHost();
|
|
132
|
+
|
|
133
|
+
// Get persist headers preference
|
|
134
|
+
const persistCheckbox = document.getElementById('persistHeaders');
|
|
135
|
+
this.usePersistentHeaders = persistCheckbox?.checked === true;
|
|
136
|
+
|
|
137
|
+
// Collect headers
|
|
138
|
+
const headers = {};
|
|
139
|
+
const headerItems = document.querySelectorAll('#settingsHeaders .header-item');
|
|
140
|
+
headerItems.forEach(item => {
|
|
141
|
+
const nameInput = item.querySelector('.name-input');
|
|
142
|
+
const valueInput = item.querySelector('.value-input');
|
|
143
|
+
const name = nameInput?.value.trim();
|
|
144
|
+
const value = valueInput?.value.trim();
|
|
145
|
+
|
|
146
|
+
if (name && value) {
|
|
147
|
+
headers[name] = value;
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
// Save host to localStorage (non-sensitive)
|
|
153
|
+
const hostSettings = {
|
|
154
|
+
host: host,
|
|
155
|
+
persistHeaders: this.usePersistentHeaders
|
|
156
|
+
};
|
|
157
|
+
localStorage.setItem(this.storageKey, JSON.stringify(hostSettings));
|
|
158
|
+
|
|
159
|
+
// Save headers based on user preference
|
|
160
|
+
if (this.usePersistentHeaders) {
|
|
161
|
+
// Save to localStorage (less secure, but user opted in)
|
|
162
|
+
const allSettings = {
|
|
163
|
+
...hostSettings,
|
|
164
|
+
headers: headers
|
|
165
|
+
};
|
|
166
|
+
localStorage.setItem(this.storageKey, JSON.stringify(allSettings));
|
|
167
|
+
// Also save to sessionStorage for immediate use
|
|
168
|
+
sessionStorage.setItem(this.headersStorageKey, JSON.stringify(headers));
|
|
169
|
+
} else {
|
|
170
|
+
// Save to sessionStorage only (more secure, cleared on tab close)
|
|
171
|
+
sessionStorage.setItem(this.headersStorageKey, JSON.stringify(headers));
|
|
172
|
+
// Remove headers from localStorage if they were there
|
|
173
|
+
const existing = localStorage.getItem(this.storageKey);
|
|
174
|
+
if (existing) {
|
|
175
|
+
try {
|
|
176
|
+
const parsed = JSON.parse(existing);
|
|
177
|
+
delete parsed.headers;
|
|
178
|
+
localStorage.setItem(this.storageKey, JSON.stringify({
|
|
179
|
+
...parsed,
|
|
180
|
+
...hostSettings
|
|
181
|
+
}));
|
|
182
|
+
} catch (e) {
|
|
183
|
+
// If parsing fails, just save host settings
|
|
184
|
+
localStorage.setItem(this.storageKey, JSON.stringify(hostSettings));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Close modal
|
|
190
|
+
this.closeSettingsModal();
|
|
191
|
+
|
|
192
|
+
// Show success feedback
|
|
193
|
+
const storageType = this.usePersistentHeaders ? 'localStorage' : 'sessionStorage';
|
|
194
|
+
this.showToast(`Settings saved (headers in ${storageType})`, 'success');
|
|
195
|
+
|
|
196
|
+
// Reload settings in try-out form if it exists
|
|
197
|
+
if (window.FormManager && typeof window.FormManager.loadSettings === 'function') {
|
|
198
|
+
window.FormManager.loadSettings();
|
|
199
|
+
}
|
|
200
|
+
} catch (e) {
|
|
201
|
+
console.error('Failed to save settings:', e);
|
|
202
|
+
this.showToast('Failed to save settings', 'error');
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
// Get saved settings (reads from both storage locations)
|
|
207
|
+
getSettings: function() {
|
|
208
|
+
// Use helper method to load settings
|
|
209
|
+
const settings = this._loadStoredSettings();
|
|
210
|
+
return {
|
|
211
|
+
host: settings.host,
|
|
212
|
+
headers: settings.headers
|
|
213
|
+
};
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
// Add header field
|
|
217
|
+
addHeaderField: function(name = '', value = '') {
|
|
218
|
+
const headerList = document.querySelector('#settingsHeaders .header-list');
|
|
219
|
+
if (!headerList) return;
|
|
220
|
+
|
|
221
|
+
const headerItem = this.createHeaderItem(name, value);
|
|
222
|
+
headerList.appendChild(headerItem);
|
|
223
|
+
|
|
224
|
+
// Focus on the first input
|
|
225
|
+
const nameInput = headerItem.querySelector('.name-input');
|
|
226
|
+
if (nameInput) {
|
|
227
|
+
nameInput.focus();
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
// Create header item element (using DOM creation to prevent XSS)
|
|
232
|
+
createHeaderItem: function(name = '', value = '') {
|
|
233
|
+
const headerItem = document.createElement('div');
|
|
234
|
+
headerItem.className = 'header-item';
|
|
235
|
+
|
|
236
|
+
// Create header-inputs container
|
|
237
|
+
const headerInputs = document.createElement('div');
|
|
238
|
+
headerInputs.className = 'header-inputs';
|
|
239
|
+
|
|
240
|
+
// Create name input
|
|
241
|
+
const nameInput = document.createElement('input');
|
|
242
|
+
nameInput.type = 'text';
|
|
243
|
+
nameInput.className = 'modern-input name-input';
|
|
244
|
+
nameInput.placeholder = 'Header name';
|
|
245
|
+
nameInput.setAttribute('list', 'settingsHeaderSuggestions');
|
|
246
|
+
nameInput.value = name;
|
|
247
|
+
|
|
248
|
+
// Create value input
|
|
249
|
+
const valueInput = document.createElement('input');
|
|
250
|
+
valueInput.type = 'text';
|
|
251
|
+
valueInput.className = 'modern-input value-input';
|
|
252
|
+
valueInput.placeholder = 'Header value';
|
|
253
|
+
valueInput.value = value;
|
|
254
|
+
|
|
255
|
+
// Create remove button
|
|
256
|
+
const removeBtn = document.createElement('button');
|
|
257
|
+
removeBtn.className = 'remove-btn';
|
|
258
|
+
removeBtn.setAttribute('aria-label', 'Remove header');
|
|
259
|
+
|
|
260
|
+
// Create icon span
|
|
261
|
+
const iconSpan = document.createElement('span');
|
|
262
|
+
iconSpan.className = 'icon';
|
|
263
|
+
iconSpan.textContent = '✕';
|
|
264
|
+
|
|
265
|
+
// Append icon to button
|
|
266
|
+
removeBtn.appendChild(iconSpan);
|
|
267
|
+
|
|
268
|
+
// Attach click handler with addEventListener
|
|
269
|
+
removeBtn.addEventListener('click', () => {
|
|
270
|
+
SettingsManager.removeHeaderField(removeBtn);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// Append inputs and button to header-inputs
|
|
274
|
+
headerInputs.appendChild(nameInput);
|
|
275
|
+
headerInputs.appendChild(valueInput);
|
|
276
|
+
headerInputs.appendChild(removeBtn);
|
|
277
|
+
|
|
278
|
+
// Append header-inputs to header-item
|
|
279
|
+
headerItem.appendChild(headerInputs);
|
|
280
|
+
|
|
281
|
+
return headerItem;
|
|
282
|
+
},
|
|
283
|
+
|
|
284
|
+
// Remove header field
|
|
285
|
+
removeHeaderField: function(button) {
|
|
286
|
+
const headerItem = button.closest('.header-item');
|
|
287
|
+
if (headerItem) {
|
|
288
|
+
const headerList = document.querySelector('#settingsHeaders .header-list');
|
|
289
|
+
const remainingItems = headerList?.querySelectorAll('.header-item') || [];
|
|
290
|
+
|
|
291
|
+
// Don't allow removing if it's the last item
|
|
292
|
+
if (remainingItems.length > 1) {
|
|
293
|
+
headerItem.remove();
|
|
294
|
+
} else {
|
|
295
|
+
// If it's the last item, just clear the inputs
|
|
296
|
+
const nameInput = headerItem.querySelector('.name-input');
|
|
297
|
+
const valueInput = headerItem.querySelector('.value-input');
|
|
298
|
+
if (nameInput) nameInput.value = '';
|
|
299
|
+
if (valueInput) valueInput.value = '';
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
|
|
304
|
+
// Show toast notification
|
|
305
|
+
showToast: function(message, type = 'success') {
|
|
306
|
+
// Create or get toast element
|
|
307
|
+
let toast = document.getElementById('settingsToast');
|
|
308
|
+
if (!toast) {
|
|
309
|
+
toast = document.createElement('div');
|
|
310
|
+
toast.id = 'settingsToast';
|
|
311
|
+
toast.className = 'settings-toast';
|
|
312
|
+
document.body.appendChild(toast);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
toast.textContent = message;
|
|
316
|
+
toast.className = `settings-toast toast-${type}`;
|
|
317
|
+
toast.classList.add('show');
|
|
318
|
+
|
|
319
|
+
setTimeout(() => {
|
|
320
|
+
toast.classList.remove('show');
|
|
321
|
+
}, 3000);
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
// Initialize
|
|
325
|
+
init: function() {
|
|
326
|
+
// Close modal on overlay click
|
|
327
|
+
const modal = document.getElementById('settingsModal');
|
|
328
|
+
if (modal) {
|
|
329
|
+
const overlay = modal.querySelector('.modal-overlay');
|
|
330
|
+
if (overlay) {
|
|
331
|
+
overlay.addEventListener('click', (e) => {
|
|
332
|
+
if (e.target === overlay) {
|
|
333
|
+
this.closeSettingsModal();
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Close modal on Escape key (with stored handler to prevent duplicates)
|
|
339
|
+
if (!this._onKeydown) {
|
|
340
|
+
this._onKeydown = function(e) {
|
|
341
|
+
if (e.key === 'Escape' && modal && modal.classList.contains('show')) {
|
|
342
|
+
SettingsManager.closeSettingsModal();
|
|
343
|
+
}
|
|
344
|
+
}.bind(this);
|
|
345
|
+
document.addEventListener('keydown', this._onKeydown);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
// Initialize on DOM load
|
|
352
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
353
|
+
SettingsManager.init();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// Global functions for onclick handlers
|
|
357
|
+
window.openSettingsModal = () => SettingsManager.openSettingsModal();
|
|
358
|
+
window.closeSettingsModal = () => SettingsManager.closeSettingsModal();
|
|
359
|
+
|
|
360
|
+
// Export for global access
|
|
361
|
+
window.SettingsManager = SettingsManager;
|
|
@@ -5,6 +5,113 @@ const FormManager = {
|
|
|
5
5
|
this.setupEventListeners();
|
|
6
6
|
this.setupFormValidation();
|
|
7
7
|
this.initializeRequestBody();
|
|
8
|
+
this.loadSettings();
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
// Load settings and pre-fill form
|
|
12
|
+
loadSettings: function() {
|
|
13
|
+
// Get saved settings if available - try SettingsManager first, then storage as fallback
|
|
14
|
+
let settings = { host: null, headers: {} };
|
|
15
|
+
|
|
16
|
+
if (window.SettingsManager) {
|
|
17
|
+
settings = window.SettingsManager.getSettings();
|
|
18
|
+
} else {
|
|
19
|
+
// Fallback: read from storage (host from localStorage, headers from sessionStorage)
|
|
20
|
+
try {
|
|
21
|
+
// Get host from localStorage
|
|
22
|
+
const savedHost = localStorage.getItem('drfToMkdocSettings');
|
|
23
|
+
if (savedHost) {
|
|
24
|
+
const parsed = JSON.parse(savedHost);
|
|
25
|
+
settings.host = parsed.host || null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Get headers from sessionStorage (secure by default)
|
|
29
|
+
const savedHeaders = sessionStorage.getItem('drfToMkdocHeaders');
|
|
30
|
+
if (savedHeaders) {
|
|
31
|
+
settings.headers = JSON.parse(savedHeaders);
|
|
32
|
+
} else {
|
|
33
|
+
// Fallback: check localStorage if persistHeaders was enabled
|
|
34
|
+
if (savedHost) {
|
|
35
|
+
const parsed = JSON.parse(savedHost);
|
|
36
|
+
if (parsed.persistHeaders === true && parsed.headers) {
|
|
37
|
+
settings.headers = parsed.headers;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
} catch (e) {
|
|
42
|
+
console.warn('Failed to parse settings from storage:', e);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Set host in baseUrl input
|
|
47
|
+
const baseUrlInput = document.getElementById('baseUrl');
|
|
48
|
+
if (baseUrlInput) {
|
|
49
|
+
// Priority: settings.host > browser origin > localhost:8000
|
|
50
|
+
if (settings.host && settings.host.trim()) {
|
|
51
|
+
let hostValue = settings.host.trim();
|
|
52
|
+
// Ensure host has protocol (add https:// if missing for secure default)
|
|
53
|
+
if (!hostValue.match(/^https?:\/\//)) {
|
|
54
|
+
hostValue = 'https://' + hostValue;
|
|
55
|
+
}
|
|
56
|
+
baseUrlInput.value = hostValue;
|
|
57
|
+
} else {
|
|
58
|
+
const browserOrigin = window.location.origin;
|
|
59
|
+
if (browserOrigin) {
|
|
60
|
+
baseUrlInput.value = browserOrigin;
|
|
61
|
+
} else {
|
|
62
|
+
baseUrlInput.value = 'http://localhost:8000';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Add headers from settings - update existing ones or add new ones
|
|
68
|
+
if (settings.headers && Object.keys(settings.headers).length > 0) {
|
|
69
|
+
const headerList = document.querySelector('#requestHeaders .header-list');
|
|
70
|
+
if (headerList) {
|
|
71
|
+
// Create a map of existing headers by name (case-insensitive)
|
|
72
|
+
const existingHeadersMap = new Map();
|
|
73
|
+
headerList.querySelectorAll('.header-item').forEach(item => {
|
|
74
|
+
const nameInput = item.querySelector('.name-input');
|
|
75
|
+
const valueInput = item.querySelector('.value-input');
|
|
76
|
+
if (nameInput && nameInput.value.trim()) {
|
|
77
|
+
const headerName = nameInput.value.trim();
|
|
78
|
+
existingHeadersMap.set(headerName.toLowerCase(), {
|
|
79
|
+
item: item,
|
|
80
|
+
nameInput: nameInput,
|
|
81
|
+
valueInput: valueInput
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Process headers from settings
|
|
87
|
+
Object.entries(settings.headers).forEach(([name, value]) => {
|
|
88
|
+
if (!name.trim() || !value || !String(value).trim()) {
|
|
89
|
+
return; // Skip empty names or values
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const normalizedName = name.trim().toLowerCase();
|
|
93
|
+
const headerValue = String(value).trim();
|
|
94
|
+
|
|
95
|
+
// Check if header already exists (case-insensitive)
|
|
96
|
+
const existing = existingHeadersMap.get(normalizedName);
|
|
97
|
+
|
|
98
|
+
if (existing) {
|
|
99
|
+
// Update existing header value
|
|
100
|
+
if (existing.valueInput) {
|
|
101
|
+
existing.valueInput.value = headerValue;
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
// Add new header item
|
|
105
|
+
const headerItem = this.createHeaderItem();
|
|
106
|
+
const nameInput = headerItem.querySelector('.name-input');
|
|
107
|
+
const valueInput = headerItem.querySelector('.value-input');
|
|
108
|
+
if (nameInput) nameInput.value = name.trim();
|
|
109
|
+
if (valueInput) valueInput.value = headerValue;
|
|
110
|
+
headerList.appendChild(headerItem);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
8
115
|
},
|
|
9
116
|
|
|
10
117
|
initializeRequestBody: function() {
|
|
@@ -203,24 +310,36 @@ const FormManager = {
|
|
|
203
310
|
const headerItem = document.createElement('div');
|
|
204
311
|
headerItem.className = 'header-item';
|
|
205
312
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
313
|
+
const headerInputs = document.createElement('div');
|
|
314
|
+
headerInputs.className = 'header-inputs';
|
|
315
|
+
|
|
316
|
+
const nameInput = document.createElement('input');
|
|
317
|
+
nameInput.type = 'text';
|
|
318
|
+
nameInput.className = 'modern-input name-input';
|
|
319
|
+
nameInput.placeholder = 'Header name';
|
|
320
|
+
nameInput.setAttribute('list', 'headerSuggestions');
|
|
321
|
+
|
|
322
|
+
const valueInput = document.createElement('input');
|
|
323
|
+
valueInput.type = 'text';
|
|
324
|
+
valueInput.className = 'modern-input value-input';
|
|
325
|
+
valueInput.placeholder = 'Header value';
|
|
326
|
+
|
|
327
|
+
const removeBtn = document.createElement('button');
|
|
328
|
+
removeBtn.className = 'remove-btn';
|
|
329
|
+
removeBtn.setAttribute('aria-label', 'Remove header');
|
|
330
|
+
|
|
331
|
+
const iconSpan = document.createElement('span');
|
|
332
|
+
iconSpan.className = 'icon';
|
|
333
|
+
iconSpan.textContent = '✕';
|
|
334
|
+
|
|
335
|
+
removeBtn.appendChild(iconSpan);
|
|
336
|
+
|
|
337
|
+
headerInputs.appendChild(nameInput);
|
|
338
|
+
headerInputs.appendChild(valueInput);
|
|
339
|
+
headerInputs.appendChild(removeBtn);
|
|
340
|
+
|
|
341
|
+
headerItem.appendChild(headerInputs);
|
|
221
342
|
|
|
222
|
-
// Attach the removal handler programmatically
|
|
223
|
-
const removeBtn = headerItem.querySelector('.remove-btn');
|
|
224
343
|
removeBtn.addEventListener('click', (e) => FormManager.removeKvItem(e.currentTarget));
|
|
225
344
|
|
|
226
345
|
return headerItem;
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
margin-top: 20px;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
.filter-
|
|
102
|
+
.filter-settings,
|
|
103
103
|
.filter-clear {
|
|
104
104
|
padding: 10px 20px;
|
|
105
105
|
border-radius: 8px;
|
|
@@ -112,20 +112,25 @@
|
|
|
112
112
|
display: inline-flex;
|
|
113
113
|
align-items: center;
|
|
114
114
|
justify-content: center;
|
|
115
|
+
gap: 6px;
|
|
115
116
|
}
|
|
116
117
|
|
|
117
|
-
.filter-
|
|
118
|
+
.filter-settings {
|
|
118
119
|
background: var(--accent-primary);
|
|
119
120
|
color: #fff;
|
|
120
121
|
}
|
|
121
122
|
|
|
123
|
+
.filter-settings .icon {
|
|
124
|
+
font-size: 16px;
|
|
125
|
+
}
|
|
126
|
+
|
|
122
127
|
.filter-clear {
|
|
123
128
|
background: transparent;
|
|
124
129
|
color: var(--text-secondary);
|
|
125
130
|
border: 1px solid var(--border-color);
|
|
126
131
|
}
|
|
127
132
|
|
|
128
|
-
.filter-
|
|
133
|
+
.filter-settings:hover {
|
|
129
134
|
background: var(--accent-secondary);
|
|
130
135
|
transform: translateY(-1px);
|
|
131
136
|
}
|
|
@@ -136,7 +141,7 @@
|
|
|
136
141
|
background: rgba(59, 130, 246, 0.05);
|
|
137
142
|
}
|
|
138
143
|
|
|
139
|
-
.filter-
|
|
144
|
+
.filter-settings:active,
|
|
140
145
|
.filter-clear:active {
|
|
141
146
|
transform: translateY(0);
|
|
142
147
|
}
|
|
@@ -169,7 +174,7 @@
|
|
|
169
174
|
gap: 8px;
|
|
170
175
|
}
|
|
171
176
|
|
|
172
|
-
.filter-
|
|
177
|
+
.filter-settings,
|
|
173
178
|
.filter-clear {
|
|
174
179
|
width: 100%;
|
|
175
180
|
}
|