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/cli.js CHANGED
@@ -1997,11 +1997,53 @@ function initKeyboardShortcuts() {
1997
1997
  });
1998
1998
  }
1999
1999
 
2000
- // Collapse/expand functionality
2000
+ // Collapse/expand functionality (persisted in localStorage)
2001
+ var COLLAPSE_KEY = 'es-collapsed-ids';
2002
+
2003
+ function loadCollapseState() {
2004
+ try {
2005
+ var raw = localStorage.getItem(COLLAPSE_KEY);
2006
+ return raw ? new Set(JSON.parse(raw)) : new Set();
2007
+ } catch (e) {
2008
+ return new Set();
2009
+ }
2010
+ }
2011
+
2012
+ function saveCollapseState(set) {
2013
+ try {
2014
+ localStorage.setItem(COLLAPSE_KEY, JSON.stringify(Array.from(set)));
2015
+ } catch (e) { /* quota or disabled */ }
2016
+ }
2017
+
2018
+ function persistCollapse(container) {
2019
+ if (!container || !container.id) return;
2020
+ var state = loadCollapseState();
2021
+ if (container.classList.contains('collapsed')) {
2022
+ state.add(container.id);
2023
+ } else {
2024
+ state.delete(container.id);
2025
+ }
2026
+ saveCollapseState(state);
2027
+ }
2028
+
2001
2029
  function toggleCollapse(header, container) {
2002
2030
  container?.classList.toggle('collapsed');
2003
2031
  const isCollapsed = container?.classList.contains('collapsed');
2004
2032
  header.setAttribute('aria-expanded', !isCollapsed);
2033
+ persistCollapse(container);
2034
+ }
2035
+
2036
+ function restoreCollapseState() {
2037
+ var state = loadCollapseState();
2038
+ if (state.size === 0) return;
2039
+ state.forEach(function(id) {
2040
+ var el = document.getElementById(id);
2041
+ if (!el) return;
2042
+ if (!el.classList.contains('feature') && !el.classList.contains('scenario')) return;
2043
+ el.classList.add('collapsed');
2044
+ var header = el.querySelector('.feature-header, .scenario-header');
2045
+ if (header) header.setAttribute('aria-expanded', 'false');
2046
+ });
2005
2047
  }
2006
2048
 
2007
2049
  function initCollapse() {
@@ -2048,14 +2090,20 @@ function expandAll() {
2048
2090
  const header = el.querySelector('.feature-header, .scenario-header, .trace-view-header');
2049
2091
  header?.setAttribute('aria-expanded', 'true');
2050
2092
  });
2093
+ saveCollapseState(new Set());
2051
2094
  }
2052
2095
 
2053
2096
  function collapseAll() {
2097
+ var ids = new Set();
2054
2098
  document.querySelectorAll('.feature, .scenario, .trace-view').forEach(el => {
2055
2099
  el.classList.add('collapsed');
2056
2100
  const header = el.querySelector('.feature-header, .scenario-header, .trace-view-header');
2057
2101
  header?.setAttribute('aria-expanded', 'false');
2102
+ if (el.id && (el.classList.contains('feature') || el.classList.contains('scenario'))) {
2103
+ ids.add(el.id);
2104
+ }
2058
2105
  });
2106
+ saveCollapseState(ids);
2059
2107
  }
2060
2108
 
2061
2109
  // Detail level toggle
@@ -2182,6 +2230,68 @@ function copyScenarioAsMarkdown(scenarioId) {
2182
2230
  });
2183
2231
  }
2184
2232
 
