@testsmith/testblocks 0.9.7 → 0.9.8

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.
@@ -16,10 +16,30 @@
16
16
  overflow: hidden;
17
17
  }
18
18
  </style>
19
- <script type="module" crossorigin src="/assets/index-CMMJHs_d.js"></script>
20
- <link rel="stylesheet" crossorigin href="/assets/index-B8OSvcUg.css">
19
+ <script type="module" crossorigin src="/assets/index-CYoJ_6bl.js"></script>
20
+ <link rel="stylesheet" crossorigin href="/assets/index-CDAzk2fI.css">
21
21
  </head>
22
22
  <body>
23
+ <div id="chrome-warning" style="display:none;align-items:center;justify-content:center;height:100vh;background:#1e1e2e;color:#cdd6f4;font-family:system-ui,sans-serif;">
24
+ <div style="text-align:center;max-width:480px;padding:2rem;">
25
+ <div style="font-size:3rem;margin-bottom:1rem;">&#9888;&#65039;</div>
26
+ <h1 style="margin:0 0 1rem;font-size:1.5rem;">Chrome Required</h1>
27
+ <p style="margin:0 0 1.5rem;color:#a6adc8;line-height:1.6;">
28
+ TestBlocks only works in Google Chrome. Please open this page in Chrome to continue.
29
+ </p>
30
+ <a href="https://www.google.com/chrome/" target="_blank" rel="noopener noreferrer"
31
+ style="color:#89b4fa;text-decoration:underline;">Download Chrome</a>
32
+ </div>
33
+ </div>
23
34
  <div id="root"></div>
35
+ <script>
36
+ (function() {
37
+ var isChrome = /Chrome/.test(navigator.userAgent) && !/Edg/.test(navigator.userAgent);
38
+ if (!isChrome) {
39
+ document.getElementById('chrome-warning').style.display = 'flex';
40
+ document.getElementById('root').style.display = 'none';
41
+ }
42
+ })();
43
+ </script>
24
44
  </body>
25
45
  </html>
@@ -31,7 +31,7 @@ class ApiDeleteBlock extends base_1.StatementBlock {
31
31
  });
32
32
  const parsed = await (0, utils_1.parseResponse)(response);
33
33
  (0, utils_1.storeResponse)(context, parsed);
34
- return parsed;
34
+ return { ...parsed, _summary: `DELETE ${url}`, _requestHeaders: headers };
35
35
  }
36
36
  }
37
37
  exports.ApiDeleteBlock = ApiDeleteBlock;
@@ -30,7 +30,7 @@ class ApiGetBlock extends base_1.StatementBlock {
30
30
  });
31
31
  const parsed = await (0, utils_1.parseResponse)(response);
32
32
  (0, utils_1.storeResponse)(context, parsed);
33
- return parsed;
33
+ return { ...parsed, _summary: `GET ${url}`, _requestHeaders: headers };
34
34
  }
35
35
  }
36
36
  exports.ApiGetBlock = ApiGetBlock;
@@ -41,7 +41,8 @@ class ApiPatchBlock extends base_1.StatementBlock {
41
41
  });
42
42
  const parsed = await (0, utils_1.parseResponse)(response);
43
43
  (0, utils_1.storeResponse)(context, parsed);
44
- return parsed;
44
+ const requestHeaders = { 'Content-Type': 'application/json', ...inlineHeaders };
45
+ return { ...parsed, _summary: `PATCH ${url}`, _requestHeaders: requestHeaders, _requestBody: body };
45
46
  }
46
47
  }
47
48
  exports.ApiPatchBlock = ApiPatchBlock;
@@ -41,7 +41,8 @@ class ApiPostBlock extends base_1.StatementBlock {
41
41
  });
42
42
  const parsed = await (0, utils_1.parseResponse)(response);
43
43
  (0, utils_1.storeResponse)(context, parsed);
44
- return parsed;
44
+ const requestHeaders = { 'Content-Type': 'application/json', ...inlineHeaders };
45
+ return { ...parsed, _summary: `POST ${url}`, _requestHeaders: requestHeaders, _requestBody: body };
45
46
  }
46
47
  }
47
48
  exports.ApiPostBlock = ApiPostBlock;
@@ -41,7 +41,8 @@ class ApiPutBlock extends base_1.StatementBlock {
41
41
  });
42
42
  const parsed = await (0, utils_1.parseResponse)(response);
43
43
  (0, utils_1.storeResponse)(context, parsed);
44
- return parsed;
44
+ const requestHeaders = { 'Content-Type': 'application/json', ...inlineHeaders };
45
+ return { ...parsed, _summary: `PUT ${url}`, _requestHeaders: requestHeaders, _requestBody: body };
45
46
  }
46
47
  }
47
48
  exports.ApiPutBlock = ApiPutBlock;
@@ -8,6 +8,20 @@ export interface ExecutorOptions {
8
8
  testIdAttribute?: string;
9
9
  baseDir?: string;
10
10
  procedures?: Record<string, ProcedureDefinition>;
11
+ locale?: string;
12
+ timezoneId?: string;
13
+ geolocation?: {
14
+ latitude: number;
15
+ longitude: number;
16
+ };
17
+ viewport?: {
18
+ width: number;
19
+ height: number;
20
+ };
21
+ localStorage?: {
22
+ name: string;
23
+ value: string;
24
+ }[];
11
25
  }
