apigrip 0.4.0 → 0.5.1

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.
package/README.md CHANGED
@@ -67,6 +67,14 @@ apigrip curl GET /users -e Production
67
67
  apigrip list # grouped by tag
68
68
  apigrip list --search "auth" --json # fuzzy search, JSON output
69
69
 
70
+ # Show endpoint details
71
+ apigrip show GET /users # parameters, schemas, responses
72
+ apigrip show POST /users --json # full JSON output
73
+
74
+ # Print parsed spec
75
+ apigrip spec # dereferenced JSON
76
+ apigrip spec --raw # original file as-is
77
+
70
78
  # Manage projects
71
79
  apigrip projects # list bookmarks
72
80
  apigrip projects add ./my-api # bookmark a directory
@@ -207,7 +215,7 @@ apigrip/
207
215
  │ ├── index.js Yargs command registration
208
216
  │ ├── output.js CLI formatting (table, JSON, color)
209
217
  │ ├── resolve-project.js Shared project context resolution
210
- │ └── commands/ serve, send, curl, list, projects, env, last, mcp
218
+ │ └── commands/ serve, send, curl, list, show, spec, projects, env, last, mcp
211
219
  ├── core/ Shared logic (spec parsing, curl, environments, persistence)
212
220
  │ ├── spec-discovery.js Find OpenAPI spec in a directory
213
221
  │ ├── spec-parser.js Parse/dereference specs (2.0, 3.0, 3.1)
@@ -0,0 +1,148 @@
1
+ // cli/commands/show.js — Show details for a specific endpoint
2
+
3
+ import { parseSpec, getEndpointDetails } from '../../core/spec-parser.js';
4
+ import { colorMethod } from '../output.js';
5
+ import { resolveProjectContext } from '../resolve-project.js';
6
+
7
+ const DIM = '\x1b[2m';
8
+ const BOLD = '\x1b[1m';
9
+ const RESET = '\x1b[0m';
10
+ const CYAN = '\x1b[36m';
11
+ const YELLOW = '\x1b[33m';
12
+
13
+ function indent(text, level = 2) {
14
+ const pad = ' '.repeat(level);
15
+ return text.split('\n').map(l => pad + l).join('\n');
16
+ }
17
+
18
+ function formatSchema(schema, depth = 0) {
19
+ if (!schema) return DIM + 'any' + RESET;
20
+ const pad = ' '.repeat(depth);
21
+
22
+ if (schema.type === 'object' && schema.properties) {
23
+ const required = new Set(schema.required || []);
24
+ const lines = ['{'];
25
+ for (const [key, prop] of Object.entries(schema.properties)) {
26
+ const req = required.has(key) ? ` ${YELLOW}*required${RESET}` : '';
27
+ const type = prop.type || 'any';
28
+ const desc = prop.description ? ` ${DIM}${prop.description}${RESET}` : '';
29
+ if (prop.type === 'object' && prop.properties) {
30
+ lines.push(`${pad} ${key}: ${formatSchema(prop, depth + 1)}${req}${desc}`);
31
+ } else if (prop.type === 'array' && prop.items) {
32
+ const itemType = prop.items.type || 'any';
33
+ lines.push(`${pad} ${key}: ${type}<${itemType}>${req}${desc}`);
34
+ } else {
35
+ lines.push(`${pad} ${key}: ${type}${req}${desc}`);
36
+ }
37
+ }
38
+ lines.push(`${pad}}`);
39
+ return lines.join('\n');
40
+ }
41
+
42
+ if (schema.type === 'array' && schema.items) {
43
+ return `array<${schema.items.type || 'any'}>`;
44
+ }
45
+
46
+ return schema.type || 'any';
47
+ }
48
+
49
+ export async function showCommand(argv) {
50
+ const { specPath } = await resolveProjectContext(argv);
51
+ if (!specPath) {
52
+ console.error('No spec file found');
53
+ process.exit(2);
54
+ }
55
+
56
+ let spec;
57
+ try {
58
+ spec = await parseSpec(specPath);
59
+ } catch (err) {
60
+ console.error(`Spec parse error: ${err.message}`);
61
+ process.exit(2);
62
+ }
63
+
64
+ const method = argv.method.toUpperCase();
65
+ const pathArg = argv.path;
66
+ const apiPath = typeof pathArg === 'string' ? (pathArg.startsWith('/') ? pathArg : '/' + pathArg) : '/' + String(pathArg);
67
+
68
+ const details = getEndpointDetails(spec, method, apiPath);
69
+ if (!details) {
70
+ console.error(`Endpoint not found: ${method} ${apiPath}`);
71
+ process.exit(3);
72
+ }
73
+
74
+ if (argv.json) {
75
+ console.log(JSON.stringify(details, null, 2));
76
+ return;
77
+ }
78
+
79
+ const lines = [];
80
+
81
+ // Header
82
+ lines.push(`${colorMethod(method)} ${BOLD}${apiPath}${RESET}`);
83
+ if (details.summary) lines.push(details.summary);
84
+ if (details.description) lines.push(`${DIM}${details.description}${RESET}`);
85
+ if (details.tags && details.tags.length) lines.push(`${DIM}Tags: ${details.tags.join(', ')}${RESET}`);
86
+ if (details.deprecated) lines.push(`${YELLOW}DEPRECATED${RESET}`);
87
+
88
+ // Parameters
89
+ const params = details.parameters || [];
90
+ if (params.length > 0) {
91
+ lines.push('');
92
+ lines.push(`${BOLD}PARAMETERS${RESET}`);
93
+ for (const p of params) {
94
+ const req = p.required ? ` ${YELLOW}*required${RESET}` : '';
95
+ const type = p.schema?.type || 'string';
96
+ const def = p.schema?.default != null ? ` ${DIM}(default: ${p.schema.default})${RESET}` : '';
97
+ lines.push(` ${CYAN}${p.in}${RESET} ${p.name}: ${type}${req}${def}`);
98
+ if (p.description) lines.push(` ${DIM}${p.description}${RESET}`);
99
+ }
100
+ }
101
+
102
+ // Request body
103
+ const reqBody = details.request_body;
104
+ if (reqBody) {
105
+ lines.push('');
106
+ lines.push(`${BOLD}REQUEST BODY${RESET}${reqBody.required ? ` ${YELLOW}*required${RESET}` : ''}`);
107
+ if (reqBody.content) {
108
+ for (const [ct, media] of Object.entries(reqBody.content)) {
109
+ lines.push(` ${DIM}${ct}${RESET}`);
110
+ if (media.schema) {
111
+ lines.push(indent(formatSchema(media.schema), 4));
112
+ }
113
+ }
114
+ }
115
+ }
116
+
117
+ // Responses
118
+ const responses = details.responses || {};
119
+ if (Object.keys(responses).length > 0) {
120
+ lines.push('');
121
+ lines.push(`${BOLD}RESPONSES${RESET}`);
122
+ for (const [code, resp] of Object.entries(responses)) {
123
+ const desc = resp.description || '';
124
+ lines.push(` ${CYAN}${code}${RESET} ${desc}`);
125
+ if (resp.content) {
126
+ for (const [ct, media] of Object.entries(resp.content)) {
127
+ lines.push(` ${DIM}${ct}${RESET}`);
128
+ if (media.schema) {
129
+ lines.push(indent(formatSchema(media.schema), 6));
130
+ }
131
+ }
132
+ }
133
+ }
134
+ }
135
+
136
+ // Servers
137
+ const servers = details.servers || [];
138
+ if (servers.length > 0) {
139
+ lines.push('');
140
+ lines.push(`${BOLD}SERVERS${RESET}`);
141
+ for (const s of servers) {
142
+ const desc = s.description ? ` ${DIM}(${s.description})${RESET}` : '';
143
+ lines.push(` ${s.url}${desc}`);
144
+ }
145
+ }
146
+
147
+ console.log(lines.join('\n'));
148
+ }
package/cli/index.js CHANGED
@@ -62,6 +62,17 @@ const cli = yargs(hideBin(process.argv))
62
62
  const { listCommand } = await import('./commands/list.js');
