executable-stories-formatters 0.7.15 → 0.8.0

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/dist/index.cjs CHANGED
@@ -1434,11 +1434,53 @@ function initKeyboardShortcuts() {
1434
1434
  });
1435
1435
  }
1436
1436
 
1437
- // Collapse/expand functionality
1437
+ // Collapse/expand functionality (persisted in localStorage)
1438
+ var COLLAPSE_KEY = 'es-collapsed-ids';
1439
+
1440
+ function loadCollapseState() {
1441
+ try {
1442
+ var raw = localStorage.getItem(COLLAPSE_KEY);
1443
+ return raw ? new Set(JSON.parse(raw)) : new Set();
1444
+ } catch (e) {
1445
+ return new Set();
1446
+ }
1447
+ }
1448
+
1449
+ function saveCollapseState(set) {
1450
+ try {
1451
+ localStorage.setItem(COLLAPSE_KEY, JSON.stringify(Array.from(set)));
1452
+ } catch (e) { /* quota or disabled */ }
1453
+ }
1454
+
1455
+ function persistCollapse(container) {
1456
+ if (!container || !container.id) return;
1457
+ var state = loadCollapseState();
1458
+ if (container.classList.contains('collapsed')) {
1459
+ state.add(container.id);
1460
+ } else {
1461
+ state.delete(container.id);
1462
+ }
1463
+ saveCollapseState(state);
1464
+ }
1465
+
1438
1466
  function toggleCollapse(header, container) {
1439
1467
  container?.classList.toggle('collapsed');
1440
1468
  const isCollapsed = container?.classList.contains('collapsed');
1441
1469
  header.setAttribute('aria-expanded', !isCollapsed);
1470
+ persistCollapse(container);
1471
+ }
1472
+
1473
+ function restoreCollapseState() {
1474
+ var state = loadCollapseState();
1475
+ if (state.size === 0) return;
1476
+ state.forEach(function(id) {
1477
+ var el = document.getElementById(id);
1478
+ if (!el) return;
1479
+ if (!el.classList.contains('feature') && !el.classList.contains('scenario')) return;
1480
+ el.classList.add('collapsed');
1481
+ var header = el.querySelector('.feature-header, .scenario-header');
1482
+ if (header) header.setAttribute('aria-expanded', 'false');
1483
+ });
1442
1484
  }
1443
1485
 
1444
1486
  function initCollapse() {
@@ -1485,14 +1527,20 @@ function expandAll() {
1485
1527
  const header = el.querySelector('.feature-header, .scenario-header, .trace-view-header');
1486
1528
  header?.setAttribute('aria-expanded', 'true');
1487
1529
  });
1530
+ saveCollapseState(new Set());
1488
1531
  }
1489
1532
 
1490
1533
  function collapseAll() {
1534
+ var ids = new Set();
1491
1535
  document.querySelectorAll('.feature, .scenario, .trace-view').forEach(el => {
1492
1536
  el.classList.add('collapsed');
1493
1537
  const header = el.querySelector('.feature-header, .scenario-header, .trace-view-header');
1494
1538
  header?.setAttribute('aria-expanded', 'false');
1539
+ if (el.id && (el.classList.contains('feature') || el.classList.contains('scenario'))) {
1540
+ ids.add(el.id);
1541
+ }
1495
1542
  });
1543
+ saveCollapseState(ids);
1496
1544
  }
1497
1545
 
1498
1546
  // Detail level toggle
@@ -1619,6 +1667,68 @@ function copyScenarioAsMarkdown(scenarioId) {
1619
1667
  });
1620
1668
  }
1621
1669
 
