viberadar 0.3.201 → 0.3.202

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.
@@ -4707,9 +4707,10 @@ function renderObsGlobalDetail(c, filterFeatureKey) {
4707
4707
  }).join('') || '<div class="obs-sub">Шумных паттернов не обнаружено</div>';
4708
4708
 
4709
4709
  // ── Missing critical logs (V2: grouped by risk tier with failure points) ──
4710
- const v2Data = missingV2.filter(m => m.failurePoints.length > 0);
4711
- window.__obsMissingV2 = v2Data;
4712
- const fpTypeLabels = {
4710
+ const v2Data = missingV2.filter(m => m.failurePoints.length > 0);
4711
+ window.__obsMissingV2 = v2Data;
4712
+ window.__obsMissingGroupMeta = {};
4713
+ const fpTypeLabels = {
4713
4714
  'empty-catch':'пустой catch','catch-no-log':'catch без лога','promise-catch-no-log':'.catch без лога',
4714
4715
  'http-no-error-handling':'HTTP без обработки','db-no-error-handling':'DB без обработки',
4715
4716
  'throw-no-log':'throw без лога','error-check-no-log':'if(err) без лога',
@@ -4719,11 +4720,19 @@ function renderObsGlobalDetail(c, filterFeatureKey) {
4719
4720
  const tierLabels = { critical:'Критичные', important:'Важные', normal:'Обычные' };
4720
4721
  const tiers = ['critical','important','normal'];
4721
4722
  const tierSections = tiers.map(tier => {
4722
- const items = v2Data.filter(m => m.riskTier === tier);
4723
- if (!items.length) return '';
4724
- const groupId = 'missing-' + tier;
4725
- const expandBtn = hasAgent ? `<button class="obs-expand-btn" onclick="event.stopPropagation();toggleObsDetail('${groupId}')">▼</button>` : '';
4726
- const totalFPs = items.reduce((s,m) => s + m.failurePoints.length, 0);
4723
+ const items = v2Data.filter(m => m.riskTier === tier);
4724
+ if (!items.length) return '';
4725
+ const groupId = 'missing-' + tier;
4726
+ const groupIndices = items
4727
+ .map(m => (o.missingCriticalLogsV2 || []).indexOf(m))
4728
+ .filter(idx => idx >= 0);
4729
+ window.__obsMissingGroupMeta[groupId] = {
4730
+ missingLogIndices: groupIndices,
4731
+ recommendationType: 'add event',
4732
+ };
4733
+ const expandBtn = hasAgent ? `<button class="obs-expand-btn" onclick="event.stopPropagation();toggleObsDetail('${groupId}')">▼</button>` : '';
4734
+ const copyGroupBtn = `<button class="obs-copy-btn" title="Скопировать prompt для всей группы" onclick="event.stopPropagation();obsCopyPrompt('obs-fix-selected',window.__obsMissingGroupMeta['${groupId}'])">📋 Промпт</button>`;
4735
+ const totalFPs = items.reduce((s,m) => s + m.failurePoints.length, 0);
4727
4736
  const detailItems = items.map(m => {
4728
4737
  const globalIdx = (o.missingCriticalLogsV2 || []).indexOf(m); // index in full unfiltered array — must match server's missingCriticalLogsV2
4729
4738
  const fpCount = m.failurePoints.length;
@@ -4753,10 +4762,11 @@ function renderObsGlobalDetail(c, filterFeatureKey) {
4753
4762
  const _blTier = _bl?.tiers?.[tier];
4754
4763
  return `<div class="obs-tier-group">
4755
4764
  <div class="obs-tier-group-header">
4756
- <span class="obs-tier-badge obs-tier-${tier}">${tierLabels[tier]}</span>
4757
- <span>${items.length}${_d(items.length, _blTier?.modules)} модулей, ${totalFPs}${_d(totalFPs, _blTier?.fps)} точек отказа</span>
4758
- ${expandBtn}
4759
- </div>
4765
+ <span class="obs-tier-badge obs-tier-${tier}">${tierLabels[tier]}</span>
4766
+ <span>${items.length}${_d(items.length, _blTier?.modules)} модулей, ${totalFPs}${_d(totalFPs, _blTier?.fps)} точек отказа</span>
4767
+ ${copyGroupBtn}
4768
+ ${expandBtn}
4769
+ </div>
4760
4770
  ${detail}
4761
4771
  </div>`;
4762
4772
  }).join('');
@@ -4765,12 +4775,18 @@ function renderObsGlobalDetail(c, filterFeatureKey) {
4765
4775
  missingSection = '<div class="obs-sub" style="color:var(--green)">Критичные сценарии покрыты</div>';
4766
4776
  }
4767
4777
 
4768
- const fieldGaps = fieldGapsData;
4769
- const fieldGapEntries = Object.entries(fieldGaps).filter(([,v]) => v > 0).sort((a,b) => b[1] - a[1]);
4770
- const fieldGapRows = fieldGapEntries.map(([name, count]) => {
4771
- const groupId = 'field-' + name.replace(/[^a-z0-9]/gi, '_');
4772
- const affectedItems = catalog.filter(c => (c.missingFields||[]).includes(name));
4773
- const expandBtn = hasAgent ? `<button class="obs-expand-btn" onclick="event.stopPropagation();toggleObsDetail('${groupId}')">▼</button>` : '';
4778
+ const fieldGaps = fieldGapsData;
4779
+ window.__obsFieldGroupMeta = {};
4780
+ const fieldGapEntries = Object.entries(fieldGaps).filter(([,v]) => v > 0).sort((a,b) => b[1] - a[1]);
4781
+ const fieldGapRows = fieldGapEntries.map(([name, count]) => {
4782
+ const groupId = 'field-' + name.replace(/[^a-z0-9]/gi, '_');
4783
+ const affectedItems = catalog.filter(c => (c.missingFields||[]).includes(name));
4784
+ window.__obsFieldGroupMeta[groupId] = {
4785
+ fieldName: name,
4786
+ catalogPaths: affectedItems.map(c => c.modulePath),
4787
+ };
4788
+ const expandBtn = hasAgent ? `<button class="obs-expand-btn" onclick="event.stopPropagation();toggleObsDetail('${groupId}')">▼</button>` : '';
4789
+ const copyFieldBtn = `<button class="obs-copy-btn" title="Скопировать prompt для всех модулей с этим полем" onclick="event.stopPropagation();obsCopyPrompt('obs-fix-selected',window.__obsFieldGroupMeta['${groupId}'])">📋 Промпт</button>`;
4774
4790
  const detailItems = affectedItems.map(ci => {
4775
4791
  const catIdx = catalog.indexOf(ci);
4776
4792
  const otherMissing = (ci.missingFields||[]).filter(f => f !== name);
@@ -4789,10 +4805,11 @@ function renderObsGlobalDetail(c, filterFeatureKey) {
4789
4805
  </div>` : '';
4790
4806
  return `<div>
4791
4807
  <div class="obs-list-item">
4792
- <span><code>${escapeHtml(name)}</code></span>
4793
- <strong style="color:${count > 20 ? 'var(--red)' : count > 5 ? 'var(--yellow)' : 'var(--muted)'}">${count} пропусков в ${affectedItems.length} модул${affectedItems.length === 1 ? 'е' : affectedItems.length < 5 ? 'ях' : 'ях'}</strong>
4794
- ${expandBtn}
4795
- </div>
4808
+ <span><code>${escapeHtml(name)}</code></span>
4809
+ <strong style="color:${count > 20 ? 'var(--red)' : count > 5 ? 'var(--yellow)' : 'var(--muted)'}">${count} пропусков в ${affectedItems.length} модул${affectedItems.length === 1 ? 'е' : affectedItems.length < 5 ? 'ях' : 'ях'}</strong>
4810
+ ${copyFieldBtn}
4811
+ ${expandBtn}
4812
+ </div>
4796
4813
  ${detail}
4797
4814
  </div>`;
4798
4815
  }).join('') || '<div class="obs-sub" style="color:var(--green)">Все обязательные поля на месте</div>';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viberadar",
3
- "version": "0.3.201",
3
+ "version": "0.3.202",
4
4
  "description": "Live module map with test coverage for vibecoding projects",
5
5
  "main": "./dist/cli.js",
6
6
  "bin": {