helm-env-delta 1.14.1 → 1.14.2

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
@@ -56,7 +56,7 @@ HelmEnvDelta (`hed`) automates environment synchronization for GitOps workflows
56
56
 
57
57
  📦 **Config Inheritance** - Reuse base configurations with environment-specific overrides.
58
58
 
59
- 📊 **Multiple Reports** - Console, HTML (visual, self-contained), and JSON (CI/CD) output formats. HTML reports include collapsible diff stats dashboard, stop rule violations table (dry-run only), synchronized side-by-side scrolling, copy diff buttons, file search, and collapse/expand controls. Empty categories are automatically hidden.
59
+ 📊 **Multiple Reports** - Console, HTML (visual, self-contained), and JSON (CI/CD) output formats. HTML reports include collapsible diff stats dashboard, stop rule violations table (dry-run only), synchronized side-by-side scrolling, copy diff buttons, file search, collapse/expand controls, jump-to-sidebar navigation, and auto-collapse for large file sets. Empty categories are automatically hidden.
60
60
 
61
61
  🔍 **Discovery Tools** - Preview files (`-l`), inspect config (`--show-config`), filter by filename/content (`-f`), filter by change type (`-m`), validate with comprehensive warnings including unused pattern detection.
62
62
 
@@ -121,7 +121,7 @@ hed -c config.yaml
121
121
  hed -c config.yaml -H
122
122
  ```
123
123
 
124
- Self-contained HTML report — works offline, no CDN required. Includes collapsible diff stats dashboard, stop rule violations table (shown in dry-run mode), synchronized side-by-side scrolling, copy buttons, sidebar search, and collapse/expand controls. Empty categories are automatically hidden.
124
+ Self-contained HTML report — works offline, no CDN required. Includes collapsible diff stats dashboard, stop rule violations table (shown in dry-run mode), synchronized side-by-side scrolling, copy buttons, sidebar search, collapse/expand controls, and jump-to-sidebar navigation. File blocks auto-collapse when there are more than 10 files. Empty categories are automatically hidden.
125
125
 
126
126
  ### 5️⃣ Get Smart Suggestions (Optional)
127
127
 
@@ -59,13 +59,14 @@ const generateAddedFileSummary = (file) => {
59
59
  return file.path;
60
60
  return `<span class="filename-transform">${file.originalPath} → ${file.path}</span>`;
61
61
  };
62
- const generateAddedFileSection = (file, fileId) => {
62
+ const JUMP_TO_SIDEBAR_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M2 2h3v12H2V2zm0-1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H2zm5 4h7v1H7V5zm0 3h7v1H7V8zm0 3h5v1H7v-1z"/></svg>`;
63
+ const generateAddedFileSection = (file, fileId, open) => {
63
64
  const summary = generateAddedFileSummary(file);
64
65
  const escapedContent = (0, treeRenderer_1.escapeHtml)(file.processedContent);
65
66
  const filename = file.path.split('/').pop() || file.path;
66
67
  return `
67
- <details class="file-section" id="${fileId}" data-file-id="${fileId}" open>
68
- <summary>${summary}</summary>
68
+ <details class="file-section" id="${fileId}" data-file-id="${fileId}"${open ? ' open' : ''}>
69
+ <summary><a class="jump-to-sidebar-link" data-file-id="${fileId}" href="#" title="Show in file browser">${JUMP_TO_SIDEBAR_ICON}</a><span class="summary-expand-icon"></span>${summary}</summary>
69
70
  <div class="content-container">
70
71
  <div class="content-actions">
71
72
  <button class="copy-btn" data-file-id="${fileId}" title="Copy to clipboard">📋 Copy</button>
@@ -76,7 +77,7 @@ const generateAddedFileSection = (file, fileId) => {
76
77
  </details>
77
78
  `;
78
79
  };
