drf-to-mkdoc 0.2.3__py3-none-any.whl → 0.2.4__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.

Files changed (33) hide show
  1. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/field-sections-loader.js +29 -0
  2. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/query-parameters-loader.js +16 -0
  3. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/field-extractor.js +200 -0
  4. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/form-manager.js +307 -14
  5. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/main.js +39 -11
  6. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/modal.js +298 -18
  7. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/query-parameters-extractor.js +94 -0
  8. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/request-executor.js +278 -62
  9. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/response-modal.js +173 -0
  10. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/suggestions.js +59 -152
  11. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/tabs.js +52 -9
  12. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/badges.css +13 -5
  13. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/theme-toggle.css +297 -25
  14. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/try-out/fab.css +204 -0
  15. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/try-out/response.css +323 -0
  16. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/try-out/variables.css +139 -0
  17. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/field-sections.css +136 -0
  18. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/form.css +539 -0
  19. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/modal.css +239 -17
  20. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/response.css +503 -43
  21. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/tabs.css +71 -19
  22. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/variables.css +71 -15
  23. drf_to_mkdoc/templates/endpoints/detail/request_body.html +2 -0
  24. drf_to_mkdoc/templates/try-out/fab.html +67 -3
  25. drf_to_mkdoc/templates/try-out/form.html +221 -74
  26. drf_to_mkdoc/templates/try-out/modal.html +75 -7
  27. drf_to_mkdoc/templates/try-out/response-modal.html +138 -9
  28. drf_to_mkdoc/utils/endpoint_detail_generator.py +1 -0
  29. {drf_to_mkdoc-0.2.3.dist-info → drf_to_mkdoc-0.2.4.dist-info}/METADATA +68 -9
  30. {drf_to_mkdoc-0.2.3.dist-info → drf_to_mkdoc-0.2.4.dist-info}/RECORD +33 -24
  31. {drf_to_mkdoc-0.2.3.dist-info → drf_to_mkdoc-0.2.4.dist-info}/WHEEL +0 -0
  32. {drf_to_mkdoc-0.2.3.dist-info → drf_to_mkdoc-0.2.4.dist-info}/licenses/LICENSE +0 -0
  33. {drf_to_mkdoc-0.2.3.dist-info → drf_to_mkdoc-0.2.4.dist-info}/top_level.txt +0 -0