1670
+ // Copy scenario as Claude-ready prompt (failure investigation context)
1671
+ function copyScenarioAsPrompt(scenarioId) {
1672
+ var scenario = document.getElementById(scenarioId);
1673
+ if (!scenario) return;
1674
+
1675
+ var feature = scenario.closest('.feature');
1676
+ var featureTitle = feature ? (feature.querySelector('.feature-title') || {}).textContent || '' : '';
1677
+ var title = (scenario.querySelector('.scenario-name') || {}).textContent || '';
1678
+ var statusEl = scenario.querySelector('.status-icon');
1679
+ var status = statusEl && statusEl.classList.contains('status-passed') ? 'passed' :
1680
+ statusEl && statusEl.classList.contains('status-failed') ? 'failed' :
1681
+ statusEl && statusEl.classList.contains('status-skipped') ? 'skipped' : 'pending';
1682
+ var sourceLink = scenario.querySelector('.source-link');
1683
+ var source = sourceLink ? sourceLink.textContent || '' : '';
1684
+ var tags = Array.from(scenario.querySelectorAll('.scenario-meta .tag')).map(function(t) { return t.textContent.trim(); });
1685
+ var steps = scenario.querySelectorAll('.step, .step.continuation');
1686
+
1687
+ var lines = [];
1688
+ lines.push('I need help investigating a failing executable-story scenario.');
1689
+ lines.push('');
1690
+ if (featureTitle.trim()) lines.push('Feature: ' + featureTitle.trim());
1691
+ lines.push('Scenario: ' + title.trim());
1692
+ lines.push('Status: ' + status);
1693
+ if (source.trim()) lines.push('Source: ' + source.trim());
1694
+ if (tags.length > 0) lines.push('Tags: ' + tags.join(', '));
1695
+ lines.push('');
1696
+ lines.push('Steps:');
1697
+ steps.forEach(function(step) {
1698
+ var keyword = step.getAttribute('data-keyword') || '';
1699
+ var text = step.getAttribute('data-text') || '';
1700
+ var stepStatusEl = step.querySelector('.step-status');
1701
+ var marker = ' ';
1702
+ if (stepStatusEl) {
1703
+ if (stepStatusEl.classList.contains('status-failed')) marker = 'x ';
1704
+ else if (stepStatusEl.classList.contains('status-passed')) marker = '+ ';
1705
+ else if (stepStatusEl.classList.contains('status-skipped')) marker = '- ';
1706
+ }
1707
+ lines.push(marker + keyword + ' ' + text);
1708
+ });
1709
+
1710
+ var errorBox = scenario.querySelector('.error-message');
1711
+ if (errorBox) {
1712
+ lines.push('');
1713
+ lines.push('Error:');
1714
+ lines.push((errorBox.textContent || '').trim());
1715
+ }
1716
+ var stackBox = scenario.querySelector('.error-stack');
1717
+ if (stackBox) {
1718
+ lines.push('');
1719
+ lines.push('Stack:');
1720
+ lines.push((stackBox.textContent || '').trim());
1721
+ }
1722
+
1723
+ lines.push('');
1724
+ lines.push('Please read the source file, identify the root cause, and propose a fix.');
1725
+
1726
+ var text = lines.join('\\n');
1727
+ navigator.clipboard.writeText(text).then(function() {
1728
+ showCopyToast(scenario);
1729
+ });
1730
+ }
1731
+
1622
1732
  // Hash scroll on load