79
- const generateChangedFileSection = (file, fileId) => {
80
+ const generateChangedFileSection = (file, fileId, open) => {
80
81
  const isYaml = (0, fileType_1.isYamlFile)(file.path);
81
82
  const summary = generateFileSummary(file);
82
83
  const destinationContent = (0, serialization_1.serializeForDiff)(file.processedDestContent, isYaml);
@@ -86,8 +87,8 @@ const generateChangedFileSection = (file, fileId) => {
86
87
  const { added, removed } = countDiffLines(unifiedDiff);
87
88
  const escapedDiff = (0, treeRenderer_1.escapeHtml)(unifiedDiff);
88
89
  const html = `
89
- <details class="file-section" id="${fileId}" data-file-id="${fileId}" open>
90
- <summary>${summary}<span class="summary-badges"><span class="line-badge line-added">+${added}</span><span class="line-badge line-removed">-${removed}</span></span></summary>
90
+ <details class="file-section" id="${fileId}" data-file-id="${fileId}"${open ? ' open' : ''}>
91
+ <summary><a class="jump-to-sidebar-link" data-file-id="${fileId}" href="#" title="Show in file browser">${JUMP_TO_SIDEBAR_ICON}</a><span class="summary-expand-icon"></span>${summary}<span class="summary-badges"><span class="line-badge line-added">+${added}</span><span class="line-badge line-removed">-${removed}</span></span></summary>
91
92
  <div class="diff-toolbar">
92
93
  <button class="copy-diff-btn" data-file-id="${fileId}">Copy Diff</button>
93
94
  </div>
@@ -131,16 +132,19 @@ const generateHtmlReport = async (diffResult, formattedFiles, config, dryRun, lo
131
132
  const changedFileIds = new Map();
132
133
  for (const [index, file] of diffResult.changedFiles.entries())
133
134
  changedFileIds.set(file.path, `file-${index}`);
135
+ const COLLAPSE_THRESHOLD = 10;
136
+ const changedOpen = diffResult.changedFiles.length <= COLLAPSE_THRESHOLD;
137
+ const addedOpen = diffResult.addedFiles.length <= COLLAPSE_THRESHOLD;
134
138
  const fileStats = new Map();
135
139
  const changedSections = diffResult.changedFiles.map((file, index) => {
136
- const result = generateChangedFileSection(file, `file-${index}`);
140
+ const result = generateChangedFileSection(file, `file-${index}`, changedOpen);
137
141
  fileStats.set(file.path, { added: result.added, removed: result.removed });
138
142
  return result.html;
139
143
  });
140
144
  const addedFileIds = new Map();
141
145
  for (const [index, file] of diffResult.addedFiles.entries())
142
146
  addedFileIds.set(file.path, `added-file-${index}`);
143
- const addedSections = diffResult.addedFiles.map((file, index) => generateAddedFileSection(file, `added-file-${index}`));
147
+ const addedSections = diffResult.addedFiles.map((file, index) => generateAddedFileSection(file, `added-file-${index}`, addedOpen));
144
148
  let totalAdded = 0;
145
149
  let totalRemoved = 0;
146
150
  const statsArray = [];
@@ -1,3 +1,3 @@
1
1
  export declare const DIFF2HTML_STYLES: string;
2
- export declare const HTML_STYLES = "\n /* Custom styles */\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n margin: 0;\n padding: 20px;\n background: #f6f8fa;\n }\n\n header {\n background: white;\n padding: 20px;\n border-radius: 6px;\n margin-bottom: 20px;\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n }\n\n h1 {\n margin: 0 0 10px 0;\n color: #24292e;\n }\n\n .metadata {\n display: flex;\n gap: 20px;\n margin: 10px 0;\n color: #586069;\n font-size: 14px;\n }\n\n .dry-run-badge {\n display: inline-block;\n padding: 4px 8px;\n background: #cfe2ff;\n color: #084298;\n border-radius: 4px;\n font-weight: bold;\n font-size: 12px;\n }\n\n .summary {\n display: flex;\n gap: 12px;\n margin: 15px 0;\n }\n\n .stat {\n padding: 8px 16px;\n border-radius: 6px;\n font-weight: 600;\n font-size: 14px;\n }\n\n .stat.added { background: #d4edda; color: #155724; }\n .stat.changed { background: #fff3cd; color: #856404; }\n .stat.deleted { background: #f8d7da; color: #721c24; }\n .stat.formatted { background: #d1ecf1; color: #0c5460; }\n .stat.unchanged { background: #e2e3e5; color: #383d41; }\n\n .tabs {\n display: flex;\n background: white;\n border-radius: 6px 6px 0 0;\n border-bottom: 1px solid #d0d7de;\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n }\n\n .tab {\n padding: 12px 24px;\n border: none;\n background: transparent;\n cursor: pointer;\n font-size: 14px;\n color: #586069;\n transition: all 0.2s;\n }\n\n .tab:hover {\n color: #24292e;\n }\n\n .tab.active {\n border-bottom: 2px solid #0969da;\n color: #0969da;\n font-weight: 600;\n }\n\n main {\n background: white;\n padding: 20px;\n border-radius: 0 0 6px 6px;\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n }\n\n .tab-content {\n display: none;\n }\n\n .tab-content.active {\n display: block;\n }\n\n .file-section {\n margin: 12px 0;\n border: 1px solid #d0d7de;\n border-radius: 6px;\n }\n\n .file-section summary {\n padding: 12px 16px;\n background: #f6f8fa;\n cursor: pointer;\n font-weight: 600;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n font-size: 13px;\n color: #24292e;\n }\n\n .file-section summary:hover {\n background: #eaeef2;\n }\n\n .file-section[open] > summary {\n position: sticky;\n top: 0;\n z-index: 10;\n border-bottom: 1px solid #d0d7de;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n }\n\n .filename-transform {\n color: #0969da;\n }\n\n .diff-container {\n padding: 0;\n }\n\n /* Hide diff2html file header with rename badge */\n .d2h-file-header {\n display: none;\n }\n\n .file-list {\n margin: 20px 0;\n }\n\n .file-list ul {\n list-style: none;\n padding: 0;\n margin: 10px 0;\n }\n\n .file-list li {\n padding: 8px 16px;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n font-size: 13px;\n color: #586069;\n border-bottom: 1px solid #f6f8fa;\n }\n\n .file-list li:hover {\n background: #f6f8fa;\n }\n\n /* Treeview styles */\n .tree-root {\n list-style: none;\n padding: 0;\n margin: 10px 0;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n font-size: 13px;\n }\n\n .tree-root ul {\n list-style: none;\n padding-left: 20px;\n margin: 0;\n }\n\n .tree-folder,\n .tree-file {\n padding: 4px 8px;\n border-radius: 4px;\n cursor: default;\n }\n\n .tree-folder:hover,\n .tree-file:hover {\n background: #f6f8fa;\n }\n\n .tree-toggle {\n display: inline-block;\n width: 16px;\n cursor: pointer;\n color: #586069;\n font-size: 10px;\n user-select: none;\n }\n\n .tree-folder.collapsed > .tree-toggle {\n transform: rotate(-90deg);\n }\n\n .tree-folder.collapsed > .tree-children {\n display: none;\n }\n\n .tree-folder-name {\n color: #0969da;\n font-weight: 500;\n }\n\n .tree-file-name {\n color: #586069;\n padding-left: 16px;\n }\n\n /* Sidebar styles */\n .sidebar-container {\n display: flex;\n gap: 0;\n }\n\n .sidebar {\n width: 280px;\n min-width: 280px;\n border-right: 1px solid #d0d7de;\n background: #f6f8fa;\n transition: width 0.2s, min-width 0.2s, padding 0.2s, opacity 0.2s;\n }\n\n .sidebar.collapsed {\n width: 0;\n min-width: 0;\n padding: 0;\n overflow: hidden;\n border-right: none;\n }\n\n .sidebar-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n border-bottom: 1px solid #d0d7de;\n background: #fff;\n font-weight: 600;\n font-size: 14px;\n color: #24292e;\n position: sticky;\n top: 0;\n z-index: 1;\n }\n\n .sidebar-toggle {\n background: none;\n border: 1px solid #d0d7de;\n border-radius: 4px;\n cursor: pointer;\n padding: 4px 8px;\n color: #586069;\n font-size: 12px;\n }\n\n .sidebar-toggle:hover {\n background: #f6f8fa;\n color: #24292e;\n }\n\n .sidebar-content {\n padding: 8px;\n }\n\n .sidebar-tree .tree-file-link {\n color: #586069;\n text-decoration: none;\n padding-left: 16px;\n display: block;\n }\n\n .sidebar-tree .tree-file-link:hover {\n color: #0969da;\n }\n\n .sidebar-tree .tree-file.active .tree-file-link {\n color: #0969da;\n font-weight: 600;\n }\n\n .changed-content {\n flex: 1;\n min-width: 0;\n padding-left: 20px;\n }\n\n .sidebar-expand-btn {\n display: none;\n position: fixed;\n left: 0;\n top: 50%;\n transform: translateY(-50%);\n background: #f6f8fa;\n border: 1px solid #d0d7de;\n border-left: none;\n border-radius: 0 4px 4px 0;\n padding: 8px 4px;\n cursor: pointer;\n color: #586069;\n z-index: 100;\n }\n\n .sidebar-expand-btn:hover {\n background: #eaeef2;\n color: #24292e;\n }\n\n .sidebar.collapsed ~ .sidebar-expand-btn {\n display: block;\n }\n\n /* Added content area (same as changed-content) */\n .added-content {\n flex: 1;\n min-width: 0;\n padding-left: 20px;\n }\n\n /* Content container for added files */\n .content-container {\n padding: 16px;\n background: #f6f8fa;\n border-top: 1px solid #d0d7de;\n }\n\n .content-actions {\n display: flex;\n gap: 8px;\n margin-bottom: 12px;\n }\n\n .copy-btn,\n .download-btn {\n padding: 6px 12px;\n border: 1px solid #d0d7de;\n border-radius: 6px;\n background: white;\n cursor: pointer;\n font-size: 13px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n color: #24292e;\n transition: all 0.2s;\n }\n\n .copy-btn:hover,\n .download-btn:hover {\n background: #f3f4f6;\n border-color: #b0b7be;\n }\n\n .copy-btn.copied {\n background: #d4edda;\n border-color: #28a745;\n color: #155724;\n }\n\n .file-content {\n margin: 0;\n padding: 16px;\n background: white;\n border: 1px solid #d0d7de;\n border-radius: 6px;\n overflow-x: auto;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n font-size: 12px;\n line-height: 1.5;\n white-space: pre;\n }\n\n .file-content code {\n font-family: inherit;\n }\n\n /* Scroll-to-top button */\n .scroll-to-top {\n display: none;\n position: fixed;\n bottom: 30px;\n right: 30px;\n width: 40px;\n height: 40px;\n border: none;\n border-radius: 50%;\n background: #0969da;\n color: white;\n font-size: 18px;\n cursor: pointer;\n box-shadow: 0 2px 8px rgba(0,0,0,0.2);\n transition: opacity 0.2s, background 0.2s;\n z-index: 200;\n line-height: 40px;\n text-align: center;\n padding: 0;\n }\n\n .scroll-to-top:hover {\n background: #0550ae;\n }\n\n .scroll-to-top.visible {\n display: block;\n }\n\n /* Content toolbar (collapse/expand buttons) */\n .content-toolbar {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n margin-bottom: 12px;\n }\n\n .collapse-all-btn,\n .expand-all-btn {\n padding: 4px 12px;\n border: 1px solid #d0d7de;\n border-radius: 4px;\n background: none;\n cursor: pointer;\n font-size: 12px;\n color: #586069;\n transition: all 0.2s;\n }\n\n .collapse-all-btn:hover,\n .expand-all-btn:hover {\n background: #f6f8fa;\n color: #24292e;\n }\n\n /* Line change count badges */\n .summary-badges {\n float: right;\n display: inline-flex;\n gap: 6px;\n margin-left: 12px;\n }\n\n .line-badge {\n display: inline-block;\n padding: 1px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n line-height: 18px;\n }\n\n .line-added {\n background: #d4edda;\n color: #155724;\n }\n\n .line-removed {\n background: #f8d7da;\n color: #721c24;\n }\n\n /* Diff toolbar */\n .diff-toolbar {\n display: flex;\n justify-content: flex-end;\n padding: 8px 16px;\n border-bottom: 1px solid #d0d7de;\n background: #f6f8fa;\n }\n\n .copy-diff-btn {\n padding: 4px 12px;\n border: 1px solid #d0d7de;\n border-radius: 6px;\n background: white;\n cursor: pointer;\n font-size: 12px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n color: #24292e;\n transition: all 0.2s;\n }\n\n .copy-diff-btn:hover {\n background: #f3f4f6;\n border-color: #b0b7be;\n }\n\n .copy-diff-btn.copied {\n background: #d4edda;\n border-color: #28a745;\n color: #155724;\n }\n\n /* Sidebar search */\n .sidebar-search {\n width: 100%;\n padding: 6px 8px;\n border: 1px solid #d0d7de;\n border-radius: 4px;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n font-size: 12px;\n margin-bottom: 8px;\n box-sizing: border-box;\n position: sticky;\n top: 0;\n z-index: 1;\n background: #fff;\n }\n\n .sidebar-search:focus {\n outline: none;\n border-color: #0969da;\n box-shadow: 0 0 0 3px rgba(9,105,218,0.15);\n }\n\n /* Stats toggle button */\n .stats-toggle-btn {\n padding: 4px 12px;\n border: 1px solid #d0d7de;\n border-radius: 4px;\n background: none;\n cursor: pointer;\n font-size: 12px;\n color: #586069;\n transition: all 0.2s;\n }\n\n .stats-toggle-btn:hover {\n background: #f6f8fa;\n color: #24292e;\n }\n\n /* Statistics dashboard */\n .stats-dashboard {\n margin: 15px 0 0;\n padding: 12px 0 0;\n border-top: 1px solid #e1e4e8;\n }\n\n .stats-summary {\n display: flex;\n gap: 16px;\n align-items: center;\n margin-bottom: 10px;\n }\n\n .stats-summary .total-added {\n font-weight: 700;\n color: #155724;\n font-size: 16px;\n }\n\n .stats-summary .total-removed {\n font-weight: 700;\n color: #721c24;\n font-size: 16px;\n }\n\n .stats-bar {\n display: flex;\n height: 8px;\n border-radius: 4px;\n overflow: hidden;\n background: #e1e4e8;\n margin-bottom: 10px;\n }\n\n .stats-segment {\n height: 100%;\n min-width: 2px;\n }\n\n .stats-segment.added-segment {\n background: #28a745;\n }\n\n .stats-segment.removed-segment {\n background: #d73a49;\n }\n\n .top-changed-files {\n list-style: none;\n padding: 0;\n margin: 0;\n font-size: 13px;\n }\n\n .top-changed-files li {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 3px 0;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n color: #586069;\n }\n\n .top-changed-files .file-path {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin-right: 8px;\n }\n\n .top-changed-files .file-stats {\n white-space: nowrap;\n flex-shrink: 0;\n }\n\n /* Stop rules violations badge */\n .stat.violations { background: #f8d7da; color: #721c24; }\n\n /* Violations section */\n .violations-section {\n margin: 15px 0 0;\n padding: 12px 0 0;\n border-top: 1px solid #e1e4e8;\n }\n\n .violations-toggle-btn {\n padding: 4px 12px;\n border: 1px solid #d0d7de;\n border-radius: 4px;\n background: none;\n cursor: pointer;\n font-size: 12px;\n color: #586069;\n transition: all 0.2s;\n }\n\n .violations-toggle-btn:hover {\n background: #f6f8fa;\n color: #24292e;\n }\n\n .violations-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n margin-top: 10px;\n }\n\n .violations-table th {\n background: #f6f8fa;\n text-align: left;\n padding: 8px 12px;\n border-bottom: 2px solid #d0d7de;\n font-weight: 600;\n color: #24292e;\n }\n\n .violations-table td {\n padding: 8px 12px;\n border-bottom: 1px solid #e1e4e8;\n color: #24292e;\n vertical-align: top;\n }\n\n .violations-table tr:hover td {\n background: #f6f8fa;\n }\n\n .violation-rule-badge {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n background: #fff3cd;\n color: #856404;\n white-space: nowrap;\n }\n\n .violation-value {\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n font-size: 12px;\n background: #f6f8fa;\n padding: 2px 6px;\n border-radius: 3px;\n border: 1px solid #e1e4e8;\n }\n";
2
+ export declare const HTML_STYLES = "\n /* Custom styles */\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n margin: 0;\n padding: 20px;\n background: #f6f8fa;\n }\n\n header {\n background: white;\n padding: 20px;\n border-radius: 6px;\n margin-bottom: 20px;\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n }\n\n h1 {\n margin: 0 0 10px 0;\n color: #24292e;\n }\n\n .metadata {\n display: flex;\n gap: 20px;\n margin: 10px 0;\n color: #586069;\n font-size: 14px;\n }\n\n .dry-run-badge {\n display: inline-block;\n padding: 4px 8px;\n background: #cfe2ff;\n color: #084298;\n border-radius: 4px;\n font-weight: bold;\n font-size: 12px;\n }\n\n .summary {\n display: flex;\n gap: 12px;\n margin: 15px 0;\n }\n\n .stat {\n padding: 8px 16px;\n border-radius: 6px;\n font-weight: 600;\n font-size: 14px;\n }\n\n .stat.added { background: #d4edda; color: #155724; }\n .stat.changed { background: #fff3cd; color: #856404; }\n .stat.deleted { background: #f8d7da; color: #721c24; }\n .stat.formatted { background: #d1ecf1; color: #0c5460; }\n .stat.unchanged { background: #e2e3e5; color: #383d41; }\n\n .tabs {\n display: flex;\n background: white;\n border-radius: 6px 6px 0 0;\n border-bottom: 1px solid #d0d7de;\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n }\n\n .tab {\n padding: 12px 24px;\n border: none;\n background: transparent;\n cursor: pointer;\n font-size: 14px;\n color: #586069;\n transition: all 0.2s;\n }\n\n .tab:hover {\n color: #24292e;\n }\n\n .tab.active {\n border-bottom: 2px solid #0969da;\n color: #0969da;\n font-weight: 600;\n }\n\n main {\n background: white;\n padding: 20px;\n border-radius: 0 0 6px 6px;\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n }\n\n .tab-content {\n display: none;\n }\n\n .tab-content.active {\n display: block;\n }\n\n .file-section {\n margin: 12px 0;\n border: 1px solid #d0d7de;\n border-radius: 6px;\n }\n\n .file-section summary {\n padding: 12px 16px;\n background: #f6f8fa;\n cursor: pointer;\n font-weight: 600;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n font-size: 13px;\n color: #24292e;\n display: flex;\n align-items: center;\n gap: 6px;\n list-style: none;\n }\n\n .file-section summary::-webkit-details-marker {\n display: none;\n }\n\n .summary-expand-icon::before {\n content: '\u25B6';\n font-size: 10px;\n color: #6a737d;\n }\n\n .file-section[open] > summary .summary-expand-icon::before {\n content: '\u25BC';\n }\n\n .jump-to-sidebar-link {\n display: inline-flex;\n align-items: center;\n color: #6a737d;\n text-decoration: none;\n padding: 2px 3px;\n border-radius: 3px;\n flex-shrink: 0;\n line-height: 1;\n }\n\n .jump-to-sidebar-link:hover {\n color: #0969da;\n background: rgba(9, 105, 218, 0.08);\n }\n\n .file-section summary:hover {\n background: #eaeef2;\n }\n\n .file-section[open] > summary {\n position: sticky;\n top: 0;\n z-index: 10;\n border-bottom: 1px solid #d0d7de;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n }\n\n .filename-transform {\n color: #0969da;\n }\n\n .diff-container {\n padding: 0;\n }\n\n /* Hide diff2html file header with rename badge */\n .d2h-file-header {\n display: none;\n }\n\n .file-list {\n margin: 20px 0;\n }\n\n .file-list ul {\n list-style: none;\n padding: 0;\n margin: 10px 0;\n }\n\n .file-list li {\n padding: 8px 16px;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n font-size: 13px;\n color: #586069;\n border-bottom: 1px solid #f6f8fa;\n }\n\n .file-list li:hover {\n background: #f6f8fa;\n }\n\n /* Treeview styles */\n .tree-root {\n list-style: none;\n padding: 0;\n margin: 10px 0;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n font-size: 13px;\n }\n\n .tree-root ul {\n list-style: none;\n padding-left: 20px;\n margin: 0;\n }\n\n .tree-folder,\n .tree-file {\n padding: 4px 8px;\n border-radius: 4px;\n cursor: default;\n }\n\n .tree-folder:hover,\n .tree-file:hover {\n background: #f6f8fa;\n }\n\n .tree-toggle {\n display: inline-block;\n width: 16px;\n cursor: pointer;\n color: #586069;\n font-size: 10px;\n user-select: none;\n }\n\n .tree-folder.collapsed > .tree-toggle {\n transform: rotate(-90deg);\n }\n\n .tree-folder.collapsed > .tree-children {\n display: none;\n }\n\n .tree-folder-name {\n color: #0969da;\n font-weight: 500;\n }\n\n .tree-file-name {\n color: #586069;\n padding-left: 16px;\n }\n\n /* Sidebar styles */\n .sidebar-container {\n display: flex;\n gap: 0;\n }\n\n .sidebar {\n width: 280px;\n min-width: 280px;\n border-right: 1px solid #d0d7de;\n background: #f6f8fa;\n transition: width 0.2s, min-width 0.2s, padding 0.2s, opacity 0.2s;\n }\n\n .sidebar.collapsed {\n width: 0;\n min-width: 0;\n padding: 0;\n overflow: hidden;\n border-right: none;\n }\n\n .sidebar-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n border-bottom: 1px solid #d0d7de;\n background: #fff;\n font-weight: 600;\n font-size: 14px;\n color: #24292e;\n position: sticky;\n top: 0;\n z-index: 1;\n }\n\n .sidebar-toggle {\n background: none;\n border: 1px solid #d0d7de;\n border-radius: 4px;\n cursor: pointer;\n padding: 4px 8px;\n color: #586069;\n font-size: 12px;\n }\n\n .sidebar-toggle:hover {\n background: #f6f8fa;\n color: #24292e;\n }\n\n .sidebar-content {\n padding: 8px;\n }\n\n .sidebar-tree .tree-file-link {\n color: #586069;\n text-decoration: none;\n padding-left: 16px;\n display: block;\n }\n\n .sidebar-tree .tree-file-link:hover {\n color: #0969da;\n }\n\n .sidebar-tree .tree-file.active .tree-file-link {\n color: #0969da;\n font-weight: 600;\n }\n\n .changed-content {\n flex: 1;\n min-width: 0;\n padding-left: 20px;\n }\n\n .sidebar-expand-btn {\n display: none;\n position: fixed;\n left: 0;\n top: 50%;\n transform: translateY(-50%);\n background: #f6f8fa;\n border: 1px solid #d0d7de;\n border-left: none;\n border-radius: 0 4px 4px 0;\n padding: 8px 4px;\n cursor: pointer;\n color: #586069;\n z-index: 100;\n }\n\n .sidebar-expand-btn:hover {\n background: #eaeef2;\n color: #24292e;\n }\n\n .sidebar.collapsed ~ .sidebar-expand-btn {\n display: block;\n }\n\n /* Added content area (same as changed-content) */\n .added-content {\n flex: 1;\n min-width: 0;\n padding-left: 20px;\n }\n\n /* Content container for added files */\n .content-container {\n padding: 16px;\n background: #f6f8fa;\n border-top: 1px solid #d0d7de;\n }\n\n .content-actions {\n display: flex;\n gap: 8px;\n margin-bottom: 12px;\n }\n\n .copy-btn,\n .download-btn {\n padding: 6px 12px;\n border: 1px solid #d0d7de;\n border-radius: 6px;\n background: white;\n cursor: pointer;\n font-size: 13px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n color: #24292e;\n transition: all 0.2s;\n }\n\n .copy-btn:hover,\n .download-btn:hover {\n background: #f3f4f6;\n border-color: #b0b7be;\n }\n\n .copy-btn.copied {\n background: #d4edda;\n border-color: #28a745;\n color: #155724;\n }\n\n .file-content {\n margin: 0;\n padding: 16px;\n background: white;\n border: 1px solid #d0d7de;\n border-radius: 6px;\n overflow-x: auto;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n font-size: 12px;\n line-height: 1.5;\n white-space: pre;\n }\n\n .file-content code {\n font-family: inherit;\n }\n\n /* Scroll-to-top button */\n .scroll-to-top {\n display: none;\n position: fixed;\n bottom: 30px;\n right: 30px;\n width: 40px;\n height: 40px;\n border: none;\n border-radius: 50%;\n background: #0969da;\n color: white;\n font-size: 18px;\n cursor: pointer;\n box-shadow: 0 2px 8px rgba(0,0,0,0.2);\n transition: opacity 0.2s, background 0.2s;\n z-index: 200;\n line-height: 40px;\n text-align: center;\n padding: 0;\n }\n\n .scroll-to-top:hover {\n background: #0550ae;\n }\n\n .scroll-to-top.visible {\n display: block;\n }\n\n /* Content toolbar (collapse/expand buttons) */\n .content-toolbar {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n margin-bottom: 12px;\n }\n\n .collapse-all-btn,\n .expand-all-btn {\n padding: 4px 12px;\n border: 1px solid #d0d7de;\n border-radius: 4px;\n background: none;\n cursor: pointer;\n font-size: 12px;\n color: #586069;\n transition: all 0.2s;\n }\n\n .collapse-all-btn:hover,\n .expand-all-btn:hover {\n background: #f6f8fa;\n color: #24292e;\n }\n\n /* Line change count badges */\n .summary-badges {\n display: inline-flex;\n gap: 6px;\n margin-left: auto;\n }\n\n .line-badge {\n display: inline-block;\n padding: 1px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n line-height: 18px;\n }\n\n .line-added {\n background: #d4edda;\n color: #155724;\n }\n\n .line-removed {\n background: #f8d7da;\n color: #721c24;\n }\n\n /* Diff toolbar */\n .diff-toolbar {\n display: flex;\n justify-content: flex-end;\n padding: 8px 16px;\n border-bottom: 1px solid #d0d7de;\n background: #f6f8fa;\n }\n\n .copy-diff-btn {\n padding: 4px 12px;\n border: 1px solid #d0d7de;\n border-radius: 6px;\n background: white;\n cursor: pointer;\n font-size: 12px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n color: #24292e;\n transition: all 0.2s;\n }\n\n .copy-diff-btn:hover {\n background: #f3f4f6;\n border-color: #b0b7be;\n }\n\n .copy-diff-btn.copied {\n background: #d4edda;\n border-color: #28a745;\n color: #155724;\n }\n\n /* Sidebar search */\n .sidebar-search {\n width: 100%;\n padding: 6px 8px;\n border: 1px solid #d0d7de;\n border-radius: 4px;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n font-size: 12px;\n margin-bottom: 8px;\n box-sizing: border-box;\n position: sticky;\n top: 0;\n z-index: 1;\n background: #fff;\n }\n\n .sidebar-search:focus {\n outline: none;\n border-color: #0969da;\n box-shadow: 0 0 0 3px rgba(9,105,218,0.15);\n }\n\n /* Stats toggle button */\n .stats-toggle-btn {\n padding: 4px 12px;\n border: 1px solid #d0d7de;\n border-radius: 4px;\n background: none;\n cursor: pointer;\n font-size: 12px;\n color: #586069;\n transition: all 0.2s;\n }\n\n .stats-toggle-btn:hover {\n background: #f6f8fa;\n color: #24292e;\n }\n\n /* Statistics dashboard */\n .stats-dashboard {\n margin: 15px 0 0;\n padding: 12px 0 0;\n border-top: 1px solid #e1e4e8;\n }\n\n .stats-summary {\n display: flex;\n gap: 16px;\n align-items: center;\n margin-bottom: 10px;\n }\n\n .stats-summary .total-added {\n font-weight: 700;\n color: #155724;\n font-size: 16px;\n }\n\n .stats-summary .total-removed {\n font-weight: 700;\n color: #721c24;\n font-size: 16px;\n }\n\n .stats-bar {\n display: flex;\n height: 8px;\n border-radius: 4px;\n overflow: hidden;\n background: #e1e4e8;\n margin-bottom: 10px;\n }\n\n .stats-segment {\n height: 100%;\n min-width: 2px;\n }\n\n .stats-segment.added-segment {\n background: #28a745;\n }\n\n .stats-segment.removed-segment {\n background: #d73a49;\n }\n\n .top-changed-files {\n list-style: none;\n padding: 0;\n margin: 0;\n font-size: 13px;\n }\n\n .top-changed-files li {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 3px 0;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n color: #586069;\n }\n\n .top-changed-files .file-path {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin-right: 8px;\n }\n\n .top-changed-files .file-stats {\n white-space: nowrap;\n flex-shrink: 0;\n }\n\n /* Stop rules violations badge */\n .stat.violations { background: #f8d7da; color: #721c24; }\n\n /* Violations section */\n .violations-section {\n margin: 15px 0 0;\n padding: 12px 0 0;\n border-top: 1px solid #e1e4e8;\n }\n\n .violations-toggle-btn {\n padding: 4px 12px;\n border: 1px solid #d0d7de;\n border-radius: 4px;\n background: none;\n cursor: pointer;\n font-size: 12px;\n color: #586069;\n transition: all 0.2s;\n }\n\n .violations-toggle-btn:hover {\n background: #f6f8fa;\n color: #24292e;\n }\n\n .violations-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n margin-top: 10px;\n }\n\n .violations-table th {\n background: #f6f8fa;\n text-align: left;\n padding: 8px 12px;\n border-bottom: 2px solid #d0d7de;\n font-weight: 600;\n color: #24292e;\n }\n\n .violations-table td {\n padding: 8px 12px;\n border-bottom: 1px solid #e1e4e8;\n color: #24292e;\n vertical-align: top;\n }\n\n .violations-table tr:hover td {\n background: #f6f8fa;\n }\n\n .violation-rule-badge {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n background: #fff3cd;\n color: #856404;\n white-space: nowrap;\n }\n\n .violation-value {\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n font-size: 12px;\n background: #f6f8fa;\n padding: 2px 6px;\n border-radius: 3px;\n border: 1px solid #e1e4e8;\n }\n";
3
3
  export declare const TAB_SCRIPT: string;
@@ -119,6 +119,40 @@ exports.HTML_STYLES = `
119
119
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
120
120
  font-size: 13px;
121
121
  color: #24292e;
122
+ display: flex;
123
+ align-items: center;
124
+ gap: 6px;
125
+ list-style: none;
126
+ }
127
+
128
+ .file-section summary::-webkit-details-marker {
129
+ display: none;
130
+ }
131
+
132
+ .summary-expand-icon::before {
133
+ content: '\u25B6';
134
+ font-size: 10px;
135
+ color: #6a737d;
136
+ }
137
+
138
+ .file-section[open] > summary .summary-expand-icon::before {
139
+ content: '\u25BC';
140
+ }
141
+
142
+ .jump-to-sidebar-link {
143
+ display: inline-flex;
144
+ align-items: center;
145
+ color: #6a737d;
146
+ text-decoration: none;
147
+ padding: 2px 3px;
148
+ border-radius: 3px;
149
+ flex-shrink: 0;
150
+ line-height: 1;
151
+ }
152
+
153
+ .jump-to-sidebar-link:hover {
154
+ color: #0969da;
155
+ background: rgba(9, 105, 218, 0.08);
122
156
  }
123
157
 
124
158
  .file-section summary:hover {
@@ -445,10 +479,9 @@ exports.HTML_STYLES = `
445
479
 
446
480
  /* Line change count badges */
447
481
  .summary-badges {
448
- float: right;
449
482
  display: inline-flex;
450
483
  gap: 6px;
451
- margin-left: 12px;
484
+ margin-left: auto;
452
485
  }
453
486
 
454
487
  .line-badge {
@@ -770,6 +803,37 @@ exports.TAB_SCRIPT = String.raw `
770
803
  });
771
804
  });