@@ -1,50 +1,102 @@
1
- /* Tabs */
2
- .tabs {
1
+ /* Smart Tabs */
2
+ .smart-tabs {
3
3
  display: flex;
4
- gap: 0;
4
+ gap: var(--try-out-spacing-xs);
5
+ border-bottom: 1px solid var(--try-out-border);
6
+ padding-bottom: var(--try-out-spacing-xs);
5
7
  margin-bottom: var(--try-out-spacing-lg);
6
- border: 1px solid var(--try-out-border);
7
- border-radius: var(--try-out-border-radius);
8
- overflow: hidden;
9
- background-color: var(--try-out-surface);
10
8
  }
11
9
 
12
10
  .tab {
13
- flex: 1;
14
- padding: var(--try-out-spacing) var(--try-out-spacing-lg);
15
- border: none;
11
+ display: flex;
12
+ align-items: center;
13
+ gap: var(--try-out-spacing-xs);
14
+ padding: var(--try-out-spacing-sm) var(--try-out-spacing);
15
+ border-radius: var(--try-out-border-radius);
16
16
  background: transparent;
17
+ border: none;
17
18
  color: var(--try-out-text-light);
18
19
  cursor: pointer;
20
+ transition: var(--try-out-transition);
19
21
  font-size: var(--try-out-font-base);
20
22
  font-weight: 500;
21
23
  position: relative;
22
- transition: var(--try-out-transition);
23
- border-right: 1px solid var(--try-out-border);
24
24
  }
25
25
 
26
- .tab:last-child {
27
- border-right: none;
28
- }
29
-
30
- .tab:hover {
31
- background-color: rgba(25, 118, 210, 0.05);
26
+ .tab:hover:not(.active) {
27
+ background: var(--try-out-surface);
32
28
  color: var(--try-out-text);
33
29
  }
34
30
 
35
31
  .tab.active {
36
- background-color: var(--try-out-primary);
32
+ background: var(--try-out-primary);
37
33
  color: white;
34
+ box-shadow: var(--try-out-shadow-md);
35
+ }
36
+
37
+ .tab .icon {
38
+ font-size: 0.9rem;
39
+ line-height: 1;
40
+ }
41
+
42
+ .badge {
43
+ background: rgba(255, 255, 255, 0.2);
44
+ padding: 2px 6px;
45
+ border-radius: 10px;
46
+ font-size: 0.7em;
47
+ font-weight: 600;
48
+ line-height: 1;
49
+ }
50
+
51
+ .tab.active .badge {
52
+ background: rgba(255, 255, 255, 0.25);
53
+ }
54
+
55
+ .tab:not(.active) .badge {
56
+ background: var(--try-out-primary-light);
57
+ color: var(--try-out-primary);
38
58
  }
39
59
 
40
60
  .tab-content {
41
61
  display: none;
62
+ animation: fadeIn 0.2s ease-out;
42
63
  }
43
64
 
44
65
  .tab-content.active {
45
66
  display: block;
46
67
  }
47
68
 
69
+ /* Response Tabs */
70
+ .response-tabs {
71
+ display: flex;
72
+ gap: var(--try-out-spacing-xs);
73
+ border-bottom: 1px solid var(--try-out-border);
74
+ padding-bottom: var(--try-out-spacing-xs);
75
+ margin-bottom: var(--try-out-spacing);
76
+ }
77
+
78
+ .response-tabs .tab {
79
+ padding: var(--try-out-spacing-xs) var(--try-out-spacing-sm);
80
+ font-size: var(--try-out-font-sm);
81
+ }
82
+
83
+ /* Tab Focus States */
84
+ .tab:focus-visible {
85
+ outline: 2px solid var(--try-out-primary);
86
+ outline-offset: 2px;
87
+ }
88
+
89
+ @keyframes fadeIn {
90
+ from {
91
+ opacity: 0;
92
+ transform: translateY(10px);
93
+ }
94
+ to {
95
+ opacity: 1;
96
+ transform: translateY(0);
97
+ }
98
+ }
99
+
48
100
  /* Mobile Tabs */
49
101
  @media screen and (max-width: 768px) {
50
102
  .tabs {
@@ -1,6 +1,9 @@
1
1
  :root {
2
+ /* Color Palette */
2
3
  --try-out-primary: #1976d2;
3
4
  --try-out-primary-hover: #1565c0;
5
+ --try-out-primary-rgb: 25, 118, 210;
6
+ --try-out-primary-light: rgba(25, 118, 210, 0.1);
4
7
  --try-out-success: #28a745;
5
8
  --try-out-success-hover: #218838;
6
9
  --try-out-danger: #dc3545;
@@ -8,31 +11,84 @@
8
11
  --try-out-warning: #ffc107;
9
12
  --try-out-info: #17a2b8;
10
13
 
14
+ /* Light Theme Colors */
11
15
  --try-out-bg: #ffffff;
12
16
  --try-out-surface: #f8f9fa;
17
+ --try-out-surface-raised: #ffffff;
13
18
  --try-out-border: #e1e5e9;
14
19
  --try-out-text: #333333;
15
20
  --try-out-text-light: #666666;
16
21
  --try-out-text-lighter: #999999;
17
22
  --try-out-shadow: rgba(0, 0, 0, 0.1);
18
23
  --try-out-shadow-strong: rgba(0, 0, 0, 0.2);
24
+ --try-out-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.15);
25
+ --try-out-shadow-lg: 0 8px 25px rgba(0, 0, 0, 0.15);
26
+ --try-out-shadow-xl: 0 20px 40px rgba(0, 0, 0, 0.2);
19
27
 
20
- --try-out-border-radius: 4px;
21
- --try-out-border-radius-lg: 6px;
22
- --try-out-spacing-xs: 0.2rem;
23
- --try-out-spacing-sm: 0.4rem;
24
- --try-out-spacing: 0.6rem;
25
- --try-out-spacing-lg: 0.8rem;
26
- --try-out-spacing-xl: 1.2rem;
27
- --try-out-spacing-xxl: 1.6rem;
28
-
29
- --try-out-font-sm: 0.75rem;
30
- --try-out-font-base: 0.8rem;
31
- --try-out-font-lg: 0.9rem;
32
- --try-out-line-height: 1.4;
33
-
34
- --try-out-transition: all 0.2s ease;
28
+ /* Glass Effect */
29
+ --try-out-glass-bg: rgba(255, 255, 255, 0.95);
30
+ --try-out-glass-border: rgba(255, 255, 255, 0.2);
31
+ --try-out-backdrop-blur: 10px;
32
+
33
+ /* Gradients */
34
+ --try-out-gradient-primary: linear-gradient(135deg, var(--try-out-primary), var(--try-out-primary-hover));
35
+
36
+ /* Layout */
37
+ --try-out-border-radius: 6px;
38
+ --try-out-border-radius-lg: 10px;
39
+ --try-out-spacing-2xs: 0.125rem;
40
+ --try-out-spacing-xs: 0.25rem;
41
+ --try-out-spacing-sm: 0.5rem;
42
+ --try-out-spacing: 0.75rem;
43
+ --try-out-spacing-lg: 1rem;
44
+ --try-out-spacing-xl: 1.5rem;
45
+ --try-out-spacing-xxl: 2rem;
46
+
47
+ /* Typography */
48
+ --try-out-font-sm: 0.875rem;
49
+ --try-out-font-base: 0.9rem;
50
+ --try-out-font-lg: 1rem;
51
+ --try-out-line-height: 1.5;
52
+
53
+ /* Animation */
54
+ --try-out-transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
55
+ --theme-transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
56
+
57
+ /* Z-Index */
35
58
  --try-out-z-modal: 1000;
36
59
  --try-out-z-response: 1100;
37
60
  --try-out-z-fab: 100;
61
+ --try-out-z-sticky: 50;
62
+ }
63
+
64
+ /* Dark Theme Colors */
65
+ [data-md-color-scheme="slate"] {
66
+ --try-out-bg: #1a1a1a;
67
+ --try-out-surface: #2d2d2d;
68
+ --try-out-surface-raised: #3a3a3a;
69
+ --try-out-border: #404040;
70
+ --try-out-text: #e0e0e0;
71
+ --try-out-text-light: #b0b0b0;
72
+ --try-out-text-lighter: #808080;
73
+ --try-out-shadow: rgba(0, 0, 0, 0.3);
74
+ --try-out-shadow-strong: rgba(0, 0, 0, 0.5);
75
+ --try-out-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.3);
76
+ --try-out-shadow-lg: 0 8px 25px rgba(0, 0, 0, 0.4);
77
+ --try-out-shadow-xl: 0 20px 40px rgba(0, 0, 0, 0.5);
78
+
79
+ /* Glass Effect for Dark Mode */
80
+ --try-out-glass-bg: rgba(45, 45, 45, 0.95);
81
+ --try-out-glass-border: rgba(255, 255, 255, 0.1);
82
+
83
+ /* Adjust primary colors for better contrast */
84
+ --try-out-primary: #42a5f5;
85
+ --try-out-primary-hover: #2196f3;
86
+ --try-out-primary-rgb: 66, 165, 245;
87
+ --try-out-primary-light: rgba(66, 165, 245, 0.15);
88
+
89
+ /* Status colors for dark mode */
90
+ --try-out-success: #4caf50;
91
+ --try-out-danger: #f44336;
92
+ --try-out-warning: #ff9800;
93
+ --try-out-info: #00bcd4;
38
94
  }
@@ -4,7 +4,9 @@
4
4
  ## Request Body
5
5
 
6
6
  {% if request_example %}
7
+ <div class="request-example" data-example='{{ request_example|format_json }}'>
7
8
  {{ request_example|format_json }}
9
+ </div>
8
10
  {% endif %}
9
11
 
10
12
  {% endif %}
@@ -1,4 +1,68 @@
1
1
  <!-- Try Out Floating Action Button -->
2
- <button class="try-out-fab" role="button" tabindex="0" aria-label="Open Try It Out" onclick="TryOutSidebar.openTryOut()">
3
- <span>🚀</span>
4
- </button>
2
+ <div class="try-out-fab-container">
3
+ <button
4
+ class="try-out-fab"
5
+ role="button"
6
+ tabindex="0"
7
+ aria-label="Open Try It Out"
8
+ aria-expanded="false"
9
+ onclick="TryOutSidebar.openTryOut()"
10
+ data-tooltip="Try out this endpoint"
11
+ >
12
+ <div class="fab-content">
13
+ <span class="fab-icon">🚀</span>
14
+ </div>
15
+ <div class="ripple-container"></div>
16
+ <div class="pulse-ring"></div>
17
+ </button>
18
+ </div>
19
+
20
+ <script>
21
+ document.addEventListener('DOMContentLoaded', function() {
22
+ // Ripple effect
23
+ const fab = document.querySelector('.try-out-fab');
24
+
25
+ fab.addEventListener('click', function(e) {
26
+ const ripple = document.createElement('div');
27
+ ripple.className = 'ripple';
28
+
29
+ const rect = this.getBoundingClientRect();
30
+ const size = Math.max(rect.width, rect.height);
31
+ const x = e.clientX - rect.left - size / 2;
32
+ const y = e.clientY - rect.top - size / 2;
33
+
34
+ ripple.style.width = ripple.style.height = `${size}px`;
35
+ ripple.style.left = `${x}px`;
36
+ ripple.style.top = `${y}px`;
37
+
38
+ this.querySelector('.ripple-container').appendChild(ripple);
39
+
40
+ setTimeout(() => ripple.remove(), 600);
41
+ });
42
+
43
+ // Keyboard shortcut
44
+ document.addEventListener('keydown', function(e) {
45
+ if (e.ctrlKey && e.code === 'Space') {
46
+ e.preventDefault();
47
+ TryOutSidebar.openTryOut();
48
+
49
+ // Animate the FAB
50
+ fab.classList.add('keyboard-triggered');
51
+ setTimeout(() => fab.classList.remove('keyboard-triggered'), 200);
52
+ }
53
+ });
54
+
55
+ // Show/hide shortcut hint
56
+ let hintTimeout;
57
+ fab.addEventListener('mouseenter', function() {
58
+ clearTimeout(hintTimeout);
59
+ document.querySelector('.shortcut-hint').classList.add('visible');
60
+ });
61
+
62
+ fab.addEventListener('mouseleave', function() {
63
+ hintTimeout = setTimeout(() => {
64
+ document.querySelector('.shortcut-hint').classList.remove('visible');
65
+ }, 500);
66
+ });
67
+ });
68
+ </script>
@@ -1,113 +1,260 @@
1
1
  {% load custom_filters %}
2
2
 
3
3
  <div class="try-out-form" data-method="{{ method }}">
4
- <!-- Base URL Section -->
5
- <div class="base-url-section">
6
- <label for="baseUrl">Request URL</label>
7
- <div class="url-display">
8
- <input type="text" id="baseUrl" class="base-url-input" value="http://localhost:8000" placeholder="https://api.example.com">
9
- <span class="path-display">{{ path }}</span>
4
+ <!-- Method Badge and Base URL Section -->
5
+ <div class="request-header">
6
+ <div class="method-badge method-{{ method|lower }}" data-method="{{ method|upper }}">{{ method|upper }}</div>
7
+ <div class="base-url-section">
8
+ <div class="floating-label-group">
9
+ <input type="text"
10
+ id="baseUrl"
11
+ class="base-url-input floating-input"
12
+ value="http://localhost:8000"
13
+ placeholder=" "
14
+ required>
15
+ <label for="baseUrl" class="floating-label">Request URL</label>
16
+ <div class="url-preview">
17
+ <code class="path-display">{{ path }}</code>
18
+ <button class="copy-btn" onclick="copyToClipboard(this)" aria-label="Copy URL">
19
+ <span class="icon">📋</span>
20
+ </button>
21
+ </div>
22
+ </div>
10
23
  </div>
11
24
  </div>
12
25
 
13
- <!-- Tabs -->
14
- <div class="tabs">
15
- <button class="tab active" data-tab="parameters">Parameters</button>
16
- <button class="tab" data-tab="headers">Headers</button>
26
+ <!-- Smart Tabs with Icons -->
27
+ <div class="smart-tabs" role="tablist">
28
+ <button class="tab active"
29
+ id="tab-parameters"
30
+ role="tab"
31
+ aria-selected="true"
32
+ aria-controls="parametersTab"
33
+ data-tab="parameters">
34
+ <span class="icon">📝</span>
35
+ Parameters
36
+ {% if path_params %}<span class="badge">{{ path_params|length }}</span>{% endif %}
37
+ </button>
38
+ <button class="tab"
39
+ id="tab-headers"
40
+ role="tab"
41
+ aria-selected="false"
42
+ aria-controls="headersTab"
43
+ data-tab="headers">
44
+ <span class="icon">📋</span>
45
+ Headers
46
+ </button>
17
47
  {% if method|upper in "POST,PUT,PATCH" %}
18
- <button class="tab" data-tab="body">Request Body</button>
48
+ <button class="tab"
49
+ id="tab-body"
50
+ role="tab"
51
+ aria-selected="false"
52
+ aria-controls="bodyTab"
53
+ data-tab="body">
54
+ <span class="icon">📦</span>
55
+ Request Body
56
+ </button>
19
57
  {% endif %}
20
58
  </div>
21
59
 
22
60
  <!-- Parameters Tab -->
23
- <div class="tab-content active" id="parametersTab">
61
+ <div class="tab-content active"
62
+ id="parametersTab"
63
+ role="tabpanel"
64
+ aria-labelledby="tab-parameters">
24
65
  {% if path_params %}
25
- <div class="form-group">
26
- <label class="form-label">Path Parameters</label>
27
- <div class="kv-container" id="pathParams">
66
+ <div class="form-section">
67
+ <div class="section-header">
68
+ <h4>Path Parameters</h4>
69
+ <div class="tooltip" data-tooltip="Values that are part of the URL path">ℹ️</div>
70
+ </div>
71
+ <div class="parameter-grid" id="pathParams">
28
72
  {% for param in path_params %}
29
- <div class="kv-item">
30
- <label class="param-label{% if param.required %} required{% endif %}">{{ param.name }}</label>
31
- <input type="text"
32
- placeholder="Enter {{ param.name }} value"
33
- data-param="{{ param.name }}"
34
- {% if param.required %}required{% endif %}>
35
- <div class="error-message"></div>
73
+ <div class="parameter-card{% if param.required %} required{% endif %}">
74
+ <div class="parameter-header">
75
+ <label class="param-label">{{ param.name }}</label>
76
+ {% if param.required %}
77
+ <span class="required-badge">Required</span>
78
+ {% endif %}
79
+ </div>
80
+ <div class="parameter-input-group">
81
+ <input type="text"
82
+ class="modern-input"
83
+ placeholder="Enter value"
84
+ data-param="{{ param.name }}"
85
+ {% if param.required %}required{% endif %}>
86
+ {% if param.description %}
87
+ <div class="param-description">{{ param.description }}</div>
88
+ {% endif %}
89
+ <div class="validation-message"></div>
90
+ </div>
36
91
  </div>
37
92
  {% endfor %}
38
93
  </div>
39
94
  </div>
40
95
  {% endif %}
41
96
 
42
- <div class="form-group">
43
- <label class="form-label">Query Parameters</label>
44
- <div class="kv-container" id="queryParams">
45
- <div class="kv-item">
46
- <input type="text" placeholder="Parameter name">
47
- <input type="text" placeholder="Parameter value">
48
- <button class="remove-btn" onclick="TryOutSidebar.removeKvItem(this)">✕</button>
97
+ <div class="form-section">
98
+ <div class="section-header">
99
+ <h4>Query Parameters</h4>
100
+ <div class="tooltip" data-tooltip="Parameters added to the URL after ?">ℹ️</div>
101
+ </div>
102
+ <div class="parameter-container" id="queryParams">
103
+ <div class="parameter-toolbar">
104
+ <button class="add-param-btn" onclick="TryOutSidebar.addQueryParam()">
105
+ <span class="icon">+</span> Add Parameter
106
+ </button>
107
+ </div>
108
+ <div class="parameter-list">
109
+ <div class="parameter-item">
110
+ <div class="parameter-inputs">
111
+ <input type="text"
112
+ class="modern-input name-input"
113
+ placeholder="Parameter name"
114
+ list="paramSuggestions">
115
+ <input type="text"
116
+ class="modern-input value-input"
117
+ placeholder="Value">
118
+ <button class="remove-btn"
119
+ onclick="TryOutSidebar.removeKvItem(this)"
120
+ aria-label="Remove parameter">
121
+ <span class="icon">✕</span>
122
+ </button>
123
+ </div>
124
+ </div>
49
125
  </div>
126
+ <!-- Parameter Suggestions -->
127
+ <datalist id="paramSuggestions">
128
+ {% for field in query_parameters.filter_fields %}
129
+ <option value="{{ field }}"></option>
130
+ {% endfor %}
131
+
132
+ {% if query_parameters.search_fields %}
133
+
134
+ <option value="search"></option>
135
+ {% endif %}
136
+
137
+ {% if query_parameters.ordering_fields %}
138
+
139
+ <option value="ordering"></option>
140
+ {% endif %}
141
+
142
+ {% for field in query_parameters.pagination_fields %}
143
+ <option value="{{ field }}"></option>
144
+ {% endfor %}
145
+ </datalist>
50
146
  </div>
51
- <button class="add-btn" onclick="TryOutSidebar.addQueryParam()">
52
- <span>+</span> Add Parameter
53
- </button>
54
147
  </div>
55
148
  </div>
56
149
 
57
150
  <!-- Headers Tab -->
58
- <div class="tab-content" id="headersTab">
59
- <div class="form-group">
60
- <label class="form-label">Request Headers</label>
61
- <div class="kv-container" id="requestHeaders">
62
- <div class="kv-item">
63
- <input type="text" value="Content-Type">
64
- <input type="text" value="application/json">
65
- <button class="remove-btn" onclick="TryOutSidebar.removeKvItem(this)">✕</button>
151
+ <div class="tab-content"
152
+ id="headersTab"
153
+ role="tabpanel"
154
+ aria-labelledby="tab-headers">
155
+ <div class="form-section">
156
+ <div class="section-header">
157
+ <h4>Request Headers</h4>
158
+ <div class="tooltip" data-tooltip="HTTP headers sent with the request">ℹ️</div>
159
+ </div>
160
+ <div class="header-container" id="requestHeaders">
161
+ <div class="header-toolbar">
162
+ <button class="add-header-btn" onclick="TryOutSidebar.addHeader()">
163
+ <span class="icon">+</span> Add Header
164
+ </button>
66
165
  </div>
67
- <div class="kv-item">
68
- <input type="text" value="Authorization">
69
- <input type="text" placeholder="Bearer your-token">
70
- <button class="remove-btn" onclick="TryOutSidebar.removeKvItem(this)">✕</button>
166
+ <div class="header-list">
167
+ <div class="header-item">
168
+ <div class="header-inputs">
169
+ <input type="text"
170
+ class="modern-input name-input"
171
+ value="Content-Type"
172
+ list="headerSuggestions">
173
+ <input type="text"
174
+ class="modern-input value-input"
175
+ value="application/json">
176
+ <button class="remove-btn"
177
+ onclick="TryOutSidebar.removeKvItem(this)"
178
+ aria-label="Remove header">
179
+ <span class="icon">✕</span>
180
+ </button>
181
+ </div>
182
+ </div>
183
+ <div class="header-item">
184
+ <div class="header-inputs">
185
+ <input type="text"
186
+ class="modern-input name-input"
187
+ value="Authorization"
188
+ list="headerSuggestions">
189
+ <input type="text"
190
+ class="modern-input value-input"
191
+ placeholder="Bearer your-token">
192
+ <button class="remove-btn"
193
+ onclick="TryOutSidebar.removeKvItem(this)"
194
+ aria-label="Remove header">
195
+ <span class="icon">✕</span>
196
+ </button>
197
+ </div>
198
+ </div>
71
199
  </div>
200
+ <!-- Header Suggestions -->
201
+ <datalist id="headerSuggestions">
202
+ <option value="Accept">Response format preference</option>
203
+ <option value="Authorization">Authentication credentials</option>
204
+ <option value="Content-Type">Request body format</option>
205
+ <option value="Cache-Control">Caching behavior</option>
206
+ </datalist>
72
207
  </div>
73
- <button class="add-btn" onclick="TryOutSidebar.addHeader()">
74
- <span>+</span> Add Header
75
- </button>
76
208
  </div>
77
209
  </div>
78
210
 
79
211
  <!-- Request Body Tab -->
80
212
  {% if method|upper in "POST,PUT,PATCH" %}
81
- <div class="tab-content" id="bodyTab">
82
- <div class="form-group">
83
- <label class="form-label">Request Body</label>
84
- <textarea id="requestBody"
85
- class="form-input form-textarea"
86
- placeholder="Enter JSON request body..."
87
- rows="8">{% if request_example %}{{ request_example|extract_json_from_markdown }}{% endif %}</textarea>
213
+ <div class="tab-content"
214
+ id="bodyTab"
215
+ role="tabpanel"
216
+ aria-labelledby="tab-body">
217
+ <div class="form-section">
218
+ <div class="section-header">
219
+ <h4>Request Body</h4>
220
+ <div class="body-toolbar">
221
+ <button class="format-btn" onclick="formatJson()">
222
+ <span class="icon">🔧</span> Format
223
+ </button>
224
+ <button class="validate-btn" onclick="validateJson()">
225
+ <span class="icon">✓</span> Validate
226
+ </button>
227
+ </div>
228
+ </div>
229
+ <div class="editor-container">
230
+ <div class="editor-wrapper">
231
+ <textarea id="requestBody"
232
+ class="code-editor"
233
+ placeholder="Enter JSON request body..."
234
+ spellcheck="false"
235
+ rows="8">{% if request_example %}{{ request_example|extract_json_from_markdown }}{% endif %}</textarea>
236
+ <div class="editor-overlay"></div>
237
+ </div>
238
+ <div class="validation-status"></div>
239
+ </div>
88
240
  </div>
89
241
  </div>
90
242
  {% endif %}
91
243
 
92
- <!-- Execute Button -->
93
- <button class="execute-btn" id="executeBtn" onclick="executeRequest()">
94
- <span>▶</span> Execute Request
95
- </button>
244
+ <!-- Action Bar - Hidden since buttons are in modal footer -->
245
+ <div class="action-bar" style="display: none;">
246
+ <div class="action-group">
247
+ <button class="secondary-btn" onclick="resetForm()">
248
+ <span class="icon">↺</span> Reset
249
+ </button>
250
+ <button class="primary-btn" id="executeBtn" onclick="executeRequest()">
251
+ <span class="icon">▶</span> Execute Request
252
+ <div class="loading-spinner"></div>
253
+ </button>
254
+ </div>
255
+ </div>
96
256
  </div>
97
257
 
98
- <script>
99
- // Pass query parameters data to JavaScript
100
- window.queryParametersData = {
101
- {% if query_parameters %}
102
- filter_fields: {{ query_parameters.filter_fields|default:"[]"|safe }},
103
- search_fields: {{ query_parameters.search_fields|default:"[]"|safe }},
104
- ordering_fields: {{ query_parameters.ordering_fields|default:"[]"|safe }},
105
- pagination_fields: {{ query_parameters.pagination_fields|default:"[]"|safe }}
106
- {% else %}
107
- filter_fields: [],
108
- search_fields: [],
109
- ordering_fields: [],
110
- pagination_fields: []
111
- {% endif %}
112
- };
113
- </script>
258
+ <!-- Form styles are now in form.css -->
259
+
260
+ <!-- JavaScript functionality is now handled in the modular try-out/*.js files -->