1623
1733
  function initHashScroll() {
1624
1734
  if (!location.hash) return;
@@ -1788,6 +1898,7 @@ function generateScript(options) {
1788
1898
  initCalls.push("initStatusFilter();");
1789
1899
  initCalls.push("initKeyboardShortcuts();");
1790
1900
  initCalls.push("initCollapse();");
1901
+ initCalls.push("restoreCollapseState();");
1791
1902
  initCalls.push("initDetailLevel();");
1792
1903
  initCalls.push("applyAllFilters();");
1793
1904
  initCalls.push("initHashScroll();");
@@ -3895,6 +4006,33 @@ body {
3895
4006
  color: var(--primary);
3896
4007
  }
3897
4008
 
4009
+ .copy-prompt-btn {
4010
+ display: inline-flex;
4011
+ align-items: center;
4012
+ justify-content: center;
4013
+ width: 1.5rem;
4014
+ height: 1.5rem;
4015
+ border: none;
4016
+ background: none;
4017
+ color: var(--muted-foreground);
4018
+ cursor: pointer;
4019
+ opacity: 0.6;
4020
+ transition: opacity 0.15s ease, transform 0.15s ease;
4021
+ font-size: 0.95rem;
4022
+ padding: 0;
4023
+ flex-shrink: 0;
4024
+ }
4025
+
4026
+ .scenario-header:hover .copy-prompt-btn,
4027
+ .copy-prompt-btn:focus-visible {
4028
+ opacity: 1;
4029
+ }
4030
+
4031
+ .copy-prompt-btn:hover {
4032
+ color: var(--primary);
4033
+ transform: scale(1.15);
4034
+ }
4035
+
3898
4036
  /* ============================================================================
3899
4037
  Keyboard Navigation
3900
4038
  ============================================================================ */
@@ -4132,6 +4270,82 @@ a.toc-title:hover {
4132
4270
  outline-offset: 2px;
4133
4271
  }
4134
4272
 
4273
+ /* ============================================================================
4274
+ Mobile responsive refinements
4275
+ ============================================================================ */
4276
+ @media (max-width: 640px) {
4277
+ .container {
4278
+ padding: 0.875rem;
4279
+ }
4280
+
4281
+ .header {
4282
+ flex-direction: column;
4283
+ align-items: stretch;
4284
+ gap: 0.75rem;
4285
+ }
4286
+
4287
+ .header-actions {
4288
+ flex-wrap: wrap;
4289
+ gap: 0.5rem;
4290
+ }
4291
+
4292
+ .search-input {
4293
+ width: 100%;
4294
+ flex: 1 1 100%;
4295
+ min-width: 0;
4296
+ }
4297
+
4298
+ .header h1 {
4299
+ font-size: 1.25rem;
4300
+ }
4301
+
4302
+ .scenario-header,
4303
+ .feature-header {
4304
+ flex-wrap: wrap;
4305
+ gap: 0.5rem;
4306
+ }
4307
+
4308
+ .scenario-meta {
4309
+ flex-wrap: wrap;
4310
+ }
4311
+
4312
+ .scenario-actions {
4313
+ flex-wrap: wrap;
4314
+ }
4315
+
4316
+ /* Always-visible action buttons on touch (no hover) */
4317
+ .copy-scenario-btn,
4318
+ .copy-prompt-btn,
4319
+ .permalink-anchor {
4320
+ opacity: 1 !important;
4321
+ }
4322
+
4323
+ .summary-card {
4324
+ padding: 0.75rem 0.875rem;
4325
+ }
4326
+
4327
+ .summary-card .value {
4328
+ font-size: 1.5rem;
4329
+ }
4330
+
4331
+ .tag-bar {
4332
+ overflow-x: auto;
4333
+ -webkit-overflow-scrolling: touch;
4334
+ }
4335
+
4336
+ .shortcuts-overlay {
4337
+ padding: 1rem;
4338
+ }
4339
+ }
4340
+
4341
+ @media (hover: none) and (pointer: coarse) {
4342
+ .copy-scenario-btn,
4343
+ .copy-prompt-btn,
4344
+ .permalink-anchor {
4345
+ opacity: 1;
4346
+ }
4347
+ }
4348
+
4135
4349
  `;
4136
4350
 
4137
4351
  // src/formatters/html/themes/default.ts
@@ -13858,6 +14072,7 @@ function renderScenario(args, deps) {
13858
14072
  </div>
13859
14073
  <div class="scenario-actions">
13860
14074
  <button class="copy-scenario-btn" onclick="copyScenarioAsMarkdown('scenario-${tc.id}')" aria-label="Copy scenario as markdown" title="Copy as Markdown"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button>
14075
+ ${tc.status === "failed" ? `<button class="copy-prompt-btn" onclick="copyScenarioAsPrompt('scenario-${tc.id}')" aria-label="Copy as Claude prompt" title="Copy as AI prompt">\u2728</button>` : ""}
13861
14076
  <button class="permalink-anchor" onclick="copyPermalink('scenario-${tc.id}')" aria-label="Copy link to scenario" title="Copy link">#</button>
13862
14077
  <span class="scenario-duration">${duration}</span>
13863
14078
  </div>
@@ -19654,7 +19869,7 @@ var ReportGenerator = class {
19654
19869
  exclude: options.exclude ?? [],
19655
19870
  includeTags: options.includeTags ?? [],
19656
19871
  excludeTags: options.excludeTags ?? [],
19657
- formats: options.formats ?? ["cucumber-json"],
19872
+ formats: options.formats ?? ["html"],
19658
19873
  outputDir: options.outputDir ?? "reports",
19659
19874
  outputName: options.outputName ?? "index",
19660
19875
  outputNameTimestamp: options.outputNameTimestamp ?? false,