772
805
 
806
+ // Jump-to-sidebar link in file block headers
807
+ document.querySelectorAll('.jump-to-sidebar-link').forEach(link => {
808
+ link.addEventListener('click', (e) => {
809
+ e.preventDefault();
810
+ e.stopPropagation(); // prevent <details> toggle
811
+ const fileId = link.getAttribute('data-file-id');
812
+ const tabContent = link.closest('.tab-content');
813
+ if (!tabContent || !fileId) return;
814
+
815
+ const treeFile = tabContent.querySelector('.tree-file[data-file-id="' + fileId + '"]');
816
+ if (!treeFile) return;
817
+
818
+ // Expand any collapsed parent folders
819
+ let el = treeFile.parentElement;
820
+ while (el) {
821
+ if (el.classList.contains('tree-children')) {
822
+ el.style.display = '';
823
+ if (el.parentElement) el.parentElement.classList.remove('collapsed');
824
+ }
825
+ el = el.parentElement;
826
+ }
827
+
828
+ // Highlight in sidebar
829
+ tabContent.querySelectorAll('.sidebar-tree .tree-file').forEach(f => f.classList.remove('active'));
830
+ treeFile.classList.add('active');
831
+
832
+ // Scroll sidebar to the file entry
833
+ treeFile.scrollIntoView({ behavior: 'smooth', block: 'center' });
834
+ });
835
+ });
836
+
773
837
  // IntersectionObserver to highlight current file on scroll