12
26
  export declare class TestExecutor {
13
27
  private options;
@@ -121,10 +121,30 @@ class TestExecutor {
121
121
  this.browser = await playwright_1.chromium.launch({
122
122
  headless: this.options.headless,
123
123
  });
124
- // Use consistent viewport size for headless and headed modes
125
- this.context = await this.browser.newContext({
126
- viewport: { width: 1920, height: 1080 },
127
- });
124
+ // Build browser context options
125
+ const contextOptions = {
126
+ viewport: this.options.viewport || { width: 1920, height: 1080 },
127
+ };
128
+ if (this.options.locale) {
129
+ contextOptions.locale = this.options.locale;
130
+ }
131
+ if (this.options.timezoneId) {
132
+ contextOptions.timezoneId = this.options.timezoneId;
133
+ }
134
+ if (this.options.geolocation) {
135
+ contextOptions.geolocation = this.options.geolocation;
136
+ contextOptions.permissions = ['geolocation'];
137
+ }
138
+ console.log('Browser context options:', JSON.stringify(contextOptions));
139
+ this.context = await this.browser.newContext(contextOptions);
140
+ if (this.options.localStorage && this.options.localStorage.length > 0) {
141
+ const items = this.options.localStorage;
142
+ await this.context.addInitScript((storageItems) => {
143
+ for (const item of storageItems) {
144
+ localStorage.setItem(item.name, item.value);
145
+ }
146
+ }, items);
147
+ }
128
148
  this.page = await this.context.newPage();
129
149
  if (this.options.timeout) {
130
150
  this.page.setDefaultTimeout(this.options.timeout);
@@ -189,8 +209,8 @@ class TestExecutor {
189
209
  }
190
210
  const sharedContext = {
191
211
  variables: new Map(Object.entries({
192
- ...this.resolveVariableDefaults(testFile.variables),
193
212
  ...this.resolveVariableDefaults(this.options.variables),
213
+ ...this.resolveVariableDefaults(testFile.variables),
194
214
  })),
195
215
  results: [],
196
216
  browser: this.browser,
@@ -414,8 +434,8 @@ class TestExecutor {
414
434
  const baseVariables = sharedContext
415
435
  ? Object.fromEntries(sharedContext.variables)
416
436
  : {
417
- ...this.resolveVariableDefaults(fileVariables),
418
437
  ...this.resolveVariableDefaults(this.options.variables),
438
+ ...this.resolveVariableDefaults(fileVariables),
419
439
  };
420
440
  const context = {
421
441
  variables: new Map(Object.entries(baseVariables)),
@@ -504,8 +524,8 @@ class TestExecutor {
504
524
  const baseVariables = sharedContext
505
525
  ? Object.fromEntries(sharedContext.variables)
506
526
  : {
507
- ...this.resolveVariableDefaults(fileVariables),
508
527
  ...this.resolveVariableDefaults(this.options.variables),
528
+ ...this.resolveVariableDefaults(fileVariables),
509
529
  };
510
530
  const context = {
511
531
  variables: new Map(Object.entries(baseVariables)),
@@ -403,14 +403,37 @@ async function startServer(options = {}) {
403
403
  const globalVars = (0, globals_1.getGlobalVariables)();
404
404
  const globalProcs = (0, globals_1.getGlobalProcedures)();
405
405
  const testIdAttr = (0, globals_1.getTestIdAttribute)();
406
- const executor = new executor_1.TestExecutor({
406
+ const executorOpts = {
407
407
  headless: req.query.headless !== 'false',
408
408
  timeout: Number(req.query.timeout) || (0, globals_1.getGlobalTimeout)(),
409
409
  variables: globalVars,
410
410
  procedures: globalProcs,
411
- testIdAttribute: testIdAttr,
411
+ testIdAttribute: req.query.testIdAttribute || testIdAttr,
412
412
  baseDir: globalsDir,
413
- });
413
+ };
414
+ if (req.query.locale)
415
+ executorOpts.locale = req.query.locale;
416
+ if (req.query.timezoneId)
417
+ executorOpts.timezoneId = req.query.timezoneId;
418
+ if (req.query.geoLatitude && req.query.geoLongitude) {
419
+ executorOpts.geolocation = {
420
+ latitude: parseFloat(req.query.geoLatitude),
421
+ longitude: parseFloat(req.query.geoLongitude),
422
+ };
423
+ }
424
+ if (req.query.viewportWidth && req.query.viewportHeight) {
425
+ executorOpts.viewport = {
426
+ width: parseInt(req.query.viewportWidth, 10),
427
+ height: parseInt(req.query.viewportHeight, 10),
428
+ };
429
+ }
430
+ if (req.query.localStorage) {
431
+ try {
432
+ executorOpts.localStorage = JSON.parse(req.query.localStorage);
433
+ }
434
+ catch { /* ignore invalid JSON */ }
435
+ }
436
+ const executor = new executor_1.TestExecutor(executorOpts);
414
437
  const results = await executor.runTestFile(mergedTestFile);
415
438
  const passed = results.filter(r => r.status === 'passed').length;
416
439
  const failed = results.filter(r => r.status === 'failed').length;
@@ -453,14 +476,37 @@ async function startServer(options = {}) {
453
476
  const globalVars = (0, globals_1.getGlobalVariables)();
454
477
  const globalProcs = (0, globals_1.getGlobalProcedures)();
455
478
  const testIdAttr = (0, globals_1.getTestIdAttribute)();
456
- const executor = new executor_1.TestExecutor({
479
+ const executorOpts = {
457
480
  headless: req.query.headless !== 'false',
458
481
  timeout: Number(req.query.timeout) || (0, globals_1.getGlobalTimeout)(),
459
482
  variables: globalVars,
460
483
  procedures: globalProcs,
461
- testIdAttribute: testIdAttr,
484
+ testIdAttribute: req.query.testIdAttribute || testIdAttr,
462
485
  baseDir: globalsDir,
463
- });
486
+ };
487
+ if (req.query.locale)
488
+ executorOpts.locale = req.query.locale;
489
+ if (req.query.timezoneId)
490
+ executorOpts.timezoneId = req.query.timezoneId;
491
+ if (req.query.geoLatitude && req.query.geoLongitude) {
492
+ executorOpts.geolocation = {
493
+ latitude: parseFloat(req.query.geoLatitude),
494
+ longitude: parseFloat(req.query.geoLongitude),
495
+ };
496
+ }
497
+ if (req.query.viewportWidth && req.query.viewportHeight) {
498
+ executorOpts.viewport = {
499
+ width: parseInt(req.query.viewportWidth, 10),
500
+ height: parseInt(req.query.viewportHeight, 10),
501
+ };
502
+ }
503
+ if (req.query.localStorage) {
504
+ try {
505
+ executorOpts.localStorage = JSON.parse(req.query.localStorage);
506
+ }
507
+ catch { /* ignore invalid JSON */ }
508
+ }
509
+ const executor = new executor_1.TestExecutor(executorOpts);
464
510
  // Register file-level procedures (overrides globals)
465
511
  if (mergedTestFile.procedures) {
466
512
  executor.registerProcedures(mergedTestFile.procedures);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testsmith/testblocks",
3
- "version": "0.9.7",
3
+ "version": "0.9.8",
4
4
  "description": "Visual test automation tool with Blockly - API and Playwright testing",
5
5
  "author": "Roy de Kleijn",
6
6
  "license": "MIT",
@@ -1 +0,0 @@
1
- :root{--ts-blue: #205d96;--ts-blue-light: #e0f2fe;--ts-blue-dark: #1a4a78;--ts-green: #9fc93c;--ts-green-light: #ecfccb;--ts-green-dark: #5c7a1f;--ts-gray: #59575d;--ts-gray-light: #6b7280;--ts-gray-bg: #f8fafc;--primary-color: var(--ts-blue);--primary-dark: var(--ts-blue-dark);--primary-light: var(--ts-blue-light);--secondary-color: var(--ts-gray);--background: var(--ts-gray-bg);--surface: #ffffff;--error: #d32f2f;--success: var(--ts-green-dark);--success-light: var(--ts-green-light);--warning: #e67e00;--text-primary: var(--ts-gray);--text-secondary: var(--ts-gray-light);--border-color: #e0e0e0}*{margin:0;padding:0;box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,sans-serif;background-color:var(--background);color:var(--text-primary)}.app{display:flex;flex-direction:column;height:100vh;overflow:hidden}.header{display:flex;align-items:center;justify-content:space-between;padding:0 16px;height:56px;background:var(--primary-color);color:#fff;box-shadow:0 2px 4px #0000001a;z-index:100}.header h1{font-size:20px;font-weight:500;display:flex;align-items:center;gap:8px}.header-actions{display:flex;gap:8px}.btn{display:inline-flex;align-items:center;gap:6px;padding:8px 16px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s,opacity .2s}.btn:disabled{opacity:.6;cursor:not-allowed}.btn-primary{background:#fff;color:var(--primary-color)}.btn-primary:hover:not(:disabled){background:#ffffffe6}.btn-secondary{background:#ffffff26;color:#fff}.btn-secondary:hover:not(:disabled){background:#ffffff40}.btn-success{background:var(--ts-green);color:#fff}.btn-success:hover:not(:disabled){background:var(--ts-green-dark)}.btn-danger{background:var(--error);color:#fff}.btn-danger:hover:not(:disabled){background:#c62828}.main-content{display:flex;flex:1;overflow:hidden}.sidebar{width:280px;background:var(--surface);border-right:1px solid var(--border-color);display:flex;flex-direction:column;overflow:hidden;transition:width .2s ease}.sidebar.collapsed{width:40px}.sidebar-toggle-header{display:flex;align-items:center;justify-content:flex-end;padding:8px;border-bottom:1px solid var(--border-color)}.sidebar.collapsed .sidebar-toggle-header{justify-content:center}.sidebar-header{padding:16px;border-bottom:1px solid var(--border-color)}.sidebar-header h2{font-size:14px;font-weight:600;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.5px}.test-list{flex:1;overflow-y:auto;padding:8px}.test-item{padding:12px;border-radius:4px;cursor:pointer;border:1px solid transparent;margin-bottom:4px;transition:background-color .2s}.test-item:hover{background:var(--background)}.test-item.active{background:#1976d214;border-color:var(--primary-color)}.test-item-name{font-weight:500;font-size:14px;margin-bottom:4px}.test-item-steps{font-size:12px;color:var(--text-secondary)}.add-test-btn{margin:8px;padding:12px;border:2px dashed var(--border-color);border-radius:4px;background:transparent;color:var(--text-secondary);cursor:pointer;font-size:14px;transition:border-color .2s,color .2s}.add-test-btn:hover{border-color:var(--primary-color);color:var(--primary-color)}.editor-area{flex:1;display:flex;flex-direction:column;overflow:hidden}.editor-toolbar{display:flex;align-items:center;justify-content:space-between;padding:8px 16px;background:var(--surface);border-bottom:1px solid var(--border-color)}.test-name-input{font-size:16px;font-weight:500;padding:8px 12px;border:1px solid var(--border-color);border-radius:4px;width:300px}.test-name-input:focus{outline:none;border-color:var(--primary-color)}.blockly-container{flex:1;position:relative}#blockly-div{position:absolute;top:0;left:0;right:0;bottom:0}.blocklyTreeSeparator{height:1px!important;margin:12px 8px!important;background-color:#0006!important;border:none!important}.blocklyWidgetDiv .blocklyHtmlInput{max-width:350px;font-family:monospace;font-size:12px;line-height:1.4}.results-panel{width:350px;background:var(--surface);border-left:1px solid var(--border-color);display:flex;flex-direction:column;overflow:hidden;transition:width .2s ease}.results-panel.collapsed{width:40px}.results-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--border-color);gap:12px}.results-panel.collapsed .results-header{justify-content:center;padding:12px 8px}.panel-toggle-btn{background:none;border:none;color:var(--text-secondary);cursor:pointer;font-size:12px;padding:4px 8px;border-radius:4px;flex-shrink:0}.panel-toggle-btn:hover{background:var(--hover-bg);color:var(--text-primary)}.results-header h2{font-size:14px;font-weight:600;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.5px;flex:1;display:flex;align-items:center;gap:8px}.results-summary{display:flex;gap:12px;font-size:13px;font-weight:500;padding:10px 16px;background:var(--bg-tertiary);border-bottom:1px solid var(--border-color)}.results-summary .passed-count{color:var(--success)}.results-summary .failed-count{color:var(--danger)}.results-summary .skipped-count{color:#f59e0b}.results-actions{display:flex;gap:8px}.btn-report{padding:4px 8px;font-size:11px;font-weight:500;border:1px solid var(--border-color);border-radius:4px;background:var(--surface);color:var(--text-secondary);cursor:pointer;transition:all .2s}.btn-report:hover{background:var(--primary-light);border-color:var(--primary-color);color:var(--primary-color)}.results-content{flex:1;overflow-y:auto;padding:16px}.result-item{padding:12px;border-radius:4px;margin-bottom:8px;border-left:4px solid}.result-item.passed{background:#388e3c14;border-left-color:var(--success)}.result-item.failed{background:#d32f2f14;border-left-color:var(--error)}.result-item.running{background:#1976d214;border-left-color:var(--primary-color)}.result-step{font-size:13px;margin-bottom:4px}.result-error{font-size:12px;color:var(--error);font-family:monospace;white-space:pre-wrap;margin-top:8px}.result-duration{font-size:11px;color:var(--text-secondary)}.modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:1000}.modal{background:var(--surface);border-radius:8px;padding:24px;min-width:400px;max-width:600px;max-height:80vh;overflow-y:auto;box-shadow:0 4px 20px #00000026}.modal h2{margin-bottom:16px}.modal-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:24px}.folder-hooks-modal{background:var(--surface);border-radius:8px;width:90vw;max-width:1200px;height:80vh;display:flex;flex-direction:column;box-shadow:0 4px 20px #00000026}.folder-hooks-header{padding:16px 24px;border-bottom:1px solid var(--border-color);position:relative}.folder-hooks-header h2{margin:0 0 4px;font-size:18px}.folder-hooks-description{font-size:13px;color:var(--text-secondary);margin:0}.folder-hooks-tabs{display:flex;border-bottom:1px solid var(--border-color);padding:0 16px;background:var(--background)}.folder-hooks-tab{padding:12px 16px;border:none;background:transparent;color:var(--text-secondary);font-size:13px;font-weight:500;cursor:pointer;border-bottom:2px solid transparent;display:flex;align-items:center;gap:8px}.folder-hooks-tab:hover{color:var(--text-primary)}.folder-hooks-tab.active{color:var(--primary-color);border-bottom-color:var(--primary-color)}.folder-hooks-tab .tab-badge{font-size:10px;padding:2px 6px;border-radius:10px;background:var(--primary-color);color:#fff}.folder-hooks-workspace{flex:1;min-height:0}.folder-hooks-footer{padding:16px 24px;border-top:1px solid var(--border-color);display:flex;justify-content:flex-end;gap:8px}.form-group{margin-bottom:16px}.form-group label{display:block;font-size:14px;font-weight:500;margin-bottom:6px}.form-group input,.form-group textarea{width:100%;padding:10px 12px;border:1px solid var(--border-color);border-radius:4px;font-size:14px}.form-group input:focus,.form-group textarea:focus{outline:none;border-color:var(--primary-color)}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--text-secondary);text-align:center;padding:40px}.empty-state h3{margin-bottom:8px;color:var(--text-primary)}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:var(--background)}::-webkit-scrollbar-thumb{background:#bdbdbd;border-radius:4px}::-webkit-scrollbar-thumb:hover{background:#9e9e9e}.status-indicator{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:6px}.status-indicator.running{background:var(--primary-color);animation:pulse 1s infinite}.status-indicator.passed{background:var(--success)}.status-indicator.failed{background:var(--error)}.sidebar-section{border-bottom:1px solid var(--border-color)}.sidebar-section:last-child{border-bottom:none;flex:1;display:flex;flex-direction:column;overflow:hidden}.sidebar-header.clickable{cursor:pointer;-webkit-user-select:none;user-select:none}.sidebar-header.clickable:hover{background:var(--background)}.variables-list{padding:8px}.variable-item{display:flex;align-items:center;gap:8px;padding:6px 0}.variable-name{font-family:monospace;font-size:12px;color:var(--primary-color);min-width:80px;flex-shrink:0}.variable-value{flex:1;padding:4px 8px;border:1px solid var(--border-color);border-radius:4px;font-size:12px}.variable-value:focus{outline:none;border-color:var(--primary-color)}.global-variables{background:linear-gradient(to right,rgba(32,93,150,.05),transparent)}.variable-item.global{padding:4px 0}.variable-item.global .variable-name{color:var(--ts-blue);font-size:11px;word-break:break-all}.variable-value.readonly{background:var(--background);color:var(--text-secondary);font-family:monospace;font-size:11px;padding:2px 6px;border:none;border-radius:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.variable-value.global-input{flex:1;padding:4px 6px;border:1px solid rgba(32,93,150,.3);border-radius:4px;font-size:11px;font-family:monospace;background:#205d960d;color:var(--text-primary)}.variable-value.global-input:focus{outline:none;border-color:var(--ts-blue);background:#205d961a}.variable-group{margin-bottom:8px}.variable-group-header{font-weight:600;font-size:11px;color:var(--text-secondary);text-transform:uppercase;padding:4px 0;border-bottom:1px solid var(--border-color);margin-bottom:4px}.variable-group-content{padding-left:12px;border-left:2px solid rgba(32,93,150,.3)}.global-badge{font-size:12px;margin-left:8px;opacity:.7}.btn-icon{background:none;border:none;color:var(--text-secondary);cursor:pointer;padding:4px 8px;font-size:16px;line-height:1;border-radius:4px}.btn-icon:hover{background:var(--background);color:var(--error)}.add-variable-btn{width:100%;margin-top:8px;padding:8px;border:1px dashed var(--border-color);border-radius:4px;background:transparent;color:var(--text-secondary);cursor:pointer;font-size:12px}.add-variable-btn:hover{border-color:var(--primary-color);color:var(--primary-color)}.test-item{display:flex;align-items:center;justify-content:space-between}.test-item-content{flex:1;min-width:0}.test-item-name{display:flex;align-items:center;gap:6px}.data-driven-badge{font-size:10px;font-weight:600;background:#1565c0;color:#fff;padding:1px 5px;border-radius:8px;margin-left:4px}.btn-run-test{background:none;border:1px solid var(--border-color);border-radius:4px;padding:4px 8px;cursor:pointer;font-size:10px;color:var(--text-secondary);flex-shrink:0;margin-left:8px}.btn-run-test:hover:not(:disabled){background:var(--primary-color);border-color:var(--primary-color);color:#fff}.btn-run-test:disabled{opacity:.5;cursor:not-allowed}.status-dot{display:inline-block;width:8px;height:8px;border-radius:50%;flex-shrink:0}.status-dot.passed{background:var(--success)}.status-dot.failed{background:var(--error)}.test-item.passed{background:#388e3c0d}.test-item.failed{background:#d32f2f0d}.test-item.passed.active{background:#388e3c1f}.test-item.failed.active{background:#d32f2f1f}.test-item.disabled{opacity:.6;background:#9e9e9e0d}.test-item.disabled.active{background:#9e9e9e1f}.test-item.disabled .btn-run-test{opacity:.4;cursor:not-allowed}.test-name-disabled{text-decoration:line-through;color:var(--text-secondary)}.status-dot.skipped{background:#f59e0b}.test-item.skipped{background:#f59e0b0d}.test-item.skipped.active{background:#f59e0b1f}.test-id-indicator{display:flex;align-items:center;gap:6px;color:#ffffffe6;font-size:13px;padding:6px 12px;border-radius:4px;background:#ffffff1a}.test-id-indicator code{font-family:Monaco,Menlo,monospace;background:#fff3;padding:2px 6px;border-radius:3px;font-size:12px}.headless-toggle{display:flex;align-items:center;gap:6px;color:#fff;font-size:14px;cursor:pointer;padding:8px 12px;border-radius:4px;background:#ffffff1a}.headless-toggle:hover{background:#fff3}.headless-toggle input[type=checkbox]{width:16px;height:16px;cursor:pointer}.create-block-modal{min-width:500px;max-width:600px}.create-block-modal h2{color:var(--text-primary);margin-bottom:20px}.helper-text{font-size:12px;color:var(--text-secondary);margin-bottom:8px}.color-picker{display:flex;gap:8px}.color-option{width:32px;height:32px;border-radius:4px;border:2px solid transparent;cursor:pointer;transition:transform .1s,border-color .1s}.color-option:hover{transform:scale(1.1)}.color-option.selected{border-color:var(--text-primary)}.param-list{max-height:200px;overflow-y:auto;border:1px solid var(--border-color);border-radius:4px;padding:8px}.empty-params{color:var(--text-secondary);font-size:13px;padding:12px;text-align:center}.param-item{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:4px;cursor:pointer;font-size:13px}.param-item:hover{background:var(--background)}.param-item input[type=checkbox]{flex-shrink:0}.param-name{font-weight:500;color:var(--primary-color)}.param-source{color:var(--text-secondary);font-size:11px}.param-default{color:var(--text-secondary);font-size:11px;margin-left:auto;font-family:monospace}.steps-preview{max-height:150px;overflow-y:auto;border:1px solid var(--border-color);border-radius:4px;padding:8px}.step-preview-item{display:flex;align-items:center;gap:8px;padding:4px 8px;font-size:13px}.step-number{color:var(--text-secondary);font-size:11px;width:20px}.step-type{color:var(--text-primary)}.create-block-modal .btn-primary{background:var(--primary-color);color:#fff}.create-block-modal .btn-primary:hover{background:var(--primary-dark)}.create-block-modal .btn-secondary{background:var(--background);color:var(--text-primary)}.result-test-header{display:flex;align-items:center;gap:8px;margin-bottom:8px}.result-file-name{color:var(--text-secondary);font-size:12px;margin-right:4px}.result-test-name{font-weight:500;flex:1}.result-steps{margin-top:8px;border-top:1px solid var(--border-color);padding-top:8px}.step-result-item{margin-bottom:6px;border-radius:4px;background:#00000005}.step-result-item.passed{border-left:3px solid var(--success)}.step-result-item.failed{border-left:3px solid var(--error)}.step-result-header{display:flex;align-items:center;gap:8px;padding:6px 8px;cursor:pointer;-webkit-user-select:none;user-select:none}.step-result-header:hover{background:#0000000a}.step-result-type{font-size:12px;color:var(--text-primary);white-space:nowrap}.step-result-summary{flex:1;font-size:11px;color:var(--text-secondary);font-family:monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:300px}.step-result-duration{font-size:11px;color:var(--text-secondary);white-space:nowrap}.step-result-expand{font-size:10px;color:var(--text-secondary);width:16px;text-align:center}.step-result-header.expandable{cursor:pointer}.step-result-header.expandable:hover{background:#0000000a}.step-result-details{padding:8px;background:var(--background);border-radius:0 0 4px 4px}.step-output{background:var(--surface);border-radius:4px;padding:8px}.step-result-error{padding:6px 8px;font-size:11px;color:var(--error);background:#d32f2f0d;font-family:monospace}.step-screenshot{margin-bottom:12px}.screenshot-label{font-size:11px;font-weight:600;color:var(--text-secondary);margin-bottom:6px;text-transform:uppercase;letter-spacing:.5px}.failure-screenshot{max-width:100%;max-height:300px;border:2px solid var(--error);border-radius:6px;cursor:pointer;transition:transform .2s,box-shadow .2s;display:block}.failure-screenshot:hover{transform:scale(1.02);box-shadow:0 4px 12px #d32f2f4d}.stack-trace{font-size:10px;line-height:1.4;color:var(--error);max-height:200px;overflow-y:auto}.step-result-response{padding:8px;background:var(--background);border-radius:0 0 4px 4px}.response-status{display:flex;align-items:center;gap:8px;margin-bottom:8px}.response-label{font-size:11px;color:var(--text-secondary)}.response-status-code{font-family:monospace;font-size:12px;font-weight:600;padding:2px 6px;border-radius:3px;background:var(--surface)}.response-status-code.status-success{color:var(--success);background:#388e3c1a}.response-status-code.status-client-error{color:var(--warning);background:#f57c001a}.response-status-code.status-server-error{color:var(--error);background:#d32f2f1a}.response-section{margin-top:8px}.response-section summary{font-size:11px;color:var(--text-secondary);cursor:pointer;padding:4px 0;-webkit-user-select:none;user-select:none}.response-section summary:hover{color:var(--primary-color)}.response-pre{margin:4px 0 0;padding:8px;background:var(--surface);border:1px solid var(--border-color);border-radius:4px;font-size:11px;font-family:monospace;overflow-x:auto;max-height:200px;overflow-y:auto;white-space:pre-wrap;word-break:break-all}.lifecycle-badge{display:inline-block;font-size:10px;font-weight:600;text-transform:uppercase;padding:2px 6px;border-radius:3px;background:var(--ts-gray);color:#fff;letter-spacing:.5px}.result-item.lifecycle,.result-item.lifecycle.passed{border-left-color:var(--ts-gray);background:#59575d14}.result-item.lifecycle.failed{border-left-color:#d32f2f;background:#d32f2f14}.editor-tabs{display:flex;background:var(--surface);border-bottom:1px solid var(--border-color);padding:0 8px}.editor-tab{padding:10px 16px;border:none;background:transparent;color:var(--text-secondary);font-size:13px;font-weight:500;cursor:pointer;border-bottom:2px solid transparent;transition:all .2s;display:flex;align-items:center;gap:6px}.editor-tab:hover{color:var(--text-primary);background:var(--background)}.editor-tab.active{color:var(--primary-color);border-bottom-color:var(--primary-color)}.editor-tab.test-tab{margin:0 auto 0 0;font-weight:600}.editor-tab.test-tab.active{color:var(--primary-color)}.tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;font-size:11px;font-weight:600;background:var(--ts-gray);color:#fff;border-radius:9px}.editor-tab.active .tab-badge{background:var(--primary-color)}.lifecycle-toolbar-info{display:flex;align-items:center;gap:8px;color:var(--text-primary);font-size:14px;font-weight:500}.lifecycle-icon{font-size:16px}.lifecycle-hint{color:var(--text-secondary);font-weight:400;font-size:13px}.sidebar-tabs{display:flex;border-bottom:1px solid var(--border-color)}.sidebar-tab{flex:1;padding:10px;border:none;background:transparent;color:var(--text-secondary);font-size:13px;font-weight:500;cursor:pointer;border-bottom:2px solid transparent;transition:all .2s}.sidebar-tab:hover{color:var(--text-primary);background:var(--background)}.sidebar-tab.active{color:var(--primary-color);border-bottom-color:var(--primary-color)}.file-tree-section,.file-tree{flex:1;overflow:hidden;display:flex;flex-direction:column}.file-tree-header{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;border-bottom:1px solid var(--border-color);background:var(--background)}.file-tree-root-name{font-weight:600;font-size:13px;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-tree-refresh{background:none;border:none;color:var(--text-secondary);cursor:pointer;font-size:16px;padding:4px;border-radius:4px}.file-tree-refresh:hover{background:var(--surface);color:var(--primary-color)}.file-tree-header-actions{display:flex;align-items:center;gap:4px}.file-tree-action-btn{background:none;border:none;color:var(--text-secondary);cursor:pointer;font-size:12px;padding:4px 6px;border-radius:4px}.file-tree-action-btn:hover{background:var(--surface);color:var(--primary-color)}.folder-actions{position:relative;margin-left:auto}.folder-action-btn{background:none;border:none;color:var(--text-secondary);cursor:pointer;font-size:14px;font-weight:700;width:20px;height:20px;border-radius:4px;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .15s}.file-tree-item:hover .folder-action-btn{opacity:1}.folder-action-btn:hover{background:var(--primary-light);color:var(--primary-color)}.folder-actions-menu{position:absolute;top:100%;right:0;background:#fff;border:1px solid var(--border-color);border-radius:6px;box-shadow:0 4px 12px #00000026;z-index:100;min-width:140px;padding:4px 0}.folder-actions-menu button{display:flex;align-items:center;gap:8px;width:100%;padding:8px 12px;border:none;background:none;text-align:left;font-size:13px;color:var(--text-primary);cursor:pointer}.folder-actions-menu button:hover{background:var(--primary-light);color:var(--primary-color)}.folder-actions-menu button.delete-action:hover{background:#fee2e2;color:#dc2626}.folder-actions-menu button.run-action{color:#16a34a;font-weight:500}.folder-actions-menu button.run-action:hover{background:#dcfce7;color:#15803d}.folder-actions-menu button:disabled{opacity:.5;cursor:not-allowed}.file-tree-run-btn{color:#16a34a!important}.file-tree-run-btn:hover:not(:disabled){background:#dcfce7!important}.file-tree-run-btn:disabled{opacity:.5;cursor:not-allowed}.file-tree-content{flex:1;overflow-y:auto;padding:4px 0}.file-tree-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:32px 16px;color:var(--text-secondary);text-align:center}.file-tree-empty p{margin:4px 0;font-size:13px}.file-tree-hint{font-size:12px;opacity:.7}.file-tree-item{display:flex;align-items:center;gap:6px;padding:6px 8px;cursor:pointer;font-size:13px;color:var(--text-primary);border-left:2px solid transparent;transition:all .15s;position:relative}.file-tree-item:hover{background:var(--background)}.file-tree-item.selected{background:#1976d214;border-left-color:var(--primary-color)}.file-tree-item.folder{color:var(--text-secondary)}.file-tree-item[draggable=true]{cursor:grab}.file-tree-item[draggable=true]:active{cursor:grabbing}.file-tree-item.dragging{opacity:.5;background:var(--background)}.file-tree-item.drag-over{background:#1976d226;border-left-color:var(--primary-color);border-left-width:3px}.file-tree-item.drag-over:after{content:"";position:absolute;left:0;right:0;top:0;bottom:0;border:2px dashed var(--primary-color);border-radius:4px;pointer-events:none}.file-tree-icon{font-size:14px;flex-shrink:0}.file-tree-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.file-tree-badge{font-size:10px;font-weight:600;padding:2px 6px;border-radius:10px;background:var(--primary-color);color:#fff;flex-shrink:0}.file-tree-failed-indicator{color:#ef4444;font-size:10px;margin-left:4px;flex-shrink:0}.folder-hooks-btn{background:none;border:none;color:var(--text-secondary);cursor:pointer;font-size:12px;padding:2px 4px;opacity:0;transition:opacity .2s;flex-shrink:0}.file-tree-item.folder:hover .folder-hooks-btn{opacity:1}.folder-hooks-btn:hover{color:var(--primary-color)}.folder-hooks-indicator{font-size:10px;color:var(--primary-color);margin-left:4px;flex-shrink:0}.header-version{margin-left:8px;font-size:11px;font-weight:400;color:#ffffff80;background:#ffffff1a;padding:2px 6px;border-radius:4px}.header-file-path{margin-left:12px;font-size:12px;font-weight:400;color:#ffffffb3;max-width:400px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-flex;align-items:center;gap:8px}.auto-save-indicator{font-size:11px;padding:2px 8px;border-radius:4px;flex-shrink:0}.auto-save-indicator.saving{background:#ffc10733;color:#ffc107;animation:pulse 1s ease-in-out infinite}.auto-save-indicator.saved{background:#4caf5033;color:#4caf50}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.folder-hooks-label{background:rgba(var(--ts-blue),.1);background:#e0f2fe;color:var(--ts-blue);padding:2px 8px;border-radius:4px;font-size:11px;font-weight:500;margin-right:6px}.help-modal{width:950px;max-width:95vw;height:85vh;max-height:85vh;display:flex;flex-direction:column;padding:0;overflow:hidden}.help-header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border-color);flex-shrink:0}.help-header h2{margin:0;font-size:20px;font-weight:500}.btn-close{background:none;border:none;font-size:24px;cursor:pointer;color:var(--text-secondary);padding:4px 8px;border-radius:4px;line-height:1}.btn-close:hover{background:var(--background);color:var(--text-primary)}.help-layout{display:flex;flex:1;overflow:hidden}.help-nav{width:200px;flex-shrink:0;background:var(--background);border-right:1px solid var(--border-color);padding:12px 0;overflow-y:auto}.help-nav-item{display:block;width:100%;padding:10px 20px;border:none;background:none;text-align:left;cursor:pointer;font-size:14px;color:var(--text-secondary);transition:background .2s,color .2s}.help-nav-item:hover{background:#0000000a;color:var(--text-primary)}.help-nav-item.active{background:#1976d21a;color:var(--primary-color);font-weight:500;border-left:3px solid var(--primary-color);padding-left:17px}.help-content{flex:1;overflow-y:auto;padding:24px 32px}.help-content h3{margin:0 0 8px;font-size:22px;font-weight:600;color:var(--text-primary)}.help-content>p{margin:0 0 24px;color:var(--text-secondary);font-size:15px;line-height:1.5}.help-feature{margin-bottom:24px;padding:16px 20px;background:var(--background);border-radius:8px}.help-feature h4{margin:0 0 12px;font-size:15px;font-weight:600;color:var(--text-primary)}.help-feature ol,.help-feature ul{margin:0;padding-left:20px}.help-feature li{margin-bottom:8px;line-height:1.5;color:var(--text-primary)}.help-feature li:last-child{margin-bottom:0}.help-feature p{margin:0 0 12px;line-height:1.5}.help-feature code{background:#0000000f;padding:2px 6px;border-radius:4px;font-family:SF Mono,Monaco,Courier New,monospace;font-size:13px;color:var(--primary-dark)}.help-tip{margin-bottom:24px;padding:14px 18px;background:#1976d214;border-left:3px solid var(--primary-color);border-radius:0 8px 8px 0;font-size:14px;line-height:1.5}.help-tip code{background:#0000000f;padding:2px 6px;border-radius:4px;font-family:SF Mono,Monaco,Courier New,monospace;font-size:13px}.help-code{margin:12px 0 0;padding:12px 16px;background:#1e1e1e;color:#d4d4d4;border-radius:6px;font-family:SF Mono,Monaco,Courier New,monospace;font-size:13px;line-height:1.5;overflow-x:auto;white-space:pre}.scan-project-btn{margin-top:8px}.scan-project-btn:disabled{opacity:.6;cursor:not-allowed}.matches-section{margin-top:12px}.matches-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;font-size:13px;color:var(--text-secondary)}.matches-actions{display:flex;gap:12px}.btn-link{background:none;border:none;color:var(--primary-color);cursor:pointer;font-size:12px;padding:0}.btn-link:hover{text-decoration:underline}.matches-list{max-height:200px;overflow-y:auto;border:1px solid var(--border-color);border-radius:6px;background:var(--surface)}.match-file-group{border-bottom:1px solid var(--border-color)}.match-file-group:last-child{border-bottom:none}.match-file-name{font-size:12px;font-weight:600;color:var(--text-primary);padding:8px 12px 4px;background:var(--background)}.match-item{display:flex;align-items:center;gap:8px;padding:6px 12px;cursor:pointer;transition:background .15s}.match-item:hover{background:#00000008}.match-item input[type=checkbox]{flex-shrink:0}.match-location{font-size:13px;color:var(--text-primary);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.match-detail{font-size:12px;color:var(--text-secondary);margin-left:4px}.record-dialog{min-width:500px;max-width:600px}.record-dialog .modal-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;padding-bottom:12px;border-bottom:1px solid var(--border-color)}.record-dialog .modal-header h2{margin:0;font-size:18px}.record-dialog .modal-body{min-height:150px}.record-url-input{display:flex;flex-direction:column;gap:12px}.record-url-input p{margin:0;color:var(--text-primary)}.url-input{width:100%;padding:10px 12px;border:1px solid var(--border-color);border-radius:4px;font-size:14px}.url-input:focus{outline:none;border-color:var(--primary-color)}.record-hint{font-size:13px;color:var(--text-secondary);margin-top:4px}.record-status{display:flex;align-items:center;gap:16px;padding:20px;background:var(--background);border-radius:8px;margin:16px 0}.record-status.recording{border-left:4px solid var(--error);background:#d32f2f0d}.record-status.processing{border-left:4px solid var(--warning);background:#f57c000d;justify-content:center}.recording-indicator{width:16px;height:16px;background:var(--error);border-radius:50%;animation:pulse 1.5s ease-in-out infinite;flex-shrink:0}@keyframes pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.6;transform:scale(.9)}}.recording-info h3{margin:0 0 8px;font-size:16px;color:var(--text-primary)}.recording-info p{margin:0;font-size:13px;color:var(--text-secondary)}.processing-spinner{width:24px;height:24px;border:3px solid var(--border-color);border-top-color:var(--primary-color);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.record-preview h3{margin:0 0 12px;font-size:15px}.steps-preview{max-height:300px;overflow-y:auto;border:1px solid var(--border-color);border-radius:6px;background:var(--surface)}.step-preview-item{display:flex;align-items:center;gap:10px;padding:10px 12px;border-bottom:1px solid var(--border-color);font-size:13px}.step-preview-item:last-child{border-bottom:none}.step-number{font-weight:600;color:var(--text-secondary);min-width:24px}.step-description{color:var(--text-primary);font-family:Monaco,Menlo,Ubuntu Mono,monospace;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.record-empty{text-align:center;padding:24px;color:var(--text-secondary)}.record-empty p{margin:0 0 8px}.record-error{color:var(--error);font-size:13px;padding:8px 12px;background:#d32f2f1a;border-radius:4px}.record-error-state{text-align:center;padding:20px}.record-error-state h3{margin:0 0 12px;color:var(--error)}.record-error-state .record-error{margin-bottom:16px}.btn-warning{background:var(--warning);color:#fff}.btn-warning:hover:not(:disabled){background:#e65100}.advanced-options{margin-top:16px;border-top:1px solid var(--border-color);padding-top:12px}.advanced-toggle{display:flex;align-items:center;gap:8px;background:none;border:none;padding:8px 0;font-size:13px;color:var(--text-secondary);cursor:pointer;width:100%;text-align:left}.advanced-toggle:hover{color:var(--text-primary)}.advanced-toggle .toggle-icon{font-size:10px;width:12px}.advanced-content{padding:12px 0 4px 20px}.option-row{display:flex;align-items:center;gap:12px;margin-bottom:8px}.option-row label{font-size:13px;color:var(--text-primary);white-space:nowrap;min-width:120px}.option-input{flex:1;padding:6px 10px;border:1px solid var(--border-color);border-radius:4px;font-size:13px;font-family:Monaco,Menlo,Ubuntu Mono,monospace}.option-input:focus{outline:none;border-color:var(--primary-color)}.option-hint{font-size:12px;color:var(--text-secondary);margin:4px 0 0}.json-editor-modal{width:600px;max-width:90vw;min-height:400px;max-height:80vh;display:flex;flex-direction:column;padding:0}.json-editor-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid var(--border-color)}.json-editor-header h3{margin:0;font-size:16px;font-weight:600}.json-editor-toolbar{display:flex;align-items:center;gap:8px;padding:12px 20px;background:var(--background);border-bottom:1px solid var(--border-color)}.json-editor-toolbar .btn-small{padding:4px 12px;font-size:12px}.json-status{margin-left:auto;font-size:12px;font-weight:500;padding:4px 8px;border-radius:4px}.json-status.valid{color:var(--success);background:var(--success-light)}.json-status.invalid{color:var(--error);background:#fef2f2}.json-editor-content{flex:1;display:flex;flex-direction:column;padding:16px 20px;min-height:200px}.json-textarea{flex:1;width:100%;min-height:250px;padding:12px;border:1px solid var(--border-color);border-radius:4px;font-family:Monaco,Menlo,Ubuntu Mono,Consolas,monospace;font-size:13px;line-height:1.5;resize:vertical;background:#fafafa}.json-textarea:focus{outline:none;border-color:var(--primary-color);background:#fff}.json-textarea.has-error{border-color:var(--error);background:#fef8f8}.json-error{margin-top:8px;padding:8px 12px;background:#fef2f2;border:1px solid #fecaca;border-radius:4px;color:var(--error);font-size:12px;font-family:monospace}.json-editor-footer{display:flex;justify-content:flex-end;gap:8px;padding:16px 20px;border-top:1px solid var(--border-color);background:var(--background)}.json-edit-button{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;margin-left:4px;background:var(--primary-light);border:1px solid var(--primary-color);border-radius:3px;color:var(--primary-color);font-size:11px;cursor:pointer;transition:background .2s}.json-edit-button:hover{background:var(--primary-color);color:#fff}.reopen-folder-prompt{display:flex;flex-direction:column;gap:8px;padding:12px;margin:8px;background:var(--primary-light);border:1px solid var(--primary-color);border-radius:6px;font-size:13px}.reopen-folder-prompt span{color:var(--text-secondary)}.reopen-folder-prompt strong{color:var(--text-primary);word-break:break-all}.reopen-folder-prompt .btn{align-self:flex-start}.toast-container{position:fixed;top:16px;right:16px;z-index:10000;display:flex;flex-direction:column;gap:8px;max-width:400px}.toast{display:flex;align-items:center;gap:10px;padding:12px 16px;border-radius:6px;background:#fff;box-shadow:0 4px 12px #00000026;animation:toast-slide-in .3s ease}@keyframes toast-slide-in{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}.toast-icon{flex-shrink:0;width:20px;height:20px;display:flex;align-items:center;justify-content:center;border-radius:50%;font-size:12px;font-weight:700}.toast-success .toast-icon{background:var(--success-light);color:var(--success)}.toast-error .toast-icon{background:#ffebee;color:var(--error)}.toast-warning .toast-icon{background:#fff3e0;color:var(--warning)}.toast-info .toast-icon{background:var(--primary-light);color:var(--primary-color)}.toast-message{flex:1;font-size:13px;color:var(--text-primary)}.toast-dismiss{flex-shrink:0;background:none;border:none;font-size:18px;color:var(--text-secondary);cursor:pointer;padding:0;line-height:1}.toast-dismiss:hover{color:var(--text-primary)}.variables-editor{display:flex;flex-direction:column;gap:8px}.variable-editor-item{display:flex;align-items:flex-start;gap:8px;padding:8px;background:var(--background);border-radius:4px}.variable-editor-item .var-name{flex:0 0 120px;position:relative}.variable-editor-item .var-value{flex:1;min-width:0}.variable-editor-item input,.variable-editor-item textarea{width:100%;padding:6px 8px;border:1px solid var(--border-color);border-radius:4px;font-size:13px;font-family:monospace}.variable-editor-item input:focus,.variable-editor-item textarea:focus{outline:none;border-color:var(--primary-color)}.variable-editor-item .var-actions{display:flex;gap:4px}.variable-editor-item .btn-icon{padding:4px 8px;background:none;border:1px solid var(--border-color);border-radius:4px;cursor:pointer;font-size:12px}.variable-editor-item .btn-icon:hover{background:var(--border-color)}.variable-editor-item .btn-icon.delete:hover{background:#ffebee;border-color:var(--error);color:var(--error)}.variable-editor-item.duplicate{background:#fff3e0}.variable-editor-item .duplicate-warning{position:absolute;right:6px;top:50%;transform:translateY(-50%);color:#f57c00;font-size:14px;cursor:help}.add-variable-btn{align-self:flex-start;padding:6px 12px;background:var(--primary-light);border:1px dashed var(--primary-color);border-radius:4px;color:var(--primary-color);font-size:13px;cursor:pointer}.add-variable-btn:hover{background:var(--primary-color);color:#fff;border-style:solid}.variables-section-header{display:flex;align-items:center;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--border-color);margin-bottom:8px}.variables-section-header h4{margin:0;font-size:13px;font-weight:600;color:var(--text-primary)}.variables-empty{padding:16px;text-align:center;color:var(--text-secondary);font-size:13px;background:var(--background);border-radius:4px}.context-menu{position:fixed;background:#fff;border:1px solid var(--border-color);border-radius:6px;box-shadow:0 4px 12px #00000026;min-width:180px;z-index:10000;padding:4px 0}.context-menu-item{display:block;width:100%;padding:8px 12px;border:none;background:none;text-align:left;font-size:13px;color:var(--text-primary);cursor:pointer}.context-menu-item:hover:not(.disabled){background:var(--primary-light);color:var(--primary-color)}.context-menu-item.disabled{color:var(--text-secondary);cursor:not-allowed}.create-variable-dialog{background:var(--surface);border-radius:8px;min-width:400px;max-width:500px;box-shadow:0 4px 20px #00000026}.create-variable-dialog .modal-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid var(--border-color)}.create-variable-dialog .modal-header h2{margin:0;font-size:16px;font-weight:600}.create-variable-dialog .modal-close{background:none;border:none;font-size:24px;color:var(--text-secondary);cursor:pointer;padding:0;line-height:1}.create-variable-dialog .modal-close:hover{color:var(--text-primary)}.create-variable-dialog .modal-body{padding:16px 20px}.create-variable-dialog .form-group{margin-bottom:16px}.create-variable-dialog .form-group:last-child{margin-bottom:0}.create-variable-dialog .form-group label{display:block;font-size:13px;font-weight:500;margin-bottom:6px;color:var(--text-primary)}.create-variable-dialog .form-group code{background:var(--background);padding:2px 6px;border-radius:3px;font-size:12px}.create-variable-dialog .field-values-list{display:flex;flex-direction:column;gap:6px}.create-variable-dialog .field-value-option{display:flex;align-items:center;gap:8px;padding:8px 10px;border:1px solid var(--border-color);border-radius:4px;cursor:pointer;font-size:13px}.create-variable-dialog .field-value-option:hover{background:var(--background)}.create-variable-dialog .field-value-option input[type=radio]{margin:0}.create-variable-dialog .field-label{font-weight:500;color:var(--text-secondary);flex-shrink:0}.create-variable-dialog .field-value{color:var(--primary-color);font-family:monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.create-variable-dialog .single-field-value{display:flex;gap:8px;padding:8px 10px;background:var(--background);border-radius:4px;font-size:13px}.create-variable-dialog .variable-type-options{display:flex;flex-direction:column;gap:8px}.create-variable-dialog .type-option{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:10px 12px;border:1px solid var(--border-color);border-radius:4px;cursor:pointer}.create-variable-dialog .type-option:hover{background:var(--background)}.create-variable-dialog .type-option input[type=radio]{margin:0}.create-variable-dialog .type-option span:nth-child(2){font-weight:500}.create-variable-dialog .type-hint{width:100%;margin-left:22px;font-size:12px;color:var(--text-secondary)}.create-variable-dialog .form-group input[type=text]{width:100%;padding:8px 10px;border:1px solid var(--border-color);border-radius:4px;font-size:14px}.create-variable-dialog .form-group input[type=text]:focus{outline:none;border-color:var(--primary-color);box-shadow:0 0 0 2px var(--primary-light)}.create-variable-dialog .variable-preview{margin-top:6px;font-size:12px;color:var(--text-secondary)}.create-variable-dialog .variable-preview code{background:var(--success-light);color:var(--success)}.create-variable-dialog .modal-footer{display:flex;justify-content:flex-end;gap:8px;padding:16px 20px;border-top:1px solid var(--border-color)}.prompt-dialog{min-width:400px;max-width:450px}.prompt-dialog .modal-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:16px;margin-bottom:16px;border-bottom:1px solid var(--border-color)}.prompt-dialog .modal-header h2{margin:0;font-size:18px}.prompt-dialog .modal-body{padding:0}.prompt-field{margin-bottom:16px}.prompt-field:last-child{margin-bottom:0}.prompt-field label{display:block;margin-bottom:6px;font-weight:500;color:var(--text-primary)}.prompt-field input[type=text]{width:100%;padding:10px 12px;border:1px solid var(--border-color);border-radius:4px;font-size:14px;background:var(--bg-primary);color:var(--text-primary);box-sizing:border-box}.prompt-field input[type=text]:focus{outline:none;border-color:var(--primary-color);box-shadow:0 0 0 2px var(--primary-light)}.openapi-dialog{min-width:600px;max-width:800px;max-height:85vh}.openapi-dialog .modal-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;padding-bottom:12px;border-bottom:1px solid var(--border-color)}.openapi-dialog .modal-header h2{margin:0;font-size:18px}.openapi-dialog .modal-body{min-height:300px;max-height:55vh;overflow-y:auto}.openapi-input-stage{display:flex;flex-direction:column;gap:16px}.input-mode-tabs{display:flex;border-bottom:1px solid var(--border-color);margin-bottom:8px}.input-mode-tabs .tab{padding:10px 20px;border:none;background:none;color:var(--text-secondary);font-size:14px;font-weight:500;cursor:pointer;border-bottom:2px solid transparent;transition:all .2s}.input-mode-tabs .tab:hover{color:var(--text-primary)}.input-mode-tabs .tab.active{color:var(--primary-color);border-bottom-color:var(--primary-color)}.url-input-section,.file-input-section{display:flex;flex-direction:column;gap:12px}.url-input-section label,.file-input-section label{font-weight:500;color:var(--text-primary)}.file-select-btn{align-self:flex-start;background:var(--background)!important;color:var(--text-primary)!important;border:1px solid var(--border-color)!important}.file-select-btn:hover{background:var(--border-color)!important}.file-selected{color:var(--success);font-size:13px}.input-hint{font-size:13px;color:var(--text-secondary);margin:0}.openapi-error{color:var(--error);font-size:13px;padding:10px 14px;background:#d32f2f14;border-radius:4px;border-left:3px solid var(--error)}.openapi-loading-stage{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:40px 20px}.openapi-loading-stage p{color:var(--text-secondary);font-size:14px}.openapi-selection-stage{display:flex;flex-direction:column;gap:16px}.spec-info{background:var(--background);padding:12px 16px;border-radius:6px}.spec-info h3{margin:0;font-size:16px;color:var(--text-primary)}.spec-info .version{font-weight:400;font-size:13px;color:var(--text-secondary);margin-left:8px}.spec-description{margin:8px 0 0;font-size:13px;color:var(--text-secondary);line-height:1.4}.endpoint-filters{display:flex;flex-direction:column;gap:10px}.filter-row{display:flex;gap:8px}.search-input{flex:1;padding:8px 12px;border:1px solid var(--border-color);border-radius:4px;font-size:13px}.search-input:focus{outline:none;border-color:var(--primary-color)}.filter-select{padding:8px 12px;border:1px solid var(--border-color);border-radius:4px;font-size:13px;background:#fff;min-width:120px}.filter-select:focus{outline:none;border-color:var(--primary-color)}.selection-actions{display:flex;align-items:center;gap:12px}.btn-small{padding:6px 12px;font-size:12px;background:var(--background);border:1px solid var(--border-color);border-radius:4px;color:var(--text-primary);cursor:pointer}.btn-small:hover{background:var(--border-color)}.selection-count{margin-left:auto;font-size:13px;color:var(--text-secondary)}.endpoint-list{max-height:300px;overflow-y:auto;border:1px solid var(--border-color);border-radius:6px;background:#fff}.endpoint-item{display:flex;align-items:center;gap:10px;padding:10px 12px;border-bottom:1px solid var(--border-color);cursor:pointer;transition:background .15s}.endpoint-item:last-child{border-bottom:none}.endpoint-item:hover{background:var(--background)}.endpoint-item.selected{background:#1976d214}.endpoint-item.deprecated{opacity:.6}.endpoint-item input[type=checkbox]{flex-shrink:0}.method-badge{padding:3px 8px;border-radius:4px;font-size:11px;font-weight:600;color:#fff;text-transform:uppercase;flex-shrink:0;min-width:55px;text-align:center}.endpoint-path{font-family:monospace;font-size:13px;color:var(--text-primary);flex-shrink:0}.endpoint-summary{font-size:12px;color:var(--text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}.deprecated-badge{font-size:10px;background:var(--warning);color:#fff;padding:2px 6px;border-radius:3px;flex-shrink:0}.endpoint-tags{display:flex;gap:4px;flex-shrink:0}.tag-badge{font-size:10px;background:var(--primary-light);color:var(--primary-color);padding:2px 6px;border-radius:3px}.no-endpoints{padding:24px;text-align:center;color:var(--text-secondary);font-size:14px}.openapi-options-stage{display:flex;flex-direction:column;gap:20px}.openapi-options-stage h3{margin:0 0 4px;font-size:16px}.option-group{display:flex;flex-direction:column;gap:8px}.option-group>label{font-weight:500;color:var(--text-primary);font-size:14px}.option-group .option-input{padding:10px 12px;border:1px solid var(--border-color);border-radius:4px;font-size:14px}.option-group .option-input:focus{outline:none;border-color:var(--primary-color)}.option-group .option-hint{font-size:12px;color:var(--text-secondary);margin:0}.radio-group{display:flex;flex-direction:column;gap:8px}.radio-option{display:flex;align-items:center;gap:10px;padding:10px 12px;border:1px solid var(--border-color);border-radius:4px;cursor:pointer;transition:all .15s}.radio-option:hover{background:var(--background)}.radio-option input[type=radio]{margin:0}.radio-option span:first-of-type{font-weight:500}.radio-option .option-desc{font-size:12px;color:var(--text-secondary);margin-left:auto}.checkbox-group .checkbox-option{display:flex;align-items:center;gap:10px;cursor:pointer}.checkbox-group .checkbox-option input[type=checkbox]{margin:0}.openapi-preview-stage{display:flex;flex-direction:column;gap:16px}.openapi-preview-stage h3{margin:0;font-size:16px}.preview-summary{font-size:14px;color:var(--text-secondary);margin:0}.generated-files-list{display:flex;flex-direction:column;gap:12px}.generated-file-item{border:1px solid var(--border-color);border-radius:6px;overflow:hidden}.generated-file-item .file-header{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;background:var(--background);border-bottom:1px solid var(--border-color)}.generated-file-item .file-name{font-weight:600;font-size:13px;color:var(--text-primary);font-family:monospace}.generated-file-item .test-count{font-size:12px;color:var(--text-secondary)}.generated-file-item .file-tests{padding:8px 14px;max-height:150px;overflow-y:auto}.preview-test{padding:6px 0;font-size:13px;color:var(--text-primary);border-bottom:1px solid var(--border-color)}.preview-test:last-child{border-bottom:none}.more-tests{padding:8px 0;font-size:12px;color:var(--text-secondary);font-style:italic}.openapi-error-stage{display:flex;flex-direction:column;align-items:center;gap:16px;padding:20px}.openapi-error-stage h3{margin:0;color:var(--error)}.openapi-warning{color:var(--warning);font-size:13px;padding:10px 14px;background:#e67e001a;border-radius:4px;border-left:3px solid var(--warning);margin-bottom:16px}.welcome-screen{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:40px;text-align:center;background:linear-gradient(135deg,var(--background) 0%,#e8f4fc 100%)}.welcome-content{max-width:500px}.welcome-screen h2{font-size:28px;color:var(--primary-color);margin-bottom:12px}.welcome-screen>.welcome-content>p{color:var(--text-secondary);font-size:16px;margin-bottom:24px}.reopen-section{background:var(--surface);border:2px solid var(--primary-color);border-radius:12px;padding:24px;margin-bottom:24px;box-shadow:0 4px 12px #205d9626}.reopen-section .reopen-message{font-size:14px;color:var(--text-secondary);margin-bottom:16px}.reopen-btn{font-size:16px!important;padding:14px 28px!important}.welcome-actions{display:flex;gap:16px;justify-content:center;margin-bottom:32px}.btn-large{padding:12px 24px!important;font-size:15px!important}.welcome-tip{font-size:13px;color:var(--text-secondary);background:var(--surface);padding:16px;border-radius:8px;border:1px solid var(--border-color)}.welcome-tip code{background:var(--background);padding:2px 6px;border-radius:4px;font-family:SF Mono,Monaco,Cascadia Code,monospace;font-size:12px}