2233
+ // Copy scenario as Claude-ready prompt (failure investigation context)
2234
+ function copyScenarioAsPrompt(scenarioId) {
2235
+ var scenario = document.getElementById(scenarioId);
2236
+ if (!scenario) return;
2237
+
2238
+ var feature = scenario.closest('.feature');
2239
+ var featureTitle = feature ? (feature.querySelector('.feature-title') || {}).textContent || '' : '';
2240
+ var title = (scenario.querySelector('.scenario-name') || {}).textContent || '';
2241
+ var statusEl = scenario.querySelector('.status-icon');
2242
+ var status = statusEl && statusEl.classList.contains('status-passed') ? 'passed' :
2243
+ statusEl && statusEl.classList.contains('status-failed') ? 'failed' :
2244
+ statusEl && statusEl.classList.contains('status-skipped') ? 'skipped' : 'pending';
2245
+ var sourceLink = scenario.querySelector('.source-link');
2246
+ var source = sourceLink ? sourceLink.textContent || '' : '';
2247
+ var tags = Array.from(scenario.querySelectorAll('.scenario-meta .tag')).map(function(t) { return t.textContent.trim(); });
2248
+ var steps = scenario.querySelectorAll('.step, .step.continuation');
2249
+
2250
+ var lines = [];
2251
+ lines.push('I need help investigating a failing executable-story scenario.');
2252
+ lines.push('');
2253
+ if (featureTitle.trim()) lines.push('Feature: ' + featureTitle.trim());
2254
+ lines.push('Scenario: ' + title.trim());
2255
+ lines.push('Status: ' + status);
2256
+ if (source.trim()) lines.push('Source: ' + source.trim());
2257
+ if (tags.length > 0) lines.push('Tags: ' + tags.join(', '));
2258
+ lines.push('');
2259
+ lines.push('Steps:');
2260
+ steps.forEach(function(step) {
2261
+ var keyword = step.getAttribute('data-keyword') || '';
2262
+ var text = step.getAttribute('data-text') || '';
2263
+ var stepStatusEl = step.querySelector('.step-status');
2264
+ var marker = ' ';
2265
+ if (stepStatusEl) {
2266
+ if (stepStatusEl.classList.contains('status-failed')) marker = 'x ';
2267
+ else if (stepStatusEl.classList.contains('status-passed')) marker = '+ ';
2268
+ else if (stepStatusEl.classList.contains('status-skipped')) marker = '- ';
2269
+ }
2270
+ lines.push(marker + keyword + ' ' + text);
2271
+ });
2272
+
2273
+ var errorBox = scenario.querySelector('.error-message');
2274
+ if (errorBox) {
2275
+ lines.push('');
2276
+ lines.push('Error:');
2277
+ lines.push((errorBox.textContent || '').trim());
2278
+ }
2279
+ var stackBox = scenario.querySelector('.error-stack');
2280
+ if (stackBox) {
2281
+ lines.push('');
2282
+ lines.push('Stack:');
2283
+ lines.push((stackBox.textContent || '').trim());
2284
+ }
2285
+
2286
+ lines.push('');
2287
+ lines.push('Please read the source file, identify the root cause, and propose a fix.');
2288
+
2289
+ var text = lines.join('\\n');
2290
+ navigator.clipboard.writeText(text).then(function() {
2291
+ showCopyToast(scenario);
2292
+ });
2293
+ }
2294
+
2185
2295
  // Hash scroll on load