774
838
  const fileSections = document.querySelectorAll('.file-section[id]');
775
839
  if (fileSections.length > 0 && 'IntersectionObserver' in window) {
@@ -144,6 +144,10 @@ const generateHtmlTemplate = (diffResult, formattedFiles, trulyUnchangedFiles, m
144
144
  </aside>
145
145
  <button class="sidebar-expand-btn" data-sidebar="added">&#9654;</button>
146
146
  <div class="added-content">
147
+ <div class="content-toolbar">
148
+ <button class="collapse-all-btn">Collapse All</button>
149
+ <button class="expand-all-btn">Expand All</button>
150
+ </div>
147
151
  ${addedSections.join('\n')}
148
152
  </div>
149
153
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helm-env-delta",
3
- "version": "1.14.1",
3
+ "version": "1.14.2",
4
4
  "description": "HelmEnvDelta – environment-aware YAML delta and sync for GitOps",
5
5
  "author": "BCsabaEngine",
6
6
  "license": "ISC",
@@ -67,7 +67,7 @@
67
67
  "@eslint/js": "^10.0.1",
68
68
  "@types/node": "^25.5.0",
69
69
  "@types/picomatch": "^4.0.2",
70
- "@typescript-eslint/eslint-plugin": "^8.57.0",
70
+ "@typescript-eslint/eslint-plugin": "^8.57.1",
71
71
  "@vitest/coverage-v8": "^4.1.0",
72
72
  "eslint": "^10.0.3",
73
73
  "eslint-config-prettier": "^10.1.8",