63
63
  await listCommand(argv);
64
64
  })
65
+ .command('show <method> <path>', 'Show endpoint details', (yargs) => {
66
+ return yargs
67
+ .positional('method', { type: 'string', describe: 'HTTP method' })
68
+ .positional('path', { type: 'string', describe: 'API path' })
69
+ .option('json', { type: 'boolean', describe: 'Full JSON output' })
70
+ .option('spec', { type: 'string', describe: 'Path to spec file' })
71
+ .option('project', { type: 'string', describe: 'Project directory' });
72
+ }, async (argv) => {
73
+ const { showCommand } = await import('./commands/show.js');
74
+ await showCommand(argv);
75
+ })
65
76
  .command('projects [action] [dir]', 'Manage project bookmarks', (yargs) => {
66
77
  return yargs
67
78
  .positional('action', { choices: ['add', 'remove'], describe: 'Action' })
@@ -114,5 +125,6 @@ const cli = yargs(hideBin(process.argv))
114
125
  await mcpCommand(argv);
115
126
  })
116
127
  .demandCommand(1, 'You need at least one command')
128
+ .strictCommands()
117
129
  .help()
118
130
  .argv;
@@ -0,0 +1 @@
1
+ :root{--bg: #282a36;--bg-secondary: #44475a;--bg-input: #1e1f29;--fg: #f8f8f2;--fg-muted: #6272a4;--fg-faint: #44475a;--border: #44475a;--accent: #bd93f9;--accent-hover: #caa8ff;--method-get: #50fa7b;--method-post: #bd93f9;--method-put: #ffb86c;--method-delete: #ff5555;--method-patch: #f1fa8c;--status-2xx: #50fa7b;--status-3xx: #f1fa8c;--status-4xx: #ffb86c;--status-5xx: #ff5555;--font-mono: "SF Mono", "Fira Code", "Cascadia Code", monospace;--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--radius: 6px;--transition: .15s ease}[data-theme=light]{--bg: #f8f8f2;--bg-secondary: #e8e8e2;--bg-input: #ffffff;--fg: #282a36;--fg-muted: #6272a4;--fg-faint: #c0c0c0;--border: #d0d0d0;--accent: #7c5cbf;--accent-hover: #6a4daa}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}body{background:var(--bg);color:var(--fg);font-family:var(--font-sans);font-size:14px;line-height:1.5;height:100vh;overflow:hidden}#root{display:flex;flex-direction:column;height:100%}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:var(--bg)}::-webkit-scrollbar-thumb{background:var(--bg-secondary);border-radius:4px}::-webkit-scrollbar-thumb:hover{background:var(--fg-muted)}*{scrollbar-width:thin;scrollbar-color:var(--bg-secondary) var(--bg)}::selection{background:var(--accent);color:var(--bg)}:focus-visible{outline:2px solid var(--accent);outline-offset:2px}h1{font-size:1.5rem;font-weight:700;line-height:1.3}h2{font-size:1.25rem;font-weight:600;line-height:1.3}h3{font-size:1rem;font-weight:600;line-height:1.4}h4{font-size:.875rem;font-weight:600;line-height:1.4}p{margin-bottom:.5em}code{font-family:var(--font-mono);font-size:.9em;background:var(--bg-secondary);padding:.15em .35em;border-radius:3px}pre{font-family:var(--font-mono);font-size:.85rem;line-height:1.5;white-space:pre-wrap;word-wrap:break-word}.app{display:flex;flex-direction:column;height:100%}.app__body{display:flex;flex:1;min-height:0;overflow:hidden}.app__sidebar{width:280px;min-width:200px;border-right:1px solid var(--border);display:flex;flex-direction:column;overflow:hidden}.app__main{flex:1;display:flex;min-width:0;overflow:hidden}.app__request-panel{flex:1;min-width:0;overflow-y:auto;border-right:1px solid var(--border);padding:16px}.app__response-panel{flex:1;min-width:0;overflow-y:auto;padding:16px}.topbar{display:flex;align-items:center;gap:12px;padding:8px 16px;background:var(--bg-secondary);border-bottom:1px solid var(--border);min-height:44px;flex-shrink:0}.topbar__section{display:flex;align-items:center;gap:8px}.topbar__section--right{margin-left:auto}.topbar__label{font-size:12px;color:var(--fg-muted);text-transform:uppercase;letter-spacing:.05em}.topbar__select{background:var(--bg-input);color:var(--fg);border:1px solid var(--border);border-radius:var(--radius);padding:4px 8px;font-size:13px;font-family:var(--font-sans);cursor:pointer}.topbar__select:hover{border-color:var(--accent)}.topbar__btn{background:none;border:1px solid var(--border);border-radius:var(--radius);color:var(--fg);padding:4px 10px;font-size:12px;cursor:pointer;transition:background var(--transition)}.topbar__btn:hover{background:var(--bg)}.topbar__git{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--fg-muted);font-family:var(--font-mono)}.topbar__git-branch{color:var(--accent)}.topbar__git-commit{color:var(--fg-muted)}.topbar__git-dirty{color:var(--method-put)}.topbar__toast{font-size:12px;color:var(--method-get);animation:topbar-toast-fade 3s ease forwards}@keyframes topbar-toast-fade{0%{opacity:1}70%{opacity:1}to{opacity:0}}.endpoint-list{display:flex;flex-direction:column;height:100%}.endpoint-list__search{padding:8px;border-bottom:1px solid var(--border)}.endpoint-list__search-input{width:100%;background:var(--bg-input);color:var(--fg);border:1px solid var(--border);border-radius:var(--radius);padding:6px 10px;font-size:13px;font-family:var(--font-sans)}.endpoint-list__search-input::placeholder{color:var(--fg-muted)}.endpoint-list__toolbar{display:flex;align-items:center;justify-content:flex-end;padding:4px 8px;border-bottom:1px solid var(--border)}.endpoint-list__view-btn{background:none;border:none;color:var(--fg-muted);cursor:pointer;padding:2px 6px;font-size:12px}.endpoint-list__view-btn--active{color:var(--accent)}.endpoint-list__items{flex:1;overflow-y:auto;padding:4px 0}.endpoint-list__tag-header{padding:8px 12px 4px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--fg-muted)}.endpoint-list__tree-folder{padding:4px 12px;font-size:12px;color:var(--fg-muted);cursor:pointer;display:flex;align-items:center;gap:4px;-webkit-user-select:none;user-select:none}.endpoint-list__tree-folder:hover{color:var(--fg)}.endpoint-list__tree-children{padding-left:16px}.endpoint-item{display:flex;align-items:center;gap:8px;padding:5px 12px;cursor:pointer;transition:background var(--transition);border-left:3px solid transparent}.endpoint-item:hover{background:var(--bg-secondary)}.endpoint-item--selected{background:var(--bg-secondary);border-left-color:var(--accent)}.endpoint-item__method{font-size:10px;font-weight:700;font-family:var(--font-mono);text-transform:uppercase;min-width:52px;text-align:center;padding:2px 6px;border-radius:3px;letter-spacing:.02em}.endpoint-item__method--get{color:var(--method-get);background:#50fa7b1a}.endpoint-item__method--post{color:var(--method-post);background:#bd93f91a}.endpoint-item__method--put{color:var(--method-put);background:#ffb86c1a}.endpoint-item__method--delete{color:var(--method-delete);background:#ff55551a}.endpoint-item__method--patch{color:var(--method-patch);background:#f1fa8c1a}.endpoint-item__path{font-family:var(--font-mono);font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.endpoint-item__summary{font-size:11px;color:var(--fg-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-left:auto}.request-panel__section{margin-bottom:16px}.request-panel__section-title{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--fg-muted);margin-bottom:8px}.request-panel__server-select{width:100%;background:var(--bg-input);color:var(--fg);border:1px solid var(--border);border-radius:var(--radius);padding:6px 10px;font-family:var(--font-mono);font-size:13px}.request-panel__server-vars{display:flex;flex-direction:column;gap:6px;margin-top:8px}.request-panel__send-bar{display:flex;align-items:center;gap:8px;margin-top:16px;margin-bottom:24px}.request-panel__send-btn{background:var(--accent);color:var(--bg);border:none;border-radius:var(--radius);padding:8px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:background var(--transition)}.request-panel__send-btn:hover{background:var(--accent-hover)}.request-panel__send-btn:disabled{opacity:.5;cursor:not-allowed}.request-panel__spinner{display:inline-block;width:16px;height:16px;border:2px solid var(--fg-muted);border-top-color:var(--accent);border-radius:50%;animation:spin .6s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.param-form{display:flex;flex-direction:column;gap:10px}.param-form__field{display:flex;flex-direction:column;gap:3px}.param-form__label{display:flex;align-items:center;gap:4px;font-size:12px;font-weight:600}.param-form__required{color:var(--method-delete)}.param-form__desc{font-size:11px;color:var(--fg-muted)}.param-form__input{background:var(--bg-input);color:var(--fg);border:1px solid var(--border);border-radius:var(--radius);padding:6px 10px;font-family:var(--font-mono);font-size:13px}.param-form__input:focus{border-color:var(--accent)}.param-form__resolved{font-size:11px;color:var(--fg-muted);font-family:var(--font-mono)}.hl-wrap{position:relative}.hl-backdrop{position:absolute;inset:0;padding:6px 10px;font-family:var(--font-mono);font-size:13px;line-height:normal;color:transparent;white-space:pre;overflow:hidden;pointer-events:none;border:1px solid transparent;border-radius:var(--radius)}.hl-input{background:transparent!important;position:relative;caret-color:var(--fg)}.hl-wrap .param-form__input{background:transparent}.hl-wrap:before{content:"";position:absolute;inset:0;background:var(--bg-input);border-radius:var(--radius);z-index:-1}.hl-var{background:color-mix(in srgb,var(--method-get) 20%,transparent);color:var(--method-get);border-radius:3px;padding:0 1px}.body-editor__toolbar{display:flex;align-items:center;gap:8px;margin-bottom:8px}.body-editor__content-type{background:var(--bg-input);color:var(--fg);border:1px solid var(--border);border-radius:var(--radius);padding:4px 8px;font-size:13px}.body-editor__sample-btn{background:none;border:1px solid var(--border);border-radius:var(--radius);color:var(--fg);padding:4px 10px;font-size:12px;cursor:pointer}.body-editor__sample-btn:hover{border-color:var(--accent);color:var(--accent)}.body-editor__textarea{width:100%;min-height:200px;background:var(--bg-input);color:var(--fg);border:1px solid var(--border);border-radius:var(--radius);padding:10px;font-family:var(--font-mono);font-size:13px;line-height:1.5;resize:vertical}.body-editor__kv-table{width:100%;border-collapse:collapse}.body-editor__kv-table th{text-align:left;font-size:11px;font-weight:600;color:var(--fg-muted);padding:4px 8px;border-bottom:1px solid var(--border)}.body-editor__kv-table td{padding:4px 8px}.body-editor__kv-input{width:100%;background:var(--bg-input);color:var(--fg);border:1px solid var(--border);border-radius:3px;padding:4px 8px;font-family:var(--font-mono);font-size:13px}.body-editor__kv-remove{background:none;border:none;color:var(--method-delete);cursor:pointer;font-size:16px;padding:2px 6px}.body-editor__kv-add{background:none;border:1px dashed var(--border);border-radius:var(--radius);color:var(--fg-muted);padding:4px 12px;font-size:12px;cursor:pointer;margin-top:6px}.body-editor__kv-add:hover{border-color:var(--accent);color:var(--accent)}.body-editor__validation{margin-top:6px;font-size:12px;display:flex;align-items:center;gap:4px}.body-editor__validation--valid{color:var(--method-get)}.body-editor__validation--invalid{color:var(--method-delete)}.body-editor__validation--none{color:var(--fg-muted)}.response-panel__empty{display:flex;align-items:center;justify-content:center;height:100%;color:var(--fg-muted);font-size:14px}.response-panel__loading{display:flex;align-items:center;justify-content:center;height:100%;gap:10px;color:var(--fg-muted)}.response-panel__header{display:flex;align-items:center;gap:12px;margin-bottom:12px;flex-wrap:wrap}.response-panel__status{font-weight:700;font-family:var(--font-mono);font-size:14px;padding:3px 10px;border-radius:var(--radius)}.response-panel__status--2xx{color:var(--status-2xx);background:#50fa7b1a}.response-panel__status--3xx{color:var(--status-3xx);background:#f1fa8c1a}.response-panel__status--4xx{color:var(--status-4xx);background:#ffb86c1a}.response-panel__status--5xx{color:var(--status-5xx);background:#ff55551a}.response-panel__meta{font-size:12px;color:var(--fg-muted)}.response-panel__tabs{display:flex;border-bottom:1px solid var(--border);margin-bottom:12px}.response-panel__tab{background:none;border:none;border-bottom:2px solid transparent;color:var(--fg-muted);padding:6px 16px;font-size:13px;cursor:pointer;transition:color var(--transition),border-color var(--transition)}.response-panel__tab:hover{color:var(--fg)}.response-panel__tab--active{color:var(--accent);border-bottom-color:var(--accent)}.body-view__toolbar{display:flex;align-items:center;gap:8px;margin-bottom:8px}.body-view__btn{background:none;border:1px solid var(--border);border-radius:var(--radius);color:var(--fg);padding:3px 10px;font-size:12px;cursor:pointer}.body-view__btn:hover,.body-view__btn--active{border-color:var(--accent);color:var(--accent)}.body-view__pre{background:var(--bg-input);border-radius:var(--radius);padding:12px;overflow:auto;max-height:calc(100vh - 260px);font-size:13px;line-height:1.5}.body-view__pre code{background:none;padding:0;border-radius:0}.body-view__pre .hljs-attr{color:#50fa7b}.body-view__pre .hljs-string{color:#f1fa8c}.body-view__pre .hljs-number{color:#bd93f9}.body-view__pre .hljs-literal,.body-view__pre .hljs-keyword,.body-view__pre .hljs-tag{color:#ff79c6}.body-view__pre .hljs-name{color:#50fa7b}.body-view__pre .hljs-selector-tag{color:#ff79c6}.body-view__pre .hljs-punctuation{color:var(--fg)}.body-view__pre--wrap{white-space:pre-wrap;word-wrap:break-word}.body-view__pre--nowrap{white-space:pre}.body-view__filter-group{display:flex;align-items:center;flex:1;position:relative}.body-view__filter{width:100%;background:var(--bg-input);border:1px solid var(--border);border-radius:var(--radius);color:var(--fg);padding:3px 26px 3px 10px;font-size:12px;font-family:inherit}.body-view__filter::placeholder{color:var(--fg-muted)}.body-view__filter:focus{outline:none;border-color:var(--accent)}.body-view__filter--active{border-color:var(--method-get)}.body-view__filter--error{border-color:var(--method-delete)}.body-view__filter-clear{position:absolute;right:4px;background:none;border:none;color:var(--fg-muted);cursor:pointer;font-size:12px;padding:2px 6px;line-height:1}.body-view__filter-clear:hover{color:var(--fg)}.body-view__filter-error{color:var(--method-delete);font-size:11px;margin-bottom:6px;padding:2px 0}.headers-view__table{width:100%;border-collapse:collapse}.headers-view__table th{text-align:left;font-size:11px;font-weight:600;color:var(--fg-muted);padding:6px 10px;border-bottom:1px solid var(--border)}.headers-view__table td{padding:5px 10px;font-family:var(--font-mono);font-size:13px;border-bottom:1px solid var(--fg-faint)}.headers-view__table td:first-child{color:var(--accent);white-space:nowrap}.headers-view__section-title{font-size:12px;font-weight:600;color:var(--fg-muted);margin:12px 0 6px;cursor:pointer;-webkit-user-select:none;user-select:none}.verbose-view{background:var(--bg-input);border-radius:var(--radius);padding:12px;overflow:auto;max-height:calc(100vh - 220px);font-family:var(--font-mono);font-size:13px;line-height:1.6;white-space:pre-wrap;word-wrap:break-word}.verbose-view__line--comment{color:#6272a4}.verbose-view__line--request{color:#8be9fd}.verbose-view__line--response{color:#50fa7b}.verbose-view__line--other{color:#f8f8f2}.curl-view{position:relative}.curl-view__pre{background:var(--bg-input);border-radius:var(--radius);padding:12px;font-family:var(--font-mono);font-size:13px;line-height:1.5;white-space:pre-wrap;word-wrap:break-word;overflow:auto;max-height:calc(100vh - 220px)}.curl-view__copy-btn{position:absolute;top:8px;right:8px}.env-editor{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center}.env-editor__backdrop{position:absolute;inset:0;background:#0009}.env-editor__dialog{position:relative;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);width:640px;max-width:90vw;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 8px 32px #0006}.env-editor__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--border)}.env-editor__title{font-size:16px;font-weight:600}.env-editor__close{background:none;border:none;color:var(--fg-muted);font-size:20px;cursor:pointer;padding:2px 6px}.env-editor__close:hover{color:var(--fg)}.env-editor__tabs{display:flex;border-bottom:1px solid var(--border);padding:0 16px;overflow-x:auto}.env-editor__tab{background:none;border:none;border-bottom:2px solid transparent;color:var(--fg-muted);padding:8px 14px;font-size:13px;cursor:pointer;white-space:nowrap}.env-editor__tab:hover{color:var(--fg)}.env-editor__tab--active{color:var(--accent);border-bottom-color:var(--accent)}.env-editor__body{flex:1;overflow-y:auto;padding:16px}.env-editor__new-env{display:flex;align-items:center;gap:8px;margin-bottom:12px}.env-editor__new-env-input{flex:1;background:var(--bg-input);color:var(--fg);border:1px solid var(--border);border-radius:var(--radius);padding:6px 10px;font-size:13px}.env-editor__footer{display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:12px 16px;border-top:1px solid var(--border)}.env-editor__save-btn{background:var(--accent);color:var(--bg);border:none;border-radius:var(--radius);padding:6px 20px;font-size:13px;font-weight:600;cursor:pointer}.env-editor__save-btn:hover{background:var(--accent-hover)}.env-editor__delete-btn{background:none;border:1px solid var(--method-delete);border-radius:var(--radius);color:var(--method-delete);padding:6px 14px;font-size:12px;cursor:pointer}.env-editor__delete-btn:hover{background:#ff55551a}.command-palette{position:fixed;inset:0;z-index:2000;display:flex;justify-content:center;padding-top:15vh}.command-palette__backdrop{position:absolute;inset:0;background:#00000080}.command-palette__dialog{position:relative;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);width:520px;max-width:90vw;max-height:60vh;display:flex;flex-direction:column;box-shadow:0 8px 32px #0006}.command-palette__input{background:var(--bg-input);color:var(--fg);border:none;border-bottom:1px solid var(--border);padding:14px 16px;font-size:15px;font-family:var(--font-sans);border-radius:var(--radius) var(--radius) 0 0}.command-palette__input::placeholder{color:var(--fg-muted)}.command-palette__results{flex:1;overflow-y:auto;padding:4px 0}.command-palette__section-title{padding:8px 16px 4px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--fg-muted)}.command-palette__item{display:flex;align-items:center;gap:10px;padding:8px 16px;cursor:pointer;transition:background var(--transition)}.command-palette__item:hover,.command-palette__item--active{background:var(--bg-secondary)}.command-palette__item-label{font-size:14px}.command-palette__item-detail{font-size:12px;color:var(--fg-muted);margin-left:auto}.command-palette__item-shortcut{margin-left:auto;font-family:var(--font-mono);font-size:11px;color:var(--fg-muted);background:var(--bg-input);border:1px solid var(--border);border-radius:3px;padding:1px 6px;white-space:nowrap}.endpoint-doc{border:1px solid var(--border);border-radius:var(--radius);margin-bottom:16px}.endpoint-doc__toggle{display:flex;align-items:center;justify-content:space-between;width:100%;background:none;border:none;color:var(--fg);padding:10px 24px;font-size:14px;font-weight:600;cursor:pointer;text-align:left}.endpoint-doc__toggle:hover{background:var(--bg-secondary)}.endpoint-doc__toggle-icon{color:var(--fg-muted);font-size:12px;transition:transform var(--transition)}.endpoint-doc__toggle-icon--expanded{transform:rotate(90deg)}.endpoint-doc__body{padding:0 24px 14px;font-size:13px;line-height:1.6}.endpoint-doc__deprecated{background:#ff55551a;color:var(--method-delete);padding:6px 10px;border-radius:var(--radius);font-size:12px;font-weight:600;margin-bottom:8px}.endpoint-doc__body h1,.endpoint-doc__body h2,.endpoint-doc__body h3{margin-top:.8em;margin-bottom:.4em}.endpoint-doc__body p{margin-bottom:.5em}.endpoint-doc__body code{font-size:.9em}.body-editor__schema{margin-top:8px}.body-editor__schema-toggle{display:flex;align-items:center;justify-content:space-between;width:100%;background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius);padding:6px 12px;color:var(--fg-muted);font-size:12px;cursor:pointer}.body-editor__schema-toggle:hover{color:var(--fg)}.body-editor__schema-body{padding:10px 12px;border:1px solid var(--border);border-top:none;border-radius:0 0 var(--radius) var(--radius);font-size:12px;font-family:var(--font-mono);max-height:300px;overflow-y:auto}.schema-view__prop{padding:3px 0;display:flex;align-items:baseline;flex-wrap:wrap;gap:6px}.schema-view__name{color:var(--accent);font-weight:600}.schema-view__type{color:var(--fg-muted);font-size:11px}.schema-view__required{color:var(--method-delete);font-size:10px;font-weight:600}.schema-view__desc{color:var(--fg-muted);font-style:italic;font-family:var(--font-sans);font-size:11px}.schema-view__enum{color:var(--fg-muted);font-size:10px}.validation-details{display:inline-flex;align-items:center;gap:6px;cursor:pointer;font-size:13px}.validation-details__indicator--valid{color:var(--method-get)}.validation-details__indicator--invalid{color:var(--method-delete)}.validation-details__indicator--none{color:var(--fg-muted)}.validation-details__errors{margin-top:8px;font-size:12px}.validation-details__error{padding:6px 10px;border-left:3px solid var(--method-delete);background:#ff55550d;margin-bottom:4px;border-radius:0 var(--radius) var(--radius) 0}.validation-details__error-path{font-family:var(--font-mono);color:var(--accent);font-size:11px}.validation-details__error-msg{color:var(--fg)}.validation-details__error-detail{color:var(--fg-muted);font-size:11px}.copy-btn{background:none;border:1px solid var(--border);border-radius:var(--radius);color:var(--fg);padding:3px 10px;font-size:12px;cursor:pointer}.copy-btn:hover{border-color:var(--accent);color:var(--accent)}[data-theme=light] .body-view__pre .hljs-attr{color:#1a8a3f}[data-theme=light] .body-view__pre .hljs-string{color:#b35c00}[data-theme=light] .body-view__pre .hljs-number{color:#7c3aed}[data-theme=light] .body-view__pre .hljs-literal,[data-theme=light] .body-view__pre .hljs-keyword,[data-theme=light] .body-view__pre .hljs-tag{color:#d6336c}[data-theme=light] .body-view__pre .hljs-name{color:#1a8a3f}[data-theme=light] .body-view__pre .hljs-selector-tag{color:#d6336c}[data-theme=light] .body-view__pre .hljs-punctuation{color:var(--fg)}.resize-handle{flex-shrink:0;background:var(--border);transition:background .15s}.resize-handle--horizontal{width:4px;cursor:col-resize}.resize-handle--vertical{height:4px;cursor:row-resize}.resize-handle:hover,.resize-handle:active{background:var(--accent)}
@@ -10,8 +10,8 @@
10
10
  if (t) document.documentElement.setAttribute('data-theme', t);
11
11
  } catch (e) {}