2186
2296
  function initHashScroll() {
2187
2297
  if (!location.hash) return;
@@ -2351,6 +2461,7 @@ function generateScript(options) {
2351
2461
  initCalls.push("initStatusFilter();");
2352
2462
  initCalls.push("initKeyboardShortcuts();");
2353
2463
  initCalls.push("initCollapse();");
2464
+ initCalls.push("restoreCollapseState();");
2354
2465
  initCalls.push("initDetailLevel();");
2355
2466
  initCalls.push("applyAllFilters();");
2356
2467
  initCalls.push("initHashScroll();");
@@ -4432,6 +4543,33 @@ body {
4432
4543
  color: var(--primary);
4433
4544
  }
4434
4545
 
4546
+ .copy-prompt-btn {
4547
+ display: inline-flex;
4548
+ align-items: center;
4549
+ justify-content: center;
4550
+ width: 1.5rem;
4551
+ height: 1.5rem;
4552
+ border: none;
4553
+ background: none;
4554
+ color: var(--muted-foreground);
4555
+ cursor: pointer;
4556
+ opacity: 0.6;
4557
+ transition: opacity 0.15s ease, transform 0.15s ease;
4558
+ font-size: 0.95rem;
4559
+ padding: 0;
4560
+ flex-shrink: 0;
4561
+ }
4562
+
4563
+ .scenario-header:hover .copy-prompt-btn,
4564
+ .copy-prompt-btn:focus-visible {
4565
+ opacity: 1;
4566
+ }
4567
+
4568
+ .copy-prompt-btn:hover {
4569
+ color: var(--primary);
4570
+ transform: scale(1.15);
4571
+ }
4572
+
4435
4573
  /* ============================================================================
4436
4574
  Keyboard Navigation
4437
4575
  ============================================================================ */
@@ -4669,6 +4807,82 @@ a.toc-title:hover {
4669
4807
  outline-offset: 2px;
4670
4808
  }
4671
4809
 
4810
+ /* ============================================================================
4811
+ Mobile responsive refinements
4812
+ ============================================================================ */
4813
+ @media (max-width: 640px) {
4814
+ .container {
4815
+ padding: 0.875rem;
4816
+ }
4817
+
4818
+ .header {
4819
+ flex-direction: column;
4820
+ align-items: stretch;
4821
+ gap: 0.75rem;
4822
+ }
4823
+
4824
+ .header-actions {
4825
+ flex-wrap: wrap;
4826
+ gap: 0.5rem;
4827
+ }
4828
+
4829
+ .search-input {
4830
+ width: 100%;
4831
+ flex: 1 1 100%;
4832
+ min-width: 0;
4833
+ }
4834
+
4835
+ .header h1 {
4836
+ font-size: 1.25rem;
4837
+ }
4838
+
4839
+ .scenario-header,
4840
+ .feature-header {
4841
+ flex-wrap: wrap;
4842
+ gap: 0.5rem;
4843
+ }
4844
+
4845
+ .scenario-meta {
4846
+ flex-wrap: wrap;
4847
+ }
4848
+
4849
+ .scenario-actions {
4850
+ flex-wrap: wrap;
4851
+ }
4852
+
4853
+ /* Always-visible action buttons on touch (no hover) */
4854
+ .copy-scenario-btn,
4855
+ .copy-prompt-btn,
4856
+ .permalink-anchor {
4857
+ opacity: 1 !important;
4858
+ }
4859
+
4860
+ .summary-card {
4861
+ padding: 0.75rem 0.875rem;
4862
+ }
4863
+
4864
+ .summary-card .value {
4865
+ font-size: 1.5rem;
4866
+ }
4867
+
4868
+ .tag-bar {
4869
+ overflow-x: auto;
4870
+ -webkit-overflow-scrolling: touch;
4871
+ }
4872
+
4873
+ .shortcuts-overlay {
4874
+ padding: 1rem;
4875
+ }
4876
+ }
4877
+
4878
+ @media (hover: none) and (pointer: coarse) {
4879
+ .copy-scenario-btn,
4880
+ .copy-prompt-btn,
4881
+ .permalink-anchor {
4882
+ opacity: 1;
4883
+ }
4884
+ }
4885
+
4672
4886
  `;
4673
4887
 
4674
4888
  // src/formatters/html/themes/default.ts
@@ -14387,6 +14601,7 @@ function renderScenario(args, deps) {
14387
14601
  </div>
14388
14602
  <div class="scenario-actions">
14389
14603
  <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>
14604
+ ${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>` : ""}
14390
14605
  <button class="permalink-anchor" onclick="copyPermalink('scenario-${tc.id}')" aria-label="Copy link to scenario" title="Copy link">#</button>
14391
14606
  <span class="scenario-duration">${duration}</span>
14392
14607
  </div>
@@ -19388,7 +19603,7 @@ var ReportGenerator = class {
19388
19603
  exclude: options.exclude ?? [],
19389
19604
  includeTags: options.includeTags ?? [],
19390
19605
  excludeTags: options.excludeTags ?? [],
19391
- formats: options.formats ?? ["cucumber-json"],
19606
+ formats: options.formats ?? ["html"],
19392
19607
  outputDir: options.outputDir ?? "reports",
19393
19608
  outputName: options.outputName ?? "index",
19394
19609
  outputNameTimestamp: options.outputNameTimestamp ?? false,