@principal-ade/code-quality-panels 0.1.24 → 0.1.26

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.
@@ -356,6 +356,529 @@ var useTheme = () => {
356
356
  }
357
357
  return context;
358
358
  };
359
+ var LENS_REGISTRY = [
360
+ // ============================================================
361
+ // LINTING - Code style and bug detection
362
+ // ============================================================
363
+ // TypeScript/JavaScript linting
364
+ {
365
+ id: "eslint",
366
+ name: "ESLint",
367
+ category: "linting",
368
+ languages: ["typescript", "javascript"],
369
+ outputsFileMetrics: true,
370
+ outputsAggregate: true,
371
+ colorScheme: "issues",
372
+ description: "Pluggable linting utility for JavaScript and TypeScript",
373
+ command: "eslint",
374
+ fileMetricsRequirements: {
375
+ completeCommand: "npx eslint . --format json",
376
+ requiredFlags: ["--format json"],
377
+ formatFlag: "--format json",
378
+ withoutConfig: "Only files with issues are reported in the output",
379
+ fallbackStrategy: "source-file-count",
380
+ nativelyComplete: false,
381
+ notes: "ESLint JSON output only includes files that have issues. Clean files are not listed."
382
+ }
383
+ },
384
+ {
385
+ id: "biome-lint",
386
+ name: "Biome Lint",
387
+ category: "linting",
388
+ languages: ["typescript", "javascript"],
389
+ alternativeTo: ["eslint"],
390
+ outputsFileMetrics: true,
391
+ outputsAggregate: true,
392
+ colorScheme: "issues",
393
+ description: "Fast linter for JavaScript and TypeScript",
394
+ command: "biome lint",
395
+ fileMetricsRequirements: {
396
+ completeCommand: "npx @biomejs/biome lint . --reporter=json",
397
+ requiredFlags: ["--reporter=json"],
398
+ formatFlag: "--reporter=json",
399
+ withoutConfig: "Output cannot be parsed for file metrics",
400
+ fallbackStrategy: "source-file-count",
401
+ nativelyComplete: true,
402
+ notes: "Biome JSON output includes all analyzed files."
403
+ }
404
+ },
405
+ {
406
+ id: "oxlint",
407
+ name: "OxLint",
408
+ category: "linting",
409
+ languages: ["typescript", "javascript"],
410
+ alternativeTo: ["eslint", "biome-lint"],
411
+ outputsFileMetrics: true,
412
+ outputsAggregate: true,
413
+ colorScheme: "issues",
414
+ description: "Blazing fast JavaScript/TypeScript linter",
415
+ command: "oxlint",
416
+ fileMetricsRequirements: {
417
+ completeCommand: "npx oxlint --format json",
418
+ requiredFlags: ["--format json"],
419
+ formatFlag: "--format json",
420
+ withoutConfig: "Output cannot be parsed for file metrics",
421
+ fallbackStrategy: "source-file-count",
422
+ nativelyComplete: false
423
+ }
424
+ },
425
+ // Python linting
426
+ {
427
+ id: "ruff",
428
+ name: "Ruff",
429
+ category: "linting",
430
+ languages: ["python"],
431
+ alternativeTo: ["pylint", "flake8"],
432
+ outputsFileMetrics: true,
433
+ outputsAggregate: true,
434
+ colorScheme: "issues",
435
+ description: "Extremely fast Python linter",
436
+ command: "ruff check",
437
+ fileMetricsRequirements: {
438
+ completeCommand: "ruff check . --output-format=json",
439
+ requiredFlags: ["--output-format=json"],
440
+ formatFlag: "--output-format=json",
441
+ withoutConfig: "Output cannot be parsed for file metrics",
442
+ fallbackStrategy: "source-file-count",
443
+ nativelyComplete: false,
444
+ notes: "Ruff JSON output only includes files with issues."
445
+ }
446
+ },
447
+ {
448
+ id: "pylint",
449
+ name: "Pylint",
450
+ category: "linting",
451
+ languages: ["python"],
452
+ outputsFileMetrics: true,
453
+ outputsAggregate: true,
454
+ colorScheme: "issues",
455
+ description: "Python static code analyzer",
456
+ command: "pylint",
457
+ fileMetricsRequirements: {
458
+ completeCommand: "pylint --output-format=json .",
459
+ requiredFlags: ["--output-format=json"],
460
+ formatFlag: "--output-format=json",
461
+ withoutConfig: "Output cannot be parsed for file metrics",
462
+ fallbackStrategy: "source-file-count",
463
+ nativelyComplete: false
464
+ }
465
+ },
466
+ // Go linting
467
+ {
468
+ id: "golangci-lint",
469
+ name: "golangci-lint",
470
+ category: "linting",
471
+ languages: ["go"],
472
+ outputsFileMetrics: true,
473
+ outputsAggregate: true,
474
+ colorScheme: "issues",
475
+ description: "Fast linters runner for Go",
476
+ command: "golangci-lint run"
477
+ },
478
+ // Rust linting
479
+ {
480
+ id: "clippy",
481
+ name: "Clippy",
482
+ category: "linting",
483
+ languages: ["rust"],
484
+ outputsFileMetrics: true,
485
+ outputsAggregate: true,
486
+ colorScheme: "issues",
487
+ description: "Rust linter",
488
+ command: "cargo clippy"
489
+ },
490
+ // ============================================================
491
+ // FORMATTING - Code formatting
492
+ // ============================================================
493
+ // TypeScript/JavaScript formatting
494
+ {
495
+ id: "prettier",
496
+ name: "Prettier",
497
+ category: "formatting",
498
+ languages: ["typescript", "javascript"],
499
+ outputsFileMetrics: true,
500
+ outputsAggregate: true,
501
+ colorScheme: "binary",
502
+ description: "Opinionated code formatter",
503
+ command: "prettier --check",
504
+ fileMetricsRequirements: {
505
+ completeCommand: "npx prettier --check . --no-error-on-unmatched-pattern --log-level debug",
506
+ requiredFlags: ["--check", "--log-level debug"],
507
+ withoutConfig: "Without --log-level debug, file list is not available",
508
+ fallbackStrategy: "source-file-count",
509
+ nativelyComplete: true,
510
+ notes: "Prettier with --log-level debug lists all checked files. This is the reference implementation for complete file metrics."
511
+ }
512
+ },
513
+ {
514
+ id: "biome-format",
515
+ name: "Biome Format",
516
+ category: "formatting",
517
+ languages: ["typescript", "javascript"],
518
+ alternativeTo: ["prettier"],
519
+ outputsFileMetrics: true,
520
+ outputsAggregate: true,
521
+ colorScheme: "binary",
522
+ description: "Fast code formatter for JavaScript and TypeScript",
523
+ command: "biome format",
524
+ fileMetricsRequirements: {
525
+ completeCommand: "npx @biomejs/biome format . --reporter=json",
526
+ requiredFlags: ["--reporter=json"],
527
+ formatFlag: "--reporter=json",
528
+ withoutConfig: "Output cannot be parsed for file metrics",
529
+ fallbackStrategy: "source-file-count",
530
+ nativelyComplete: true
531
+ }
532
+ },
533
+ // Python formatting
534
+ {
535
+ id: "black",
536
+ name: "Black",
537
+ category: "formatting",
538
+ languages: ["python"],
539
+ outputsFileMetrics: true,
540
+ outputsAggregate: true,
541
+ colorScheme: "binary",
542
+ description: "The uncompromising Python code formatter",
543
+ command: "black --check"
544
+ },
545
+ {
546
+ id: "ruff-format",
547
+ name: "Ruff Format",
548
+ category: "formatting",
549
+ languages: ["python"],
550
+ alternativeTo: ["black"],
551
+ outputsFileMetrics: true,
552
+ outputsAggregate: true,
553
+ colorScheme: "binary",
554
+ description: "Fast Python formatter (Ruff)",
555
+ command: "ruff format --check"
556
+ },
557
+ // Go formatting
558
+ {
559
+ id: "gofmt",
560
+ name: "gofmt",
561
+ category: "formatting",
562
+ languages: ["go"],
563
+ outputsFileMetrics: true,
564
+ outputsAggregate: true,
565
+ colorScheme: "binary",
566
+ description: "Go code formatter",
567
+ command: "gofmt -l"
568
+ },
569
+ // Rust formatting
570
+ {
571
+ id: "rustfmt",
572
+ name: "rustfmt",
573
+ category: "formatting",
574
+ languages: ["rust"],
575
+ outputsFileMetrics: true,
576
+ outputsAggregate: true,
577
+ colorScheme: "binary",
578
+ description: "Rust code formatter",
579
+ command: "cargo fmt --check"
580
+ },
581
+ // ============================================================
582
+ // TYPES - Type checking
583
+ // ============================================================
584
+ // TypeScript
585
+ {
586
+ id: "typescript",
587
+ name: "TypeScript",
588
+ category: "types",
589
+ languages: ["typescript"],
590
+ outputsFileMetrics: true,
591
+ outputsAggregate: true,
592
+ colorScheme: "issues",
593
+ description: "TypeScript type checker",
594
+ command: "tsc --noEmit",
595
+ fileMetricsRequirements: {
596
+ completeCommand: "npx tsc --noEmit --listFiles",
597
+ requiredFlags: ["--listFiles"],
598
+ withoutConfig: "Only files with type errors are reported. Cannot determine total files analyzed.",
599
+ fallbackStrategy: "source-file-count",
600
+ nativelyComplete: false,
601
+ notes: "The --listFiles flag is REQUIRED to get the complete list of files TypeScript analyzed. Without it, only files with errors appear in output."
602
+ }
603
+ },
604
+ // Python type checking
605
+ {
606
+ id: "mypy",
607
+ name: "MyPy",
608
+ category: "types",
609
+ languages: ["python"],
610
+ outputsFileMetrics: true,
611
+ outputsAggregate: true,
612
+ colorScheme: "issues",
613
+ description: "Static type checker for Python",
614
+ command: "mypy"
615
+ },
616
+ {
617
+ id: "pyright",
618
+ name: "Pyright",
619
+ category: "types",
620
+ languages: ["python"],
621
+ alternativeTo: ["mypy"],
622
+ outputsFileMetrics: true,
623
+ outputsAggregate: true,
624
+ colorScheme: "issues",
625
+ description: "Static type checker for Python",
626
+ command: "pyright"
627
+ },
628
+ // Go type checking (built into compiler)
629
+ {
630
+ id: "go-vet",
631
+ name: "Go Vet",
632
+ category: "types",
633
+ languages: ["go"],
634
+ outputsFileMetrics: true,
635
+ outputsAggregate: true,
636
+ colorScheme: "issues",
637
+ description: "Go static analyzer",
638
+ command: "go vet"
639
+ },
640
+ // ============================================================
641
+ // TESTS - Test coverage and results
642
+ // ============================================================
643
+ // JavaScript/TypeScript testing
644
+ {
645
+ id: "jest",
646
+ name: "Jest",
647
+ category: "tests",
648
+ languages: ["typescript", "javascript"],
649
+ outputsFileMetrics: true,
650
+ outputsAggregate: true,
651
+ colorScheme: "coverage",
652
+ description: "JavaScript testing framework",
653
+ command: "jest --coverage",
654
+ fileMetricsRequirements: {
655
+ completeCommand: "npx jest --coverage --json --outputFile=jest-results.json",
656
+ requiredFlags: ["--coverage", "--json"],
657
+ formatFlag: "--json",
658
+ withoutConfig: "Without --coverage, no per-file coverage data is available. Without --json, output cannot be parsed.",
659
+ fallbackStrategy: "coverage-only",
660
+ nativelyComplete: false,
661
+ notes: "Jest coverage data provides per-file metrics for source files. Test file results are separate from source coverage."
662
+ }
663
+ },
664
+ {
665
+ id: "vitest",
666
+ name: "Vitest",
667
+ category: "tests",
668
+ languages: ["typescript", "javascript"],
669
+ alternativeTo: ["jest"],
670
+ outputsFileMetrics: true,
671
+ outputsAggregate: true,
672
+ colorScheme: "coverage",
673
+ description: "Vite-native testing framework",
674
+ command: "vitest run --coverage",
675
+ fileMetricsRequirements: {
676
+ completeCommand: "npx vitest run --coverage --reporter=json",
677
+ requiredFlags: ["--coverage", "--reporter=json"],
678
+ formatFlag: "--reporter=json",
679
+ withoutConfig: "Without --coverage, no per-file coverage data is available",
680
+ fallbackStrategy: "coverage-only",
681
+ nativelyComplete: false,
682
+ notes: "Vitest coverage provides per-file metrics. Requires @vitest/coverage-v8 or @vitest/coverage-istanbul."
683
+ }
684
+ },
685
+ {
686
+ id: "bun-test",
687
+ name: "Bun Test",
688
+ category: "tests",
689
+ languages: ["typescript", "javascript"],
690
+ alternativeTo: ["jest", "vitest"],
691
+ outputsFileMetrics: true,
692
+ outputsAggregate: true,
693
+ colorScheme: "coverage",
694
+ description: "Bun native test runner",
695
+ command: "bun test",
696
+ fileMetricsRequirements: {
697
+ completeCommand: "bun test --coverage",
698
+ requiredFlags: ["--coverage"],
699
+ withoutConfig: "Without --coverage, no per-file coverage data is available",
700
+ fallbackStrategy: "coverage-only",
701
+ nativelyComplete: false,
702
+ notes: "Bun test coverage is built-in but requires --coverage flag."
703
+ }
704
+ },
705
+ // Python testing
706
+ {
707
+ id: "pytest",
708
+ name: "Pytest",
709
+ category: "tests",
710
+ languages: ["python"],
711
+ outputsFileMetrics: true,
712
+ outputsAggregate: true,
713
+ colorScheme: "coverage",
714
+ description: "Python testing framework",
715
+ command: "pytest --cov"
716
+ },
717
+ // Go testing
718
+ {
719
+ id: "go-test",
720
+ name: "Go Test",
721
+ category: "tests",
722
+ languages: ["go"],
723
+ outputsFileMetrics: true,
724
+ outputsAggregate: true,
725
+ colorScheme: "coverage",
726
+ description: "Go test runner",
727
+ command: "go test -cover"
728
+ },
729
+ // Rust testing
730
+ {
731
+ id: "cargo-test",
732
+ name: "Cargo Test",
733
+ category: "tests",
734
+ languages: ["rust"],
735
+ outputsFileMetrics: true,
736
+ outputsAggregate: true,
737
+ colorScheme: "coverage",
738
+ description: "Rust test runner",
739
+ command: "cargo test"
740
+ },
741
+ // ============================================================
742
+ // DEAD CODE - Unused code detection
743
+ // ============================================================
744
+ // TypeScript/JavaScript
745
+ {
746
+ id: "knip",
747
+ name: "Knip",
748
+ category: "dead-code",
749
+ languages: ["typescript", "javascript"],
750
+ outputsFileMetrics: true,
751
+ outputsAggregate: true,
752
+ colorScheme: "issues",
753
+ description: "Find unused files, dependencies and exports",
754
+ command: "knip",
755
+ fileMetricsRequirements: {
756
+ completeCommand: "npx knip --reporter json",
757
+ requiredFlags: ["--reporter json"],
758
+ formatFlag: "--reporter json",
759
+ withoutConfig: "Output cannot be parsed for file metrics",
760
+ fallbackStrategy: "source-file-count",
761
+ nativelyComplete: false,
762
+ notes: "Knip reports unused files and exports. It does not list all analyzed files, only those with issues."
763
+ }
764
+ },
765
+ // Python
766
+ {
767
+ id: "vulture",
768
+ name: "Vulture",
769
+ category: "dead-code",
770
+ languages: ["python"],
771
+ outputsFileMetrics: true,
772
+ outputsAggregate: true,
773
+ colorScheme: "issues",
774
+ description: "Find dead Python code",
775
+ command: "vulture"
776
+ },
777
+ // ============================================================
778
+ // DOCUMENTATION - Documentation coverage
779
+ // ============================================================
780
+ {
781
+ id: "alexandria",
782
+ name: "Alexandria",
783
+ category: "documentation",
784
+ languages: ["typescript", "javascript"],
785
+ outputsFileMetrics: true,
786
+ outputsAggregate: true,
787
+ colorScheme: "binary",
788
+ description: "Documentation coverage checker",
789
+ command: "alexandria lint",
790
+ fileMetricsRequirements: {
791
+ completeCommand: "npx @principal-ai/alexandria-cli coverage --json",
792
+ requiredFlags: ["--json"],
793
+ formatFlag: "--json",
794
+ withoutConfig: "Output cannot be parsed for file metrics",
795
+ fallbackStrategy: "source-file-count",
796
+ nativelyComplete: true,
797
+ notes: "Alexandria reports documentation coverage for all analyzed files."
798
+ }
799
+ },
800
+ {
801
+ id: "typedoc",
802
+ name: "TypeDoc",
803
+ category: "documentation",
804
+ languages: ["typescript"],
805
+ outputsFileMetrics: false,
806
+ outputsAggregate: true,
807
+ colorScheme: "coverage",
808
+ description: "TypeScript documentation generator",
809
+ command: "typedoc"
810
+ },
811
+ // ============================================================
812
+ // SECURITY - Security scanning
813
+ // ============================================================
814
+ {
815
+ id: "npm-audit",
816
+ name: "npm audit",
817
+ category: "security",
818
+ languages: ["typescript", "javascript"],
819
+ outputsFileMetrics: false,
820
+ outputsAggregate: true,
821
+ colorScheme: "issues",
822
+ description: "Check for known vulnerabilities in dependencies",
823
+ command: "npm audit"
824
+ },
825
+ {
826
+ id: "bandit",
827
+ name: "Bandit",
828
+ category: "security",
829
+ languages: ["python"],
830
+ outputsFileMetrics: true,
831
+ outputsAggregate: true,
832
+ colorScheme: "issues",
833
+ description: "Python security linter",
834
+ command: "bandit -r"
835
+ }
836
+ ];
837
+ function getLensById(id) {
838
+ return LENS_REGISTRY.find((lens) => lens.id === id);
839
+ }
840
+ function getLensesByCategory(category) {
841
+ return LENS_REGISTRY.filter((lens) => lens.category === category);
842
+ }
843
+ function getCategoryForLens(lensId) {
844
+ var _a;
845
+ return (_a = getLensById(lensId)) == null ? void 0 : _a.category;
846
+ }
847
+ function getColorModeForCategory(category, lensesRan) {
848
+ const lensesInCategory = getLensesByCategory(category);
849
+ for (const lensId of lensesRan) {
850
+ if (lensesInCategory.some((lens) => lens.id === lensId)) {
851
+ return lensId;
852
+ }
853
+ }
854
+ return null;
855
+ }
856
+ var HEXAGON_METRIC_TO_CATEGORY = {
857
+ linting: "linting",
858
+ formatting: "formatting",
859
+ types: "types",
860
+ tests: "tests",
861
+ deadCode: "dead-code",
862
+ documentation: "documentation"
863
+ };
864
+ function isLensInHexagonMetric(lensId, metric) {
865
+ const lensCategory = getCategoryForLens(lensId);
866
+ if (!lensCategory) return false;
867
+ return HEXAGON_METRIC_TO_CATEGORY[metric] === lensCategory;
868
+ }
869
+ function getColorModeForHexagonMetric(metric, lensesRan) {
870
+ const category = HEXAGON_METRIC_TO_CATEGORY[metric];
871
+ return getColorModeForCategory(category, lensesRan);
872
+ }
873
+ function isHexagonMetricConfigured(metric, lensesRan) {
874
+ if (lensesRan === void 0) {
875
+ return true;
876
+ }
877
+ if (lensesRan.length === 0) {
878
+ return false;
879
+ }
880
+ return lensesRan.some((lensId) => isLensInHexagonMetric(lensId, metric));
881
+ }
359
882
  function r(e) {
360
883
  var t, f, n = "";
361
884
  if ("string" == typeof e || "number" == typeof e) n += e;
@@ -426,42 +949,8 @@ function getValueColor$2(value, key) {
426
949
  if (effectiveValue >= 60) return "#E6A700";
427
950
  return "#C62828";
428
951
  }
429
- const LENS_TO_METRIC_MAP = {
430
- // Linting tools
431
- eslint: "linting",
432
- biome: "linting",
433
- "biome-lint": "linting",
434
- oxlint: "linting",
435
- // Type checking tools
436
- typescript: "types",
437
- flow: "types",
438
- // Testing tools
439
- test: "tests",
440
- jest: "tests",
441
- vitest: "tests",
442
- "bun-test": "tests",
443
- mocha: "tests",
444
- playwright: "tests",
445
- cypress: "tests",
446
- // Formatting tools
447
- prettier: "formatting",
448
- "biome-format": "formatting",
449
- // Dead code detection
450
- knip: "deadCode",
451
- depcheck: "deadCode",
452
- // Documentation
453
- typedoc: "documentation",
454
- jsdoc: "documentation",
455
- alexandria: "documentation"
456
- };
457
952
  function isMetricConfigured(metricKey, lensesRan) {
458
- if (lensesRan === void 0) {
459
- return true;
460
- }
461
- if (lensesRan.length === 0) {
462
- return false;
463
- }
464
- return lensesRan.some((lensId) => LENS_TO_METRIC_MAP[lensId] === metricKey);
953
+ return isHexagonMetricConfigured(metricKey, lensesRan);
465
954
  }
466
955
  const getMetricConfig = (themeColors) => [
467
956
  {
@@ -1166,7 +1655,10 @@ function QualityHexagonExpandable({
1166
1655
  },
1167
1656
  children: metricConfig.map(({ key, label }) => {
1168
1657
  const value = metrics[key];
1169
- const configured = isMetricConfigured(key, lensesRan);
1658
+ const configured = isMetricConfigured(
1659
+ key,
1660
+ lensesRan
1661
+ );
1170
1662
  return /* @__PURE__ */ jsxs(
1171
1663
  "div",
1172
1664
  {
@@ -1790,29 +2282,28 @@ const mockPackages = [
1790
2282
  }
1791
2283
  }
1792
2284
  ];
2285
+ const LEGACY_FALLBACKS = {
2286
+ linting: "eslint",
2287
+ formatting: "prettier",
2288
+ types: "typescript",
2289
+ tests: "coverage",
2290
+ deadCode: "knip",
2291
+ documentation: "alexandria"
2292
+ };
1793
2293
  function getColorModeForMetric(metric, lensesRan) {
1794
- const staticMappings = {
1795
- types: "typescript",
1796
- documentation: "alexandria",
1797
- tests: "coverage",
1798
- deadCode: "knip"
1799
- };
1800
- if (staticMappings[metric]) {
1801
- return staticMappings[metric];
1802
- }
1803
- if (metric === "linting") {
1804
- if ((lensesRan == null ? void 0 : lensesRan.includes("biome-lint")) || (lensesRan == null ? void 0 : lensesRan.includes("biome"))) {
1805
- return "biome-lint";
1806
- }
1807
- return "eslint";
1808
- }
1809
- if (metric === "formatting") {
1810
- if (lensesRan == null ? void 0 : lensesRan.includes("biome-format")) {
1811
- return "biome-format";
1812
- }
1813
- return "prettier";
2294
+ if (!isValidHexagonMetric(metric)) return null;
2295
+ const hexagonMetric = metric;
2296
+ const colorMode = getColorModeForHexagonMetric(
2297
+ hexagonMetric,
2298
+ lensesRan ?? []
2299
+ );
2300
+ if (!colorMode && lensesRan === void 0) {
2301
+ return LEGACY_FALLBACKS[hexagonMetric] ?? null;
1814
2302
  }
1815
- return null;
2303
+ return colorMode;
2304
+ }
2305
+ function isValidHexagonMetric(metric) {
2306
+ return metric in LEGACY_FALLBACKS;
1816
2307
  }
1817
2308
  const QualityHexagonPanelContent = ({
1818
2309
  context,
@@ -2039,7 +2530,10 @@ const QualityHexagonPanelContent = ({
2039
2530
  });
2040
2531
  },
2041
2532
  onMetricClick: (metric) => {
2042
- const colorMode = getColorModeForMetric(metric, pkg.lensesRan);
2533
+ const colorMode = getColorModeForMetric(
2534
+ metric,
2535
+ pkg.lensesRan
2536
+ );
2043
2537
  if (colorMode) {
2044
2538
  events.emit({
2045
2539
  type: "quality:colorMode:select",
@@ -2862,10 +3356,25 @@ function LensDataDebugPanel$1({
2862
3356
  const [expandedFiles, setExpandedFiles] = React2.useState(
2863
3357
  /* @__PURE__ */ new Set()
2864
3358
  );
3359
+ const [expandedAnalyzedFiles, setExpandedAnalyzedFiles] = React2.useState(/* @__PURE__ */ new Set());
3360
+ React2.useEffect(() => {
3361
+ if (initialSelectedPackage) {
3362
+ setSelectedPackage((current) => {
3363
+ if (current !== initialSelectedPackage) {
3364
+ setExpandedLens(null);
3365
+ setExpandedFiles(/* @__PURE__ */ new Set());
3366
+ setExpandedAnalyzedFiles(/* @__PURE__ */ new Set());
3367
+ return initialSelectedPackage;
3368
+ }
3369
+ return current;
3370
+ });
3371
+ }
3372
+ }, [initialSelectedPackage]);
2865
3373
  const handlePackageSelect = (pkg) => {
2866
3374
  setSelectedPackage(pkg);
2867
3375
  setExpandedLens(null);
2868
3376
  setExpandedFiles(/* @__PURE__ */ new Set());
3377
+ setExpandedAnalyzedFiles(/* @__PURE__ */ new Set());
2869
3378
  onPackageSelect == null ? void 0 : onPackageSelect(pkg);
2870
3379
  };
2871
3380
  const toggleLens = (lensId) => {
@@ -2881,6 +3390,15 @@ function LensDataDebugPanel$1({
2881
3390
  }
2882
3391
  setExpandedFiles(newSet);
2883
3392
  };
3393
+ const toggleAnalyzedFiles = (lensKey) => {
3394
+ const newSet = new Set(expandedAnalyzedFiles);
3395
+ if (newSet.has(lensKey)) {
3396
+ newSet.delete(lensKey);
3397
+ } else {
3398
+ newSet.add(lensKey);
3399
+ }
3400
+ setExpandedAnalyzedFiles(newSet);
3401
+ };
2884
3402
  const selectedResults = selectedPackage ? packageGroups.get(selectedPackage) || [] : [];
2885
3403
  return /* @__PURE__ */ jsxs(
2886
3404
  "div",
@@ -2896,62 +3414,7 @@ function LensDataDebugPanel$1({
2896
3414
  fontSize: 13
2897
3415
  },
2898
3416
  children: [
2899
- packageNames.length > 1 && /* @__PURE__ */ jsx(
2900
- "div",
2901
- {
2902
- style: {
2903
- display: "flex",
2904
- flexWrap: "wrap",
2905
- gap: 8,
2906
- marginBottom: 8
2907
- },
2908
- children: packageNames.map((pkg) => {
2909
- const summary = getPackageSummary(packageGroups.get(pkg) || []);
2910
- const isSelected = selectedPackage === pkg;
2911
- return /* @__PURE__ */ jsxs(
2912
- "button",
2913
- {
2914
- onClick: () => handlePackageSelect(pkg),
2915
- style: {
2916
- display: "flex",
2917
- alignItems: "center",
2918
- gap: 8,
2919
- padding: "8px 12px",
2920
- borderRadius: 6,
2921
- border: `1px solid ${isSelected ? theme.colors.primary : theme.colors.border}`,
2922
- backgroundColor: isSelected ? theme.colors.surface : "transparent",
2923
- color: theme.colors.text,
2924
- cursor: "pointer",
2925
- transition: "all 0.15s ease"
2926
- },
2927
- children: [
2928
- /* @__PURE__ */ jsx("span", { style: { fontWeight: 500 }, children: pkg }),
2929
- /* @__PURE__ */ jsxs(
2930
- "span",
2931
- {
2932
- style: {
2933
- fontSize: 11,
2934
- padding: "2px 6px",
2935
- borderRadius: 4,
2936
- backgroundColor: summary.totalErrors > 0 ? getSeverityBg("error") : summary.totalWarnings > 0 ? getSeverityBg("warning") : "rgba(34, 197, 94, 0.1)",
2937
- color: summary.totalErrors > 0 ? getSeverityColor("error") : summary.totalWarnings > 0 ? getSeverityColor("warning") : "#22c55e"
2938
- },
2939
- children: [
2940
- summary.passCount,
2941
- "/",
2942
- summary.lensCount,
2943
- " pass"
2944
- ]
2945
- }
2946
- )
2947
- ]
2948
- },
2949
- pkg
2950
- );
2951
- })
2952
- }
2953
- ),
2954
- packageNames.length === 1 && /* @__PURE__ */ jsxs(
3417
+ selectedPackage && !onPackageSelect && /* @__PURE__ */ jsxs(
2955
3418
  "div",
2956
3419
  {
2957
3420
  style: {
@@ -2964,7 +3427,26 @@ function LensDataDebugPanel$1({
2964
3427
  marginBottom: 8
2965
3428
  },
2966
3429
  children: [
2967
- /* @__PURE__ */ jsx("span", { style: { fontWeight: 600, color: theme.colors.text }, children: packageNames[0] }),
3430
+ packageNames.length > 1 ? /* @__PURE__ */ jsx(
3431
+ "select",
3432
+ {
3433
+ value: selectedPackage,
3434
+ onChange: (e) => handlePackageSelect(e.target.value),
3435
+ style: {
3436
+ padding: "4px 8px",
3437
+ borderRadius: 4,
3438
+ border: `1px solid ${theme.colors.border}`,
3439
+ backgroundColor: theme.colors.background,
3440
+ color: theme.colors.text,
3441
+ fontFamily: "monospace",
3442
+ fontSize: 13,
3443
+ fontWeight: 600,
3444
+ cursor: "pointer",
3445
+ outline: "none"
3446
+ },
3447
+ children: packageNames.map((pkg) => /* @__PURE__ */ jsx("option", { value: pkg, children: pkg }, pkg))
3448
+ }
3449
+ ) : /* @__PURE__ */ jsx("span", { style: { fontWeight: 600, color: theme.colors.text }, children: selectedPackage }),
2968
3450
  (() => {
2969
3451
  const summary = getPackageSummary(selectedResults);
2970
3452
  return /* @__PURE__ */ jsxs(
@@ -2989,21 +3471,8 @@ function LensDataDebugPanel$1({
2989
3471
  ]
2990
3472
  }
2991
3473
  ),
2992
- !selectedPackage && packageNames.length > 1 && /* @__PURE__ */ jsx(
2993
- "div",
2994
- {
2995
- style: {
2996
- padding: 24,
2997
- textAlign: "center",
2998
- color: theme.colors.textMuted,
2999
- backgroundColor: theme.colors.surface,
3000
- borderRadius: 6
3001
- },
3002
- children: "Select a package above to view lens results"
3003
- }
3004
- ),
3005
3474
  selectedPackage && selectedResults.map((result, idx) => {
3006
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
3475
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
3007
3476
  const lensKey = `${((_a = result.lens) == null ? void 0 : _a.id) ?? "unknown"}-${idx}`;
3008
3477
  const isExpanded = expandedLens === lensKey;
3009
3478
  const filesWithIssues = getFilesWithIssues(result.issues ?? []);
@@ -3226,6 +3695,149 @@ function LensDataDebugPanel$1({
3226
3695
  ]
3227
3696
  }
3228
3697
  ),
3698
+ result.coverage && /* @__PURE__ */ jsxs("div", { style: { marginTop: 16 }, children: [
3699
+ /* @__PURE__ */ jsx(
3700
+ "div",
3701
+ {
3702
+ style: {
3703
+ fontSize: 12,
3704
+ fontWeight: 600,
3705
+ color: theme.colors.textMuted,
3706
+ marginBottom: 8
3707
+ },
3708
+ children: "Coverage Data"
3709
+ }
3710
+ ),
3711
+ /* @__PURE__ */ jsxs(
3712
+ "div",
3713
+ {
3714
+ style: {
3715
+ padding: 12,
3716
+ backgroundColor: theme.colors.surface,
3717
+ borderRadius: 4
3718
+ },
3719
+ children: [
3720
+ /* @__PURE__ */ jsxs(
3721
+ "div",
3722
+ {
3723
+ style: {
3724
+ display: "grid",
3725
+ gridTemplateColumns: "repeat(auto-fit, minmax(100px, 1fr))",
3726
+ gap: 12,
3727
+ marginBottom: 12
3728
+ },
3729
+ children: [
3730
+ /* @__PURE__ */ jsxs("div", { children: [
3731
+ /* @__PURE__ */ jsx(
3732
+ "div",
3733
+ {
3734
+ style: {
3735
+ fontSize: 11,
3736
+ color: theme.colors.textMuted
3737
+ },
3738
+ children: "Line"
3739
+ }
3740
+ ),
3741
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: 14, fontWeight: 600 }, children: [
3742
+ result.coverage.line,
3743
+ "%"
3744
+ ] })
3745
+ ] }),
3746
+ result.coverage.branch !== void 0 && /* @__PURE__ */ jsxs("div", { children: [
3747
+ /* @__PURE__ */ jsx(
3748
+ "div",
3749
+ {
3750
+ style: {
3751
+ fontSize: 11,
3752
+ color: theme.colors.textMuted
3753
+ },
3754
+ children: "Branch"
3755
+ }
3756
+ ),
3757
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: 14, fontWeight: 600 }, children: [
3758
+ result.coverage.branch,
3759
+ "%"
3760
+ ] })
3761
+ ] }),
3762
+ result.coverage.function !== void 0 && /* @__PURE__ */ jsxs("div", { children: [
3763
+ /* @__PURE__ */ jsx(
3764
+ "div",
3765
+ {
3766
+ style: {
3767
+ fontSize: 11,
3768
+ color: theme.colors.textMuted
3769
+ },
3770
+ children: "Function"
3771
+ }
3772
+ ),
3773
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: 14, fontWeight: 600 }, children: [
3774
+ result.coverage.function,
3775
+ "%"
3776
+ ] })
3777
+ ] }),
3778
+ result.coverage.statement !== void 0 && /* @__PURE__ */ jsxs("div", { children: [
3779
+ /* @__PURE__ */ jsx(
3780
+ "div",
3781
+ {
3782
+ style: {
3783
+ fontSize: 11,
3784
+ color: theme.colors.textMuted
3785
+ },
3786
+ children: "Statement"
3787
+ }
3788
+ ),
3789
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: 14, fontWeight: 600 }, children: [
3790
+ result.coverage.statement,
3791
+ "%"
3792
+ ] })
3793
+ ] })
3794
+ ]
3795
+ }
3796
+ ),
3797
+ /* @__PURE__ */ jsxs(
3798
+ "div",
3799
+ {
3800
+ style: {
3801
+ padding: 8,
3802
+ borderRadius: 4,
3803
+ backgroundColor: ((_t = result.coverage.files) == null ? void 0 : _t.length) ? "rgba(34, 197, 94, 0.1)" : "rgba(239, 68, 68, 0.1)",
3804
+ border: `1px solid ${((_u = result.coverage.files) == null ? void 0 : _u.length) ? "#22c55e" : "#ef4444"}`
3805
+ },
3806
+ children: [
3807
+ /* @__PURE__ */ jsxs(
3808
+ "div",
3809
+ {
3810
+ style: {
3811
+ fontSize: 12,
3812
+ fontWeight: 500,
3813
+ color: ((_v = result.coverage.files) == null ? void 0 : _v.length) ? "#22c55e" : "#ef4444"
3814
+ },
3815
+ children: [
3816
+ "coverage.files: ",
3817
+ ((_w = result.coverage.files) == null ? void 0 : _w.length) ?? 0,
3818
+ " ",
3819
+ "entries"
3820
+ ]
3821
+ }
3822
+ ),
3823
+ !((_x = result.coverage.files) == null ? void 0 : _x.length) && /* @__PURE__ */ jsx(
3824
+ "div",
3825
+ {
3826
+ style: {
3827
+ fontSize: 11,
3828
+ color: "#ef4444",
3829
+ marginTop: 4
3830
+ },
3831
+ children: "Warning: Coverage won't appear on map visualization!"
3832
+ }
3833
+ )
3834
+ ]
3835
+ }
3836
+ )
3837
+ ]
3838
+ }
3839
+ )
3840
+ ] }),
3229
3841
  filesWithIssues.size > 0 ? /* @__PURE__ */ jsxs("div", { children: [
3230
3842
  /* @__PURE__ */ jsxs(
3231
3843
  "div",
@@ -3514,6 +4126,98 @@ function LensDataDebugPanel$1({
3514
4126
  ))
3515
4127
  }
3516
4128
  )
4129
+ ] }),
4130
+ result.analyzedFiles && result.analyzedFiles.length > 0 && /* @__PURE__ */ jsxs("div", { style: { marginTop: 16 }, children: [
4131
+ /* @__PURE__ */ jsxs(
4132
+ "div",
4133
+ {
4134
+ onClick: () => toggleAnalyzedFiles(lensKey),
4135
+ style: {
4136
+ fontSize: 12,
4137
+ fontWeight: 600,
4138
+ color: theme.colors.textMuted,
4139
+ marginBottom: 8,
4140
+ cursor: "pointer",
4141
+ display: "flex",
4142
+ alignItems: "center",
4143
+ gap: 6
4144
+ },
4145
+ children: [
4146
+ /* @__PURE__ */ jsx(
4147
+ "svg",
4148
+ {
4149
+ width: "12",
4150
+ height: "12",
4151
+ viewBox: "0 0 24 24",
4152
+ fill: "none",
4153
+ stroke: "currentColor",
4154
+ strokeWidth: "2",
4155
+ style: {
4156
+ transform: expandedAnalyzedFiles.has(lensKey) ? "rotate(90deg)" : "rotate(0deg)",
4157
+ transition: "transform 0.15s ease"
4158
+ },
4159
+ children: /* @__PURE__ */ jsx("polyline", { points: "9,18 15,12 9,6" })
4160
+ }
4161
+ ),
4162
+ "Analyzed Files (",
4163
+ result.analyzedFiles.length,
4164
+ ")"
4165
+ ]
4166
+ }
4167
+ ),
4168
+ expandedAnalyzedFiles.has(lensKey) && /* @__PURE__ */ jsx(
4169
+ "div",
4170
+ {
4171
+ style: {
4172
+ maxHeight: 300,
4173
+ overflow: "auto",
4174
+ padding: 8,
4175
+ backgroundColor: theme.colors.surface,
4176
+ borderRadius: 4
4177
+ },
4178
+ children: result.analyzedFiles.map((file, idx2) => /* @__PURE__ */ jsxs(
4179
+ "div",
4180
+ {
4181
+ style: {
4182
+ display: "flex",
4183
+ alignItems: "center",
4184
+ justifyContent: "space-between",
4185
+ padding: "4px 8px",
4186
+ fontSize: 12,
4187
+ borderRadius: 2,
4188
+ backgroundColor: file.hasIssues ? "rgba(239, 68, 68, 0.05)" : "transparent"
4189
+ },
4190
+ children: [
4191
+ /* @__PURE__ */ jsx(
4192
+ "span",
4193
+ {
4194
+ style: {
4195
+ color: theme.colors.text,
4196
+ cursor: onFileClick ? "pointer" : "default"
4197
+ },
4198
+ onClick: () => onFileClick == null ? void 0 : onFileClick(file.path),
4199
+ children: file.path
4200
+ }
4201
+ ),
4202
+ file.hasIssues && /* @__PURE__ */ jsx(
4203
+ "span",
4204
+ {
4205
+ style: {
4206
+ fontSize: 10,
4207
+ color: "#ef4444",
4208
+ padding: "1px 4px",
4209
+ borderRadius: 2,
4210
+ backgroundColor: "rgba(239, 68, 68, 0.1)"
4211
+ },
4212
+ children: "issues"
4213
+ }
4214
+ )
4215
+ ]
4216
+ },
4217
+ idx2
4218
+ ))
4219
+ }
4220
+ )
3517
4221
  ] })
3518
4222
  ]
3519
4223
  }
@@ -3529,15 +4233,54 @@ function LensDataDebugPanel$1({
3529
4233
  }
3530
4234
  const LensDataDebugPanelContent = ({
3531
4235
  context,
3532
- actions
4236
+ actions,
4237
+ events
3533
4238
  }) => {
4239
+ var _a;
3534
4240
  const { theme } = useTheme();
4241
+ const [selectedPackage, setSelectedPackage] = React2__default.useState(
4242
+ null
4243
+ );
3535
4244
  const lensResultsSlice = context.getSlice("lensResults");
3536
4245
  const hasSlice = context.hasSlice("lensResults");
3537
4246
  const isLoading = (lensResultsSlice == null ? void 0 : lensResultsSlice.loading) ?? false;
4247
+ const { packageNames, packagePathMap } = React2__default.useMemo(() => {
4248
+ var _a2;
4249
+ if (!((_a2 = lensResultsSlice == null ? void 0 : lensResultsSlice.data) == null ? void 0 : _a2.results))
4250
+ return { packageNames: [], packagePathMap: /* @__PURE__ */ new Map() };
4251
+ const names = /* @__PURE__ */ new Set();
4252
+ const pathMap = /* @__PURE__ */ new Map();
4253
+ lensResultsSlice.data.results.forEach((r2) => {
4254
+ var _a3, _b;
4255
+ const name = ((_a3 = r2.package) == null ? void 0 : _a3.name) ?? "unknown";
4256
+ const path = ((_b = r2.package) == null ? void 0 : _b.path) ?? "";
4257
+ names.add(name);
4258
+ if (path) pathMap.set(path, name);
4259
+ pathMap.set(name, name);
4260
+ });
4261
+ return { packageNames: Array.from(names), packagePathMap: pathMap };
4262
+ }, [(_a = lensResultsSlice == null ? void 0 : lensResultsSlice.data) == null ? void 0 : _a.results]);
4263
+ React2__default.useEffect(() => {
4264
+ if (packageNames.length > 0 && !selectedPackage) {
4265
+ setSelectedPackage(packageNames[0]);
4266
+ }
4267
+ }, [packageNames, selectedPackage]);
4268
+ React2__default.useEffect(() => {
4269
+ if (!events) return;
4270
+ const cleanup = events.on("package:select", (event) => {
4271
+ const payload = event.payload;
4272
+ if (!payload) return;
4273
+ const matchedPackage = packagePathMap.get(payload.packagePath) ?? packagePathMap.get(payload.packageName) ?? // Also try matching the last segment of the path
4274
+ packagePathMap.get(payload.packagePath.split("/").pop() ?? "");
4275
+ if (matchedPackage) {
4276
+ setSelectedPackage(matchedPackage);
4277
+ }
4278
+ });
4279
+ return cleanup;
4280
+ }, [events, packagePathMap]);
3538
4281
  const handleFileClick = (file, line) => {
3539
- var _a, _b, _c;
3540
- const repoPath = (_a = context.currentScope.repository) == null ? void 0 : _a.path;
4282
+ var _a2, _b, _c;
4283
+ const repoPath = (_a2 = context.currentScope.repository) == null ? void 0 : _a2.path;
3541
4284
  const fullPath = repoPath ? `${repoPath}/${file}` : file;
3542
4285
  if (line) {
3543
4286
  (_b = actions.openFile) == null ? void 0 : _b.call(actions, `${fullPath}:${line}`);
@@ -3589,34 +4332,34 @@ const LensDataDebugPanelContent = ({
3589
4332
  }
3590
4333
  )
3591
4334
  ] }),
3592
- (lensResultsSlice == null ? void 0 : lensResultsSlice.data) && /* @__PURE__ */ jsxs(
4335
+ (lensResultsSlice == null ? void 0 : lensResultsSlice.data) && packageNames.length > 0 && /* @__PURE__ */ jsx(
3593
4336
  "div",
3594
4337
  {
3595
4338
  style: {
3596
4339
  display: "flex",
3597
4340
  alignItems: "center",
3598
4341
  gap: 12,
3599
- fontSize: 11,
3600
- color: theme.colors.textMuted
4342
+ fontSize: 12
3601
4343
  },
3602
- children: [
3603
- /* @__PURE__ */ jsxs("span", { children: [
3604
- new Set(
3605
- lensResultsSlice.data.results.map(
3606
- (r2) => {
3607
- var _a;
3608
- return ((_a = r2.package) == null ? void 0 : _a.name) ?? "unknown";
3609
- }
3610
- )
3611
- ).size,
3612
- " ",
3613
- "packages"
3614
- ] }),
3615
- /* @__PURE__ */ jsxs("span", { children: [
3616
- lensResultsSlice.data.results.length,
3617
- " results"
3618
- ] })
3619
- ]
4344
+ children: packageNames.length > 1 ? /* @__PURE__ */ jsx(
4345
+ "select",
4346
+ {
4347
+ value: selectedPackage ?? "",
4348
+ onChange: (e) => setSelectedPackage(e.target.value),
4349
+ style: {
4350
+ padding: "4px 8px",
4351
+ borderRadius: 4,
4352
+ border: `1px solid ${theme.colors.border}`,
4353
+ backgroundColor: theme.colors.surface,
4354
+ color: theme.colors.text,
4355
+ fontFamily: theme.fonts.body,
4356
+ fontSize: 12,
4357
+ cursor: "pointer",
4358
+ outline: "none"
4359
+ },
4360
+ children: packageNames.map((pkg) => /* @__PURE__ */ jsx("option", { value: pkg, children: pkg }, pkg))
4361
+ }
4362
+ ) : /* @__PURE__ */ jsx("span", { style: { color: theme.colors.textMuted }, children: packageNames[0] })
3620
4363
  }
3621
4364
  )
3622
4365
  ]
@@ -3666,7 +4409,9 @@ const LensDataDebugPanelContent = ({
3666
4409
  {
3667
4410
  data: lensResultsSlice.data,
3668
4411
  theme,
3669
- onFileClick: handleFileClick
4412
+ onFileClick: handleFileClick,
4413
+ selectedPackage: selectedPackage ?? void 0,
4414
+ onPackageSelect: setSelectedPackage
3670
4415
  }
3671
4416
  )
3672
4417
  }