12
12
  </script>
13
- <script type="module" crossorigin src="/assets/index-CmY5sq4C.js"></script>
14
- <link rel="stylesheet" crossorigin href="/assets/index-Cn0vR-FL.css">
13
+ <script type="module" crossorigin src="/assets/index-Iwhkggco.js"></script>
14
+ <link rel="stylesheet" crossorigin href="/assets/index-CRa1JtiS.css">
15
15
  </head>
16
16
  <body>
17
17
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apigrip",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "A spec-first, read-only OpenAPI client for developers",
5
5
  "type": "module",
6
6
  "main": "./lib/index.cjs",
@@ -22,6 +22,9 @@ export function createRequestRoutes(state) {
22
22
  return res.status(502).json({ error: 'Spec not loaded' });
23
23
  }
24
24
 
25
+ if (!req.body) {
26
+ return res.status(400).json({ error: 'Missing request body' });
27
+ }
25
28
  const { request_id: reqId, method, path: endpointPath, params, content_type, server_index, server_variables } = req.body;
26
29
  const request_id = reqId || `req-${Date.now()}`;
27
30
 
@@ -136,6 +139,9 @@ export function createRequestRoutes(state) {
136
139
 
137
140
  // POST /api/project/request/cancel — kill in-flight request
138
141
  router.post('/project/request/cancel', (req, res) => {
142
+ if (!req.body) {
143
+ return res.status(400).json({ error: 'Missing request body' });
144
+ }
139
145
  const { request_id } = req.body;
140
146
  if (!request_id) {
141
147
  return res.status(400).json({ error: 'Missing required field: request_id' });
@@ -160,6 +166,9 @@ export function createRequestRoutes(state) {
160
166
  return res.status(502).json({ error: 'Spec not loaded' });
161
167
  }
162
168
 
169
+ if (!req.body) {
170
+ return res.status(400).json({ error: 'Missing request body' });
171
+ }
163
172
  const { method, path: endpointPath, content_type, body } = req.body;
164
173
 
165
174
  if (!method || !endpointPath) {
@@ -1 +0,0 @@
1
- :root{--bg: #282a36;--bg-secondary: #44475a;--bg-input: #1e1f29;--fg: #f8f8f2;--fg-muted: #6272a4;--fg-faint: #44475a;--border: #44475a;--accent: #bd93f9;--accent-hover: #caa8ff;--method-get: #50fa7b;--method-post: #bd93f9;--method-put: #ffb86c;--method-delete: #ff5555;--method-patch: #f1fa8c;--status-2xx: #50fa7b;--status-3xx: #f1fa8c;--status-4xx: #ffb86c;--status-5xx: #ff5555;--font-mono: "SF Mono", "Fira Code", "Cascadia Code", monospace;--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--radius: 6px;--transition: .15s ease}[data-theme=light]{--bg: #f8f8f2;--bg-secondary: #e8e8e2;--bg-input: #ffffff;--fg: #282a36;--fg-muted: #6272a4;--fg-faint: #c0c0c0;--border: #d0d0d0;--accent: #7c5cbf;--accent-hover: #6a4daa}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}body{background:var(--bg);color:var(--fg);font-family:var(--font-sans);font-size:14px;line-height:1.5;height:100vh;overflow:hidden}#root{display:flex;flex-direction:column;height:100%}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:var(--bg)}::-webkit-scrollbar-thumb{background:var(--bg-secondary);border-radius:4px}::-webkit-scrollbar-thumb:hover{background:var(--fg-muted)}*{scrollbar-width:thin;scrollbar-color:var(--bg-secondary) var(--bg)}::selection{background:var(--accent);color:var(--bg)}:focus-visible{outline:2px solid var(--accent);outline-offset:2px}h1{font-size:1.5rem;font-weight:700;line-height:1.3}h2{font-size:1.25rem;font-weight:600;line-height:1.3}h3{font-size:1rem;font-weight:600;line-height:1.4}h4{font-size:.875rem;font-weight:600;line-height:1.4}p{margin-bottom:.5em}code{font-family:var(--font-mono);font-size:.9em;background:var(--bg-secondary);padding:.15em .35em;border-radius:3px}pre{font-family:var(--font-mono);font-size:.85rem;line-height:1.5;white-space:pre-wrap;word-wrap:break-word}.app{display:flex;flex-direction:column;height:100%}.app__body{display:flex;flex:1;min-height:0;overflow:hidden}.app__sidebar{width:280px;min-width:200px;border-right:1px solid var(--border);display:flex;flex-direction:column;overflow:hidden}.app__main{flex:1;display:flex;min-width:0;overflow:hidden}.app__request-panel{flex:1;min-width:0;overflow-y:auto;border-right:1px solid var(--border);padding:16px}.app__response-panel{flex:1;min-width:0;overflow-y:auto;padding:16px}.topbar{display:flex;align-items:center;gap:12px;padding:8px 16px;background:var(--bg-secondary);border-bottom:1px solid var(--border);min-height:44px;flex-shrink:0}.topbar__section{display:flex;align-items:center;gap:8px}.topbar__section--right{margin-left:auto}.topbar__label{font-size:12px;color:var(--fg-muted);text-transform:uppercase;letter-spacing:.05em}.topbar__select{background:var(--bg-input);color:var(--fg);border:1px solid var(--border);border-radius:var(--radius);padding:4px 8px;font-size:13px;font-family:var(--font-sans);cursor:pointer}.topbar__select:hover{border-color:var(--accent)}.topbar__btn{background:none;border:1px solid var(--border);border-radius:var(--radius);color:var(--fg);padding:4px 10px;font-size:12px;cursor:pointer;transition:background var(--transition)}.topbar__btn:hover{background:var(--bg)}.topbar__git{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--fg-muted);font-family:var(--font-mono)}.topbar__git-branch{color:var(--accent)}.topbar__git-commit{color:var(--fg-muted)}.topbar__git-dirty{color:var(--method-put)}.topbar__toast{font-size:12px;color:var(--method-get);animation:topbar-toast-fade 3s ease forwards}@keyframes topbar-toast-fade{0%{opacity:1}70%{opacity:1}to{opacity:0}}.endpoint-list{display:flex;flex-direction:column;height:100%}.endpoint-list__search{padding:8px;border-bottom:1px solid var(--border)}.endpoint-list__search-input{width:100%;background:var(--bg-input);color:var(--fg);border:1px solid var(--border);border-radius:var(--radius);padding:6px 10px;font-size:13px;font-family:var(--font-sans)}.endpoint-list__search-input::placeholder{color:var(--fg-muted)}.endpoint-list__toolbar{display:flex;align-items:center;justify-content:flex-end;padding:4px 8px;border-bottom:1px solid var(--border)}.endpoint-list__view-btn{background:none;border:none;color:var(--fg-muted);cursor:pointer;padding:2px 6px;font-size:12px}.endpoint-list__view-btn--active{color:var(--accent)}.endpoint-list__items{flex:1;overflow-y:auto;padding:4px 0}.endpoint-list__tag-header{padding:8px 12px 4px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--fg-muted)}.endpoint-list__tree-folder{padding:4px 12px;font-size:12px;color:var(--fg-muted);cursor:pointer;display:flex;align-items:center;gap:4px;-webkit-user-select:none;user-select:none}.endpoint-list__tree-folder:hover{color:var(--fg)}.endpoint-list__tree-children{padding-left:16px}.endpoint-item{display:flex;align-items:center;gap:8px;padding:5px 12px;cursor:pointer;transition:background var(--transition);border-left:3px solid transparent}.endpoint-item:hover{background:var(--bg-secondary)}.endpoint-item--selected{background:var(--bg-secondary);border-left-color:var(--accent)}.endpoint-item__method{font-size:10px;font-weight:700;font-family:var(--font-mono);text-transform:uppercase;min-width:52px;text-align:center;padding:2px 6px;border-radius:3px;letter-spacing:.02em}.endpoint-item__method--get{color:var(--method-get);background:#50fa7b1a}.endpoint-item__method--post{color:var(--method-post);background:#bd93f91a}.endpoint-item__method--put{color:var(--method-put);background:#ffb86c1a}.endpoint-item__method--delete{color:var(--method-delete);background:#ff55551a}.endpoint-item__method--patch{color:var(--method-patch);background:#f1fa8c1a}.endpoint-item__path{font-family:var(--font-mono);font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.endpoint-item__summary{font-size:11px;color:var(--fg-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-left:auto}.request-panel__section{margin-bottom:16px}.request-panel__section-title{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--fg-muted);margin-bottom:8px}.request-panel__server-select{width:100%;background:var(--bg-input);color:var(--fg);border:1px solid var(--border);border-radius:var(--radius);padding:6px 10px;font-family:var(--font-mono);font-size:13px}.request-panel__server-vars{display:flex;flex-direction:column;gap:6px;margin-top:8px}.request-panel__send-bar{display:flex;align-items:center;gap:8px;margin-top:16px;margin-bottom:24px}.request-panel__send-btn{background:var(--accent);color:var(--bg);border:none;border-radius:var(--radius);padding:8px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:background var(--transition)}.request-panel__send-btn:hover{background:var(--accent-hover)}.request-panel__send-btn:disabled{opacity:.5;cursor:not-allowed}.request-panel__spinner{display:inline-block;width:16px;height:16px;border:2px solid var(--fg-muted);border-top-color:var(--accent);border-radius:50%;animation:spin .6s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.param-form{display:flex;flex-direction:column;gap:10px}.param-form__field{display:flex;flex-direction:column;gap:3px}.param-form__label{display:flex;align-items:center;gap:4px;font-size:12px;font-weight:600}.param-form__required{color:var(--method-delete)}.param-form__desc{font-size:11px;color:var(--fg-muted)}.param-form__input{background:var(--bg-input);color:var(--fg);border:1px solid var(--border);border-radius:var(--radius);padding:6px 10px;font-family:var(--font-mono);font-size:13px}.param-form__input:focus{border-color:var(--accent)}.param-form__resolved{font-size:11px;color:var(--fg-muted);font-family:var(--font-mono)}.hl-wrap{position:relative}.hl-backdrop{position:absolute;inset:0;padding:6px 10px;font-family:var(--font-mono);font-size:13px;line-height:normal;color:transparent;white-space:pre;overflow:hidden;pointer-events:none;border:1px solid transparent;border-radius:var(--radius)}.hl-input{background:transparent!important;position:relative;caret-color:var(--fg)}.hl-wrap .param-form__input{background:transparent}.hl-wrap:before{content:"";position:absolute;inset:0;background:var(--bg-input);border-radius:var(--radius);z-index:-1}.hl-var{background:color-mix(in srgb,var(--accent) 25%,transparent);color:var(--accent);border-radius:3px;padding:0 1px}.body-editor__toolbar{display:flex;align-items:center;gap:8px;margin-bottom:8px}.body-editor__content-type{background:var(--bg-input);color:var(--fg);border:1px solid var(--border);border-radius:var(--radius);padding:4px 8px;font-size:13px}.body-editor__sample-btn{background:none;border:1px solid var(--border);border-radius:var(--radius);color:var(--fg);padding:4px 10px;font-size:12px;cursor:pointer}.body-editor__sample-btn:hover{border-color:var(--accent);color:var(--accent)}.body-editor__textarea{width:100%;min-height:200px;background:var(--bg-input);color:var(--fg);border:1px solid var(--border);border-radius:var(--radius);padding:10px;font-family:var(--font-mono);font-size:13px;line-height:1.5;resize:vertical}.body-editor__kv-table{width:100%;border-collapse:collapse}.body-editor__kv-table th{text-align:left;font-size:11px;font-weight:600;color:var(--fg-muted);padding:4px 8px;border-bottom:1px solid var(--border)}.body-editor__kv-table td{padding:4px 8px}.body-editor__kv-input{width:100%;background:var(--bg-input);color:var(--fg);border:1px solid var(--border);border-radius:3px;padding:4px 8px;font-family:var(--font-mono);font-size:13px}.body-editor__kv-remove{background:none;border:none;color:var(--method-delete);cursor:pointer;font-size:16px;padding:2px 6px}.body-editor__kv-add{background:none;border:1px dashed var(--border);border-radius:var(--radius);color:var(--fg-muted);padding:4px 12px;font-size:12px;cursor:pointer;margin-top:6px}.body-editor__kv-add:hover{border-color:var(--accent);color:var(--accent)}.body-editor__validation{margin-top:6px;font-size:12px;display:flex;align-items:center;gap:4px}.body-editor__validation--valid{color:var(--method-get)}.body-editor__validation--invalid{color:var(--method-delete)}.body-editor__validation--none{color:var(--fg-muted)}.response-panel__empty{display:flex;align-items:center;justify-content:center;height:100%;color:var(--fg-muted);font-size:14px}.response-panel__loading{display:flex;align-items:center;justify-content:center;height:100%;gap:10px;color:var(--fg-muted)}.response-panel__header{display:flex;align-items:center;gap:12px;margin-bottom:12px;flex-wrap:wrap}.response-panel__status{font-weight:700;font-family:var(--font-mono);font-size:14px;padding:3px 10px;border-radius:var(--radius)}.response-panel__status--2xx{color:var(--status-2xx);background:#50fa7b1a}.response-panel__status--3xx{color:var(--status-3xx);background:#f1fa8c1a}.response-panel__status--4xx{color:var(--status-4xx);background:#ffb86c1a}.response-panel__status--5xx{color:var(--status-5xx);background:#ff55551a}.response-panel__meta{font-size:12px;color:var(--fg-muted)}.response-panel__tabs{display:flex;border-bottom:1px solid var(--border);margin-bottom:12px}.response-panel__tab{background:none;border:none;border-bottom:2px solid transparent;color:var(--fg-muted);padding:6px 16px;font-size:13px;cursor:pointer;transition:color var(--transition),border-color var(--transition)}.response-panel__tab:hover{color:var(--fg)}.response-panel__tab--active{color:var(--accent);border-bottom-color:var(--accent)}.body-view__toolbar{display:flex;align-items:center;gap:8px;margin-bottom:8px}.body-view__btn{background:none;border:1px solid var(--border);border-radius:var(--radius);color:var(--fg);padding:3px 10px;font-size:12px;cursor:pointer}.body-view__btn:hover,.body-view__btn--active{border-color:var(--accent);color:var(--accent)}.body-view__pre{background:var(--bg-input);border-radius:var(--radius);padding:12px;overflow:auto;max-height:calc(100vh - 260px);font-size:13px;line-height:1.5}.body-view__pre code{background:none;padding:0;border-radius:0}.body-view__pre .hljs-attr{color:#50fa7b}.body-view__pre .hljs-string{color:#f1fa8c}.body-view__pre .hljs-number{color:#bd93f9}.body-view__pre .hljs-literal,.body-view__pre .hljs-keyword,.body-view__pre .hljs-tag{color:#ff79c6}.body-view__pre .hljs-name{color:#50fa7b}.body-view__pre .hljs-selector-tag{color:#ff79c6}.body-view__pre .hljs-punctuation{color:var(--fg)}.body-view__pre--wrap{white-space:pre-wrap;word-wrap:break-word}.body-view__pre--nowrap{white-space:pre}.body-view__filter-group{display:flex;align-items:center;flex:1;position:relative}.body-view__filter{width:100%;background:var(--bg-input);border:1px solid var(--border);border-radius:var(--radius);color:var(--fg);padding:3px 26px 3px 10px;font-size:12px;font-family:inherit}.body-view__filter::placeholder{color:var(--fg-muted)}.body-view__filter:focus{outline:none;border-color:var(--accent)}.body-view__filter--active{border-color:var(--method-get)}.body-view__filter--error{border-color:var(--method-delete)}.body-view__filter-clear{position:absolute;right:4px;background:none;border:none;color:var(--fg-muted);cursor:pointer;font-size:12px;padding:2px 6px;line-height:1}.body-view__filter-clear:hover{color:var(--fg)}.body-view__filter-error{color:var(--method-delete);font-size:11px;margin-bottom:6px;padding:2px 0}.headers-view__table{width:100%;border-collapse:collapse}.headers-view__table th{text-align:left;font-size:11px;font-weight:600;color:var(--fg-muted);padding:6px 10px;border-bottom:1px solid var(--border)}.headers-view__table td{padding:5px 10px;font-family:var(--font-mono);font-size:13px;border-bottom:1px solid var(--fg-faint)}.headers-view__table td:first-child{color:var(--accent);white-space:nowrap}.headers-view__section-title{font-size:12px;font-weight:600;color:var(--fg-muted);margin:12px 0 6px;cursor:pointer;-webkit-user-select:none;user-select:none}.verbose-view{background:var(--bg-input);border-radius:var(--radius);padding:12px;overflow:auto;max-height:calc(100vh - 220px);font-family:var(--font-mono);font-size:13px;line-height:1.6;white-space:pre-wrap;word-wrap:break-word}.verbose-view__line--comment{color:#6272a4}.verbose-view__line--request{color:#8be9fd}.verbose-view__line--response{color:#50fa7b}.verbose-view__line--other{color:#f8f8f2}.curl-view{position:relative}.curl-view__pre{background:var(--bg-input);border-radius:var(--radius);padding:12px;font-family:var(--font-mono);font-size:13px;line-height:1.5;white-space:pre-wrap;word-wrap:break-word;overflow:auto;max-height:calc(100vh - 220px)}.curl-view__copy-btn{position:absolute;top:8px;right:8px}.env-editor{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center}.env-editor__backdrop{position:absolute;inset:0;background:#0009}.env-editor__dialog{position:relative;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);width:640px;max-width:90vw;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 8px 32px #0006}.env-editor__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--border)}.env-editor__title{font-size:16px;font-weight:600}.env-editor__close{background:none;border:none;color:var(--fg-muted);font-size:20px;cursor:pointer;padding:2px 6px}.env-editor__close:hover{color:var(--fg)}.env-editor__tabs{display:flex;border-bottom:1px solid var(--border);padding:0 16px;overflow-x:auto}.env-editor__tab{background:none;border:none;border-bottom:2px solid transparent;color:var(--fg-muted);padding:8px 14px;font-size:13px;cursor:pointer;white-space:nowrap}.env-editor__tab:hover{color:var(--fg)}.env-editor__tab--active{color:var(--accent);border-bottom-color:var(--accent)}.env-editor__body{flex:1;overflow-y:auto;padding:16px}.env-editor__new-env{display:flex;align-items:center;gap:8px;margin-bottom:12px}.env-editor__new-env-input{flex:1;background:var(--bg-input);color:var(--fg);border:1px solid var(--border);border-radius:var(--radius);padding:6px 10px;font-size:13px}.env-editor__footer{display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:12px 16px;border-top:1px solid var(--border)}.env-editor__save-btn{background:var(--accent);color:var(--bg);border:none;border-radius:var(--radius);padding:6px 20px;font-size:13px;font-weight:600;cursor:pointer}.env-editor__save-btn:hover{background:var(--accent-hover)}.env-editor__delete-btn{background:none;border:1px solid var(--method-delete);border-radius:var(--radius);color:var(--method-delete);padding:6px 14px;font-size:12px;cursor:pointer}.env-editor__delete-btn:hover{background:#ff55551a}.command-palette{position:fixed;inset:0;z-index:2000;display:flex;justify-content:center;padding-top:15vh}.command-palette__backdrop{position:absolute;inset:0;background:#00000080}.command-palette__dialog{position:relative;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);width:520px;max-width:90vw;max-height:60vh;display:flex;flex-direction:column;box-shadow:0 8px 32px #0006}.command-palette__input{background:var(--bg-input);color:var(--fg);border:none;border-bottom:1px solid var(--border);padding:14px 16px;font-size:15px;font-family:var(--font-sans);border-radius:var(--radius) var(--radius) 0 0}.command-palette__input::placeholder{color:var(--fg-muted)}.command-palette__results{flex:1;overflow-y:auto;padding:4px 0}.command-palette__section-title{padding:8px 16px 4px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--fg-muted)}.command-palette__item{display:flex;align-items:center;gap:10px;padding:8px 16px;cursor:pointer;transition:background var(--transition)}.command-palette__item:hover,.command-palette__item--active{background:var(--bg-secondary)}.command-palette__item-label{font-size:14px}.command-palette__item-detail{font-size:12px;color:var(--fg-muted);margin-left:auto}.command-palette__item-shortcut{margin-left:auto;font-family:var(--font-mono);font-size:11px;color:var(--fg-muted);background:var(--bg-input);border:1px solid var(--border);border-radius:3px;padding:1px 6px;white-space:nowrap}.endpoint-doc{border:1px solid var(--border);border-radius:var(--radius);margin-bottom:16px}.endpoint-doc__toggle{display:flex;align-items:center;justify-content:space-between;width:100%;background:none;border:none;color:var(--fg);padding:10px 24px;font-size:14px;font-weight:600;cursor:pointer;text-align:left}.endpoint-doc__toggle:hover{background:var(--bg-secondary)}.endpoint-doc__toggle-icon{color:var(--fg-muted);font-size:12px;transition:transform var(--transition)}.endpoint-doc__toggle-icon--expanded{transform:rotate(90deg)}.endpoint-doc__body{padding:0 24px 14px;font-size:13px;line-height:1.6}.endpoint-doc__deprecated{background:#ff55551a;color:var(--method-delete);padding:6px 10px;border-radius:var(--radius);font-size:12px;font-weight:600;margin-bottom:8px}.endpoint-doc__body h1,.endpoint-doc__body h2,.endpoint-doc__body h3{margin-top:.8em;margin-bottom:.4em}.endpoint-doc__body p{margin-bottom:.5em}.endpoint-doc__body code{font-size:.9em}.body-editor__schema{margin-top:8px}.body-editor__schema-toggle{display:flex;align-items:center;justify-content:space-between;width:100%;background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius);padding:6px 12px;color:var(--fg-muted);font-size:12px;cursor:pointer}.body-editor__schema-toggle:hover{color:var(--fg)}.body-editor__schema-body{padding:10px 12px;border:1px solid var(--border);border-top:none;border-radius:0 0 var(--radius) var(--radius);font-size:12px;font-family:var(--font-mono);max-height:300px;overflow-y:auto}.schema-view__prop{padding:3px 0;display:flex;align-items:baseline;flex-wrap:wrap;gap:6px}.schema-view__name{color:var(--accent);font-weight:600}.schema-view__type{color:var(--fg-muted);font-size:11px}.schema-view__required{color:var(--method-delete);font-size:10px;font-weight:600}.schema-view__desc{color:var(--fg-muted);font-style:italic;font-family:var(--font-sans);font-size:11px}.schema-view__enum{color:var(--fg-muted);font-size:10px}.validation-details{display:inline-flex;align-items:center;gap:6px;cursor:pointer;font-size:13px}.validation-details__indicator--valid{color:var(--method-get)}.validation-details__indicator--invalid{color:var(--method-delete)}.validation-details__indicator--none{color:var(--fg-muted)}.validation-details__errors{margin-top:8px;font-size:12px}.validation-details__error{padding:6px 10px;border-left:3px solid var(--method-delete);background:#ff55550d;margin-bottom:4px;border-radius:0 var(--radius) var(--radius) 0}.validation-details__error-path{font-family:var(--font-mono);color:var(--accent);font-size:11px}.validation-details__error-msg{color:var(--fg)}.validation-details__error-detail{color:var(--fg-muted);font-size:11px}.copy-btn{background:none;border:1px solid var(--border);border-radius:var(--radius);color:var(--fg);padding:3px 10px;font-size:12px;cursor:pointer}.copy-btn:hover{border-color:var(--accent);color:var(--accent)}[data-theme=light] .body-view__pre .hljs-attr{color:#1a8a3f}[data-theme=light] .body-view__pre .hljs-string{color:#b35c00}[data-theme=light] .body-view__pre .hljs-number{color:#7c3aed}[data-theme=light] .body-view__pre .hljs-literal,[data-theme=light] .body-view__pre .hljs-keyword,[data-theme=light] .body-view__pre .hljs-tag{color:#d6336c}[data-theme=light] .body-view__pre .hljs-name{color:#1a8a3f}[data-theme=light] .body-view__pre .hljs-selector-tag{color:#d6336c}[data-theme=light] .body-view__pre .hljs-punctuation{color:var(--fg)}.resize-handle{flex-shrink:0;background:var(--border);transition:background .15s}.resize-handle--horizontal{width:4px;cursor:col-resize}.resize-handle--vertical{height:4px;cursor:row-resize}.resize-handle:hover,.resize-handle:active{background:var(--accent)}