@principal-ade/code-quality-panels 0.1.1 → 0.1.3

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.
@@ -1,6 +1,6 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import * as React2 from "react";
3
- import React2__default, { forwardRef, createElement, createContext, useState, useEffect, useContext } from "react";
3
+ import React2__default, { forwardRef, createElement, createContext, useContext } from "react";
4
4
  /**
5
5
  * @license lucide-react v0.552.0 - ISC
6
6
  *
@@ -154,158 +154,6 @@ const __iconNode = [
154
154
  ["path", { d: "m4 17 6-6-6-6", key: "1yngyt" }]
155
155
  ];
156
156
  const Terminal = createLucideIcon("terminal", __iconNode);
157
- var terminalTheme = {
158
- space: [0, 4, 8, 16, 32, 64, 128, 256, 512],
159
- fonts: {
160
- body: '"SF Mono", "Monaco", "Inconsolata", "Fira Code", monospace',
161
- heading: '"SF Mono", "Monaco", "Inconsolata", "Fira Code", monospace',
162
- monospace: '"SF Mono", "Monaco", "Inconsolata", "Fira Code", monospace'
163
- },
164
- fontSizes: [12, 14, 16, 18, 20, 24, 32, 48, 64, 96],
165
- fontScale: 1,
166
- fontWeights: {
167
- body: 400,
168
- heading: 500,
169
- bold: 600,
170
- light: 300,
171
- medium: 500,
172
- semibold: 600
173
- },
174
- lineHeights: {
175
- body: 1.6,
176
- heading: 1.3,
177
- tight: 1.4,
178
- relaxed: 1.8
179
- },
180
- breakpoints: ["640px", "768px", "1024px", "1280px"],
181
- sizes: [16, 32, 64, 128, 256, 512, 768, 1024, 1536],
182
- radii: [0, 2, 4, 6, 8, 12, 16, 24],
183
- shadows: [
184
- "none",
185
- "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
186
- "0 2px 4px 0 rgba(0, 0, 0, 0.06)",
187
- "0 4px 6px 0 rgba(0, 0, 0, 0.07)",
188
- "0 8px 12px 0 rgba(0, 0, 0, 0.08)",
189
- "0 16px 24px 0 rgba(0, 0, 0, 0.10)"
190
- ],
191
- zIndices: [0, 1, 10, 20, 30, 40, 50],
192
- colors: {
193
- text: "#e4e4e4",
194
- background: "rgba(10, 10, 10, 0.85)",
195
- primary: "#66b3ff",
196
- secondary: "#80c4ff",
197
- accent: "#66ff99",
198
- highlight: "rgba(102, 179, 255, 0.15)",
199
- muted: "rgba(26, 26, 26, 0.8)",
200
- success: "#66ff99",
201
- warning: "#ffcc66",
202
- error: "#ff6666",
203
- info: "#66b3ff",
204
- border: "rgba(255, 255, 255, 0.1)",
205
- backgroundSecondary: "rgba(15, 15, 15, 0.9)",
206
- backgroundTertiary: "rgba(20, 20, 20, 0.9)",
207
- backgroundLight: "rgba(255, 255, 255, 0.05)",
208
- backgroundHover: "rgba(102, 179, 255, 0.08)",
209
- surface: "rgba(15, 15, 15, 0.95)",
210
- textSecondary: "rgba(255, 255, 255, 0.7)",
211
- textTertiary: "rgba(255, 255, 255, 0.5)",
212
- textMuted: "rgba(255, 255, 255, 0.4)",
213
- highlightBg: "rgba(255, 235, 59, 0.25)",
214
- highlightBorder: "rgba(255, 235, 59, 0.5)"
215
- },
216
- modes: {
217
- light: {
218
- text: "#1a1a1a",
219
- background: "rgba(255, 255, 255, 0.9)",
220
- primary: "#0066cc",
221
- secondary: "#0052a3",
222
- accent: "#00cc88",
223
- highlight: "rgba(0, 102, 204, 0.08)",
224
- muted: "rgba(245, 245, 245, 0.8)",
225
- success: "#00cc88",
226
- warning: "#ffaa00",
227
- error: "#ff3333",
228
- info: "#0066cc",
229
- border: "rgba(0, 0, 0, 0.1)",
230
- backgroundSecondary: "rgba(250, 250, 250, 0.9)",
231
- backgroundTertiary: "rgba(245, 245, 245, 0.9)",
232
- backgroundLight: "rgba(0, 0, 0, 0.02)",
233
- backgroundHover: "rgba(0, 102, 204, 0.04)",
234
- surface: "rgba(255, 255, 255, 0.95)",
235
- textSecondary: "rgba(0, 0, 0, 0.6)",
236
- textTertiary: "rgba(0, 0, 0, 0.4)",
237
- textMuted: "rgba(0, 0, 0, 0.3)",
238
- highlightBg: "rgba(255, 235, 59, 0.3)",
239
- highlightBorder: "rgba(255, 235, 59, 0.6)"
240
- }
241
- },
242
- buttons: {
243
- primary: {
244
- color: "white",
245
- bg: "primary",
246
- borderWidth: 0,
247
- "&:hover": {
248
- bg: "secondary"
249
- }
250
- },
251
- secondary: {
252
- color: "primary",
253
- bg: "transparent",
254
- borderWidth: 1,
255
- borderStyle: "solid",
256
- borderColor: "primary",
257
- "&:hover": {
258
- bg: "highlight"
259
- }
260
- },
261
- ghost: {
262
- color: "text",
263
- bg: "transparent",
264
- "&:hover": {
265
- bg: "backgroundHover"
266
- }
267
- }
268
- },
269
- text: {
270
- heading: {
271
- fontFamily: "heading",
272
- fontWeight: "heading",
273
- lineHeight: "heading"
274
- },
275
- body: {
276
- fontFamily: "body",
277
- fontWeight: "body",
278
- lineHeight: "body"
279
- },
280
- caption: {
281
- fontSize: 1,
282
- color: "textSecondary"
283
- }
284
- },
285
- cards: {
286
- primary: {
287
- bg: "surface",
288
- border: "1px solid",
289
- borderColor: "border",
290
- borderRadius: 1
291
- },
292
- secondary: {
293
- bg: "backgroundSecondary",
294
- border: "1px solid",
295
- borderColor: "border",
296
- borderRadius: 1
297
- }
298
- }
299
- };
300
- function getMode(theme2, mode) {
301
- if (!mode || !theme2.modes || !theme2.modes[mode]) {
302
- return theme2.colors;
303
- }
304
- return {
305
- ...theme2.colors,
306
- ...theme2.modes[mode]
307
- };
308
- }
309
157
  var ThemeContext;
310
158
  var getThemeContext = () => {
311
159
  if (typeof window !== "undefined") {
@@ -329,46 +177,6 @@ var useTheme = () => {
329
177
  }
330
178
  return context;
331
179
  };
332
- var ThemeProvider = ({
333
- children,
334
- theme: customTheme = theme,
335
- initialMode
336
- }) => {
337
- const [mode, setMode] = useState(initialMode);
338
- const activeTheme = React2__default.useMemo(() => {
339
- if (!mode || !customTheme.modes || !customTheme.modes[mode]) {
340
- return customTheme;
341
- }
342
- return {
343
- ...customTheme,
344
- colors: getMode(customTheme, mode)
345
- };
346
- }, [customTheme, mode]);
347
- useEffect(() => {
348
- if (!initialMode) {
349
- const savedMode = localStorage.getItem("principlemd-theme-mode");
350
- if (savedMode) {
351
- setMode(savedMode);
352
- }
353
- }
354
- }, [initialMode]);
355
- useEffect(() => {
356
- if (mode) {
357
- localStorage.setItem("principlemd-theme-mode", mode);
358
- } else {
359
- localStorage.removeItem("principlemd-theme-mode");
360
- }
361
- }, [mode]);
362
- const value = {
363
- theme: activeTheme,
364
- mode,
365
- setMode
366
- };
367
- return /* @__PURE__ */ React2__default.createElement(ThemeContextSingleton.Provider, {
368
- value
369
- }, children);
370
- };
371
- var theme = terminalTheme;
372
180
  function r(e) {
373
181
  var t, f, n = "";
374
182
  if ("string" == typeof e || "number" == typeof e) n += e;
@@ -385,31 +193,31 @@ function clsx() {
385
193
  function cn(...inputs) {
386
194
  return clsx(inputs);
387
195
  }
388
- function getThemeColors(theme2) {
196
+ function getThemeColors(theme) {
389
197
  return {
390
- gridColor: theme2.colors.border,
391
- axisColor: theme2.colors.muted,
392
- textColor: theme2.colors.text,
393
- scoreColor: theme2.colors.text,
198
+ gridColor: theme.colors.border,
199
+ axisColor: theme.colors.muted,
200
+ textColor: theme.colors.text,
201
+ scoreColor: theme.colors.text,
394
202
  tierColors: {
395
- none: { fill: theme2.colors.muted, stroke: theme2.colors.border, bg: theme2.colors.backgroundLight },
396
- bronze: { fill: theme2.colors.warning, stroke: theme2.colors.warning, bg: theme2.colors.backgroundLight },
397
- silver: { fill: theme2.colors.secondary, stroke: theme2.colors.secondary, bg: theme2.colors.backgroundLight },
398
- gold: { fill: theme2.colors.accent, stroke: theme2.colors.accent, bg: theme2.colors.backgroundLight },
399
- platinum: { fill: theme2.colors.primary, stroke: theme2.colors.primary, bg: theme2.colors.backgroundLight }
203
+ none: { fill: theme.colors.muted, stroke: theme.colors.border, bg: theme.colors.backgroundLight },
204
+ bronze: { fill: theme.colors.warning, stroke: theme.colors.warning, bg: theme.colors.backgroundLight },
205
+ silver: { fill: theme.colors.secondary, stroke: theme.colors.secondary, bg: theme.colors.backgroundLight },
206
+ gold: { fill: theme.colors.accent, stroke: theme.colors.accent, bg: theme.colors.backgroundLight },
207
+ platinum: { fill: theme.colors.primary, stroke: theme.colors.primary, bg: theme.colors.backgroundLight }
400
208
  },
401
209
  metricColors: {
402
- types: theme2.colors.warning,
403
- documentation: theme2.colors.info,
404
- tests: theme2.colors.success,
405
- deadCode: theme2.colors.error,
406
- formatting: theme2.colors.accent,
407
- linting: theme2.colors.primary
210
+ types: theme.colors.warning,
211
+ documentation: theme.colors.info,
212
+ tests: theme.colors.success,
213
+ deadCode: theme.colors.error,
214
+ formatting: theme.colors.accent,
215
+ linting: theme.colors.primary
408
216
  },
409
217
  qualityIndicators: {
410
- good: theme2.colors.success,
411
- medium: theme2.colors.warning,
412
- poor: theme2.colors.error
218
+ good: theme.colors.success,
219
+ medium: theme.colors.warning,
220
+ poor: theme.colors.error
413
221
  }
414
222
  };
415
223
  }
@@ -440,12 +248,15 @@ function calculateMetricPoint(center, radius, angle, value) {
440
248
  function QualityHexagon({
441
249
  metrics,
442
250
  tier,
443
- theme: theme2,
251
+ theme,
444
252
  showLabels = false,
445
253
  showValues = false,
446
- className
254
+ className,
255
+ onVertexHover,
256
+ onVertexLeave,
257
+ onVertexClick
447
258
  }) {
448
- const themeColors = getThemeColors(theme2);
259
+ const themeColors = getThemeColors(theme);
449
260
  const colors = themeColors.tierColors[tier] ?? themeColors.tierColors.none;
450
261
  const metricConfig = getMetricConfig(themeColors);
451
262
  const viewBoxSize = 300;
@@ -523,38 +334,71 @@ function QualityHexagon({
523
334
  style: { transition: "all 0.5s ease" }
524
335
  }
525
336
  ),
526
- metricConfig.map(({ key, angle }) => {
527
- let value = metrics[key];
337
+ metricConfig.map(({ key, label, color, angle }) => {
338
+ const rawValue = metrics[key];
339
+ let value = rawValue;
528
340
  if (key === "deadCode") {
529
341
  value = 100 - value;
530
342
  }
531
343
  const point = calculateMetricPoint(center, radius, angle, 100);
532
344
  const dataPoint = calculateMetricPoint(center, radius, angle, value);
533
- return /* @__PURE__ */ jsxs("g", { children: [
534
- /* @__PURE__ */ jsx(
535
- "circle",
536
- {
537
- cx: point.x,
538
- cy: point.y,
539
- r: dotSize,
540
- fill: "white",
541
- stroke: colors.stroke,
542
- strokeWidth: 1.5
543
- }
544
- ),
545
- /* @__PURE__ */ jsx(
546
- "circle",
547
- {
548
- cx: dataPoint.x,
549
- cy: dataPoint.y,
550
- r: dotSize * 0.7,
551
- fill: colors.fill,
552
- stroke: colors.stroke,
553
- strokeWidth: 1,
554
- style: { opacity: 0.9 }
555
- }
556
- )
557
- ] }, key);
345
+ const vertexInfo = {
346
+ key,
347
+ label,
348
+ value: rawValue,
349
+ color
350
+ };
351
+ const handleMouseEnter = () => {
352
+ onVertexHover == null ? void 0 : onVertexHover(vertexInfo);
353
+ };
354
+ const handleClick = (e) => {
355
+ e.stopPropagation();
356
+ onVertexClick == null ? void 0 : onVertexClick(vertexInfo);
357
+ };
358
+ return /* @__PURE__ */ jsxs(
359
+ "g",
360
+ {
361
+ onMouseEnter: handleMouseEnter,
362
+ onMouseLeave: onVertexLeave,
363
+ onClick: handleClick,
364
+ style: { cursor: onVertexHover || onVertexClick ? "pointer" : "default" },
365
+ children: [
366
+ /* @__PURE__ */ jsx(
367
+ "circle",
368
+ {
369
+ cx: point.x,
370
+ cy: point.y,
371
+ r: dotSize * 2.5,
372
+ fill: "transparent"
373
+ }
374
+ ),
375
+ /* @__PURE__ */ jsx(
376
+ "circle",
377
+ {
378
+ cx: point.x,
379
+ cy: point.y,
380
+ r: dotSize,
381
+ fill: "white",
382
+ stroke: colors.stroke,
383
+ strokeWidth: 1.5
384
+ }
385
+ ),
386
+ /* @__PURE__ */ jsx(
387
+ "circle",
388
+ {
389
+ cx: dataPoint.x,
390
+ cy: dataPoint.y,
391
+ r: dotSize * 0.7,
392
+ fill: colors.fill,
393
+ stroke: colors.stroke,
394
+ strokeWidth: 1,
395
+ style: { opacity: 0.9 }
396
+ }
397
+ )
398
+ ]
399
+ },
400
+ key
401
+ );
558
402
  }),
559
403
  /* @__PURE__ */ jsx(
560
404
  "text",
@@ -608,7 +452,7 @@ function QualityHexagon({
608
452
  function QualityHexagonCompact({
609
453
  metrics,
610
454
  tier,
611
- theme: theme2,
455
+ theme,
612
456
  className
613
457
  }) {
614
458
  return /* @__PURE__ */ jsx("div", { className: cn("w-20 h-20", className), children: /* @__PURE__ */ jsx(
@@ -616,7 +460,7 @@ function QualityHexagonCompact({
616
460
  {
617
461
  metrics,
618
462
  tier,
619
- theme: theme2,
463
+ theme,
620
464
  showLabels: false,
621
465
  showValues: false
622
466
  }
@@ -625,14 +469,14 @@ function QualityHexagonCompact({
625
469
  function QualityHexagonDetailed({
626
470
  metrics,
627
471
  tier,
628
- theme: theme2,
472
+ theme,
629
473
  className,
630
474
  packageName,
631
475
  packageVersion,
632
476
  onRefresh,
633
477
  isRefreshing = false
634
478
  }) {
635
- const themeColors = getThemeColors(theme2);
479
+ const themeColors = getThemeColors(theme);
636
480
  const colors = themeColors.tierColors[tier] ?? themeColors.tierColors.none;
637
481
  const metricConfig = getMetricConfig(themeColors);
638
482
  const hasHeader = packageName || onRefresh;
@@ -659,7 +503,7 @@ function QualityHexagonDetailed({
659
503
  packageName.startsWith("@") && packageName.includes("/") ? /* @__PURE__ */ jsxs(Fragment, { children: [
660
504
  /* @__PURE__ */ jsx("span", { style: {
661
505
  fontSize: 12,
662
- color: theme2.colors.textMuted
506
+ color: theme.colors.textMuted
663
507
  }, children: packageName.split("/")[0] }),
664
508
  /* @__PURE__ */ jsx("span", { style: {
665
509
  fontSize: 14,
@@ -673,7 +517,7 @@ function QualityHexagonDetailed({
673
517
  }, children: packageName }),
674
518
  packageVersion && /* @__PURE__ */ jsxs("span", { style: {
675
519
  fontSize: 12,
676
- color: theme2.colors.textMuted
520
+ color: theme.colors.textMuted
677
521
  }, children: [
678
522
  "v",
679
523
  packageVersion
@@ -689,10 +533,10 @@ function QualityHexagonDetailed({
689
533
  display: "flex",
690
534
  alignItems: "center",
691
535
  justifyContent: "center",
692
- border: `1px solid ${theme2.colors.border}`,
536
+ border: `1px solid ${theme.colors.border}`,
693
537
  borderRadius: 4,
694
- background: theme2.colors.surface,
695
- color: theme2.colors.textMuted,
538
+ background: theme.colors.surface,
539
+ color: theme.colors.textMuted,
696
540
  cursor: isRefreshing ? "not-allowed" : "pointer",
697
541
  opacity: isRefreshing ? 0.6 : 1
698
542
  },
@@ -734,7 +578,7 @@ function QualityHexagonDetailed({
734
578
  {
735
579
  metrics,
736
580
  tier,
737
- theme: theme2,
581
+ theme,
738
582
  showLabels: true,
739
583
  showValues: false
740
584
  }
@@ -745,7 +589,7 @@ function QualityHexagonDetailed({
745
589
  return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }, children: [
746
590
  /* @__PURE__ */ jsxs("span", { style: {
747
591
  fontSize: 14,
748
- color: theme2.colors.textMuted
592
+ color: theme.colors.textMuted
749
593
  }, children: [
750
594
  label,
751
595
  key === "deadCode" ? " ↓" : ""
@@ -768,7 +612,7 @@ function QualityHexagonDetailed({
768
612
  function QualityHexagonExpandable({
769
613
  metrics,
770
614
  tier,
771
- theme: theme2,
615
+ theme,
772
616
  className,
773
617
  packageName,
774
618
  packageVersion,
@@ -777,7 +621,7 @@ function QualityHexagonExpandable({
777
621
  defaultExpanded = false
778
622
  }) {
779
623
  const [expanded, setExpanded] = React2.useState(defaultExpanded);
780
- const themeColors = getThemeColors(theme2);
624
+ const themeColors = getThemeColors(theme);
781
625
  const colors = themeColors.tierColors[tier] ?? themeColors.tierColors.none;
782
626
  const metricConfig = getMetricConfig(themeColors);
783
627
  const hasHeader = packageName || onRefresh;
@@ -805,7 +649,7 @@ function QualityHexagonExpandable({
805
649
  packageName.startsWith("@") && packageName.includes("/") ? /* @__PURE__ */ jsxs(Fragment, { children: [
806
650
  /* @__PURE__ */ jsx("span", { style: {
807
651
  fontSize: 12,
808
- color: theme2.colors.textMuted
652
+ color: theme.colors.textMuted
809
653
  }, children: packageName.split("/")[0] }),
810
654
  /* @__PURE__ */ jsx("span", { style: {
811
655
  fontSize: 14,
@@ -819,7 +663,7 @@ function QualityHexagonExpandable({
819
663
  }, children: packageName }),
820
664
  packageVersion && /* @__PURE__ */ jsxs("span", { style: {
821
665
  fontSize: 12,
822
- color: theme2.colors.textMuted
666
+ color: theme.colors.textMuted
823
667
  }, children: [
824
668
  "v",
825
669
  packageVersion
@@ -835,10 +679,10 @@ function QualityHexagonExpandable({
835
679
  display: "flex",
836
680
  alignItems: "center",
837
681
  justifyContent: "center",
838
- border: `1px solid ${theme2.colors.border}`,
682
+ border: `1px solid ${theme.colors.border}`,
839
683
  borderRadius: 4,
840
- background: theme2.colors.surface,
841
- color: theme2.colors.textMuted,
684
+ background: theme.colors.surface,
685
+ color: theme.colors.textMuted,
842
686
  cursor: isRefreshing ? "not-allowed" : "pointer",
843
687
  opacity: isRefreshing ? 0.6 : 1
844
688
  },
@@ -883,7 +727,7 @@ function QualityHexagonExpandable({
883
727
  {
884
728
  metrics,
885
729
  tier,
886
- theme: theme2,
730
+ theme,
887
731
  showLabels: true,
888
732
  showValues: false
889
733
  }
@@ -903,14 +747,14 @@ function QualityHexagonExpandable({
903
747
  flexDirection: "column",
904
748
  gap: 8,
905
749
  padding: "8px 24px",
906
- borderTop: `1px solid ${theme2.colors.border}`,
750
+ borderTop: `1px solid ${theme.colors.border}`,
907
751
  marginTop: 8
908
752
  }, children: metricConfig.map(({ key, label, color }) => {
909
753
  const value = metrics[key];
910
754
  return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }, children: [
911
755
  /* @__PURE__ */ jsxs("span", { style: {
912
756
  fontSize: 14,
913
- color: theme2.colors.textMuted
757
+ color: theme.colors.textMuted
914
758
  }, children: [
915
759
  label,
916
760
  key === "deadCode" ? " ↓" : ""
@@ -944,7 +788,7 @@ function QualityHexagonExpandable({
944
788
  height: "16",
945
789
  viewBox: "0 0 24 24",
946
790
  fill: "none",
947
- stroke: theme2.colors.textMuted,
791
+ stroke: theme.colors.textMuted,
948
792
  strokeWidth: "2",
949
793
  strokeLinecap: "round",
950
794
  strokeLinejoin: "round",
@@ -980,7 +824,7 @@ function checkFileExistsInTree(treeData, targetPath) {
980
824
  return filePath.endsWith(normalizedTarget) || filePath === normalizedTarget;
981
825
  });
982
826
  }
983
- const CommandLine = ({ command, theme: theme2 }) => {
827
+ const CommandLine = ({ command, theme }) => {
984
828
  const [copied, setCopied] = React2__default.useState(false);
985
829
  const handleCopy = async () => {
986
830
  try {
@@ -1001,13 +845,13 @@ const CommandLine = ({ command, theme: theme2 }) => {
1001
845
  gap: 12,
1002
846
  padding: "10px 14px",
1003
847
  borderRadius: 6,
1004
- backgroundColor: theme2.colors.background,
1005
- border: `1px solid ${theme2.colors.border}`,
848
+ backgroundColor: theme.colors.background,
849
+ border: `1px solid ${theme.colors.border}`,
1006
850
  fontFamily: "monospace",
1007
851
  fontSize: 13
1008
852
  },
1009
853
  children: [
1010
- /* @__PURE__ */ jsx("code", { style: { color: theme2.colors.text }, children: command }),
854
+ /* @__PURE__ */ jsx("code", { style: { color: theme.colors.text }, children: command }),
1011
855
  /* @__PURE__ */ jsx(
1012
856
  "button",
1013
857
  {
@@ -1019,11 +863,11 @@ const CommandLine = ({ command, theme: theme2 }) => {
1019
863
  padding: 4,
1020
864
  border: "none",
1021
865
  backgroundColor: "transparent",
1022
- color: theme2.colors.textMuted,
866
+ color: theme.colors.textMuted,
1023
867
  cursor: "pointer"
1024
868
  },
1025
869
  title: "Copy command",
1026
- children: copied ? /* @__PURE__ */ jsx(Check, { size: 16, color: theme2.colors.success }) : /* @__PURE__ */ jsx(Copy, { size: 16 })
870
+ children: copied ? /* @__PURE__ */ jsx(Check, { size: 16, color: theme.colors.success }) : /* @__PURE__ */ jsx(Copy, { size: 16 })
1027
871
  }
1028
872
  )
1029
873
  ]
@@ -1031,7 +875,7 @@ const CommandLine = ({ command, theme: theme2 }) => {
1031
875
  );
1032
876
  };
1033
877
  const QualityEmptyState = ({
1034
- theme: theme2,
878
+ theme,
1035
879
  hasWorkflow
1036
880
  }) => {
1037
881
  if (hasWorkflow) {
@@ -1052,7 +896,7 @@ const QualityEmptyState = ({
1052
896
  style: {
1053
897
  margin: 0,
1054
898
  fontSize: 14,
1055
- color: theme2.colors.textMuted,
899
+ color: theme.colors.textMuted,
1056
900
  lineHeight: 1.5
1057
901
  },
1058
902
  children: "Quality metrics will appear here after your next CI run completes."
@@ -1067,8 +911,8 @@ const QualityEmptyState = ({
1067
911
  gap: 8,
1068
912
  padding: "10px 14px",
1069
913
  borderRadius: 6,
1070
- backgroundColor: `${theme2.colors.success}15`,
1071
- color: theme2.colors.success,
914
+ backgroundColor: `${theme.colors.success}15`,
915
+ color: theme.colors.success,
1072
916
  fontSize: 13
1073
917
  },
1074
918
  children: [
@@ -1101,7 +945,7 @@ const QualityEmptyState = ({
1101
945
  style: {
1102
946
  margin: 0,
1103
947
  fontSize: 14,
1104
- color: theme2.colors.textMuted,
948
+ color: theme.colors.textMuted,
1105
949
  lineHeight: 1.5
1106
950
  },
1107
951
  children: "Track your code quality with automated analysis of tests, linting, types, formatting, dead code, and documentation."
@@ -1116,8 +960,8 @@ const QualityEmptyState = ({
1116
960
  gap: 16,
1117
961
  padding: 20,
1118
962
  borderRadius: 8,
1119
- backgroundColor: theme2.colors.surface,
1120
- border: `1px solid ${theme2.colors.border}`
963
+ backgroundColor: theme.colors.surface,
964
+ border: `1px solid ${theme.colors.border}`
1121
965
  },
1122
966
  children: [
1123
967
  /* @__PURE__ */ jsxs(
@@ -1130,7 +974,7 @@ const QualityEmptyState = ({
1130
974
  marginBottom: 4
1131
975
  },
1132
976
  children: [
1133
- /* @__PURE__ */ jsx(Terminal, { size: 20, color: theme2.colors.text }),
977
+ /* @__PURE__ */ jsx(Terminal, { size: 20, color: theme.colors.text }),
1134
978
  /* @__PURE__ */ jsx(
1135
979
  "h4",
1136
980
  {
@@ -1138,7 +982,7 @@ const QualityEmptyState = ({
1138
982
  margin: 0,
1139
983
  fontSize: 15,
1140
984
  fontWeight: 600,
1141
- color: theme2.colors.text
985
+ color: theme.colors.text
1142
986
  },
1143
987
  children: "Get Started"
1144
988
  }
@@ -1156,7 +1000,7 @@ const QualityEmptyState = ({
1156
1000
  gap: 8,
1157
1001
  marginBottom: 8,
1158
1002
  fontSize: 13,
1159
- color: theme2.colors.textMuted
1003
+ color: theme.colors.textMuted
1160
1004
  },
1161
1005
  children: [
1162
1006
  /* @__PURE__ */ jsx(
@@ -1169,8 +1013,8 @@ const QualityEmptyState = ({
1169
1013
  width: 20,
1170
1014
  height: 20,
1171
1015
  borderRadius: "50%",
1172
- backgroundColor: theme2.colors.primary,
1173
- color: theme2.colors.background,
1016
+ backgroundColor: theme.colors.primary,
1017
+ color: theme.colors.background,
1174
1018
  fontSize: 11,
1175
1019
  fontWeight: 600
1176
1020
  },
@@ -1185,7 +1029,7 @@ const QualityEmptyState = ({
1185
1029
  CommandLine,
1186
1030
  {
1187
1031
  command: "npm install -g @principal-ai/quality-lens-cli",
1188
- theme: theme2
1032
+ theme
1189
1033
  }
1190
1034
  )
1191
1035
  ] }),
@@ -1199,7 +1043,7 @@ const QualityEmptyState = ({
1199
1043
  gap: 8,
1200
1044
  marginBottom: 8,
1201
1045
  fontSize: 13,
1202
- color: theme2.colors.textMuted
1046
+ color: theme.colors.textMuted
1203
1047
  },
1204
1048
  children: [
1205
1049
  /* @__PURE__ */ jsx(
@@ -1212,8 +1056,8 @@ const QualityEmptyState = ({
1212
1056
  width: 20,
1213
1057
  height: 20,
1214
1058
  borderRadius: "50%",
1215
- backgroundColor: theme2.colors.primary,
1216
- color: theme2.colors.background,
1059
+ backgroundColor: theme.colors.primary,
1060
+ color: theme.colors.background,
1217
1061
  fontSize: 11,
1218
1062
  fontWeight: 600
1219
1063
  },
@@ -1224,7 +1068,7 @@ const QualityEmptyState = ({
1224
1068
  ]
1225
1069
  }
1226
1070
  ),
1227
- /* @__PURE__ */ jsx(CommandLine, { command: "quality-lens list", theme: theme2 })
1071
+ /* @__PURE__ */ jsx(CommandLine, { command: "quality-lens list", theme })
1228
1072
  ] }),
1229
1073
  /* @__PURE__ */ jsxs("div", { children: [
1230
1074
  /* @__PURE__ */ jsxs(
@@ -1236,7 +1080,7 @@ const QualityEmptyState = ({
1236
1080
  gap: 8,
1237
1081
  marginBottom: 8,
1238
1082
  fontSize: 13,
1239
- color: theme2.colors.textMuted
1083
+ color: theme.colors.textMuted
1240
1084
  },
1241
1085
  children: [
1242
1086
  /* @__PURE__ */ jsx(
@@ -1249,8 +1093,8 @@ const QualityEmptyState = ({
1249
1093
  width: 20,
1250
1094
  height: 20,
1251
1095
  borderRadius: "50%",
1252
- backgroundColor: theme2.colors.primary,
1253
- color: theme2.colors.background,
1096
+ backgroundColor: theme.colors.primary,
1097
+ color: theme.colors.background,
1254
1098
  fontSize: 11,
1255
1099
  fontWeight: 600
1256
1100
  },
@@ -1261,7 +1105,7 @@ const QualityEmptyState = ({
1261
1105
  ]
1262
1106
  }
1263
1107
  ),
1264
- /* @__PURE__ */ jsx(CommandLine, { command: "quality-lens init", theme: theme2 })
1108
+ /* @__PURE__ */ jsx(CommandLine, { command: "quality-lens init", theme })
1265
1109
  ] }),
1266
1110
  /* @__PURE__ */ jsxs(
1267
1111
  "div",
@@ -1272,7 +1116,7 @@ const QualityEmptyState = ({
1272
1116
  gap: 6,
1273
1117
  paddingTop: 8,
1274
1118
  fontSize: 13,
1275
- color: theme2.colors.textMuted
1119
+ color: theme.colors.textMuted
1276
1120
  },
1277
1121
  children: [
1278
1122
  /* @__PURE__ */ jsx(ChevronRight, { size: 14 }),
@@ -1306,7 +1150,7 @@ const QualityHexagonPanelContent = ({
1306
1150
  events
1307
1151
  }) => {
1308
1152
  var _a;
1309
- const { theme: theme2 } = useTheme();
1153
+ const { theme } = useTheme();
1310
1154
  const [refreshingPackages, setRefreshingPackages] = React2__default.useState(/* @__PURE__ */ new Set());
1311
1155
  const qualitySlice = context.getSlice("quality");
1312
1156
  const hasQualitySlice = context.hasSlice("quality");
@@ -1345,11 +1189,11 @@ const QualityHexagonPanelContent = ({
1345
1189
  return () => unsubscribers.forEach((unsub) => unsub());
1346
1190
  }, [events, context, packages]);
1347
1191
  const tierColors = {
1348
- none: theme2.colors.muted,
1349
- bronze: theme2.colors.warning,
1350
- silver: theme2.colors.secondary,
1351
- gold: theme2.colors.accent,
1352
- platinum: theme2.colors.primary
1192
+ none: theme.colors.muted,
1193
+ bronze: theme.colors.warning,
1194
+ silver: theme.colors.secondary,
1195
+ gold: theme.colors.accent,
1196
+ platinum: theme.colors.primary
1353
1197
  };
1354
1198
  const overallTier = packages.length > 0 ? calculateQualityTier(
1355
1199
  packages.reduce((acc, pkg) => ({
@@ -1365,11 +1209,11 @@ const QualityHexagonPanelContent = ({
1365
1209
  "div",
1366
1210
  {
1367
1211
  style: {
1368
- fontFamily: theme2.fonts.body,
1212
+ fontFamily: theme.fonts.body,
1369
1213
  height: "100%",
1370
1214
  minHeight: 0,
1371
- backgroundColor: theme2.colors.background,
1372
- color: theme2.colors.text,
1215
+ backgroundColor: theme.colors.background,
1216
+ color: theme.colors.text,
1373
1217
  overflowY: "auto",
1374
1218
  boxSizing: "border-box"
1375
1219
  },
@@ -1392,7 +1236,7 @@ const QualityHexagonPanelContent = ({
1392
1236
  margin: 0,
1393
1237
  fontSize: 20,
1394
1238
  fontWeight: 600,
1395
- color: theme2.colors.text
1239
+ color: theme.colors.text
1396
1240
  },
1397
1241
  children: "Code Quality"
1398
1242
  }
@@ -1408,9 +1252,9 @@ const QualityHexagonPanelContent = ({
1408
1252
  width: 18,
1409
1253
  height: 18,
1410
1254
  borderRadius: "50%",
1411
- border: `1px solid ${theme2.colors.border}`,
1255
+ border: `1px solid ${theme.colors.border}`,
1412
1256
  fontSize: 12,
1413
- color: theme2.colors.textMuted,
1257
+ color: theme.colors.textMuted,
1414
1258
  cursor: "help"
1415
1259
  },
1416
1260
  children: "?"
@@ -1418,7 +1262,7 @@ const QualityHexagonPanelContent = ({
1418
1262
  ),
1419
1263
  packages.length > 1 && /* @__PURE__ */ jsxs("span", { style: {
1420
1264
  fontSize: 14,
1421
- color: theme2.colors.textMuted
1265
+ color: theme.colors.textMuted
1422
1266
  }, children: [
1423
1267
  packages.length,
1424
1268
  " packages"
@@ -1427,11 +1271,11 @@ const QualityHexagonPanelContent = ({
1427
1271
  /* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 16, minHeight: 0 }, children: isLoading ? /* @__PURE__ */ jsx("div", { style: {
1428
1272
  padding: 40,
1429
1273
  textAlign: "center",
1430
- color: theme2.colors.textMuted
1274
+ color: theme.colors.textMuted
1431
1275
  }, children: "Loading quality metrics..." }) : packages.length === 0 ? /* @__PURE__ */ jsx(
1432
1276
  QualityEmptyState,
1433
1277
  {
1434
- theme: theme2,
1278
+ theme,
1435
1279
  hasWorkflow
1436
1280
  }
1437
1281
  ) : packages.map((pkg) => {
@@ -1441,7 +1285,7 @@ const QualityHexagonPanelContent = ({
1441
1285
  {
1442
1286
  metrics: pkg.metrics,
1443
1287
  tier,
1444
- theme: theme2,
1288
+ theme,
1445
1289
  packageName: pkg.name,
1446
1290
  packageVersion: pkg.version
1447
1291
  },
@@ -1454,9 +1298,15 @@ const QualityHexagonPanelContent = ({
1454
1298
  }
1455
1299
  );
1456
1300
  };
1457
- const QualityHexagonPanel = (props) => {
1458
- return /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(QualityHexagonPanelContent, { ...props }) });
1459
- };
1301
+ const QualityHexagonPanel = QualityHexagonPanelContent;
1302
+ const METRIC_OPTIONS = [
1303
+ { key: "types", label: "Types" },
1304
+ { key: "documentation", label: "Docs" },
1305
+ { key: "tests", label: "Tests" },
1306
+ { key: "deadCode", label: "Dead Code" },
1307
+ { key: "formatting", label: "Format" },
1308
+ { key: "linting", label: "Linting" }
1309
+ ];
1460
1310
  function flattenRepositories(repositories) {
1461
1311
  const items = [];
1462
1312
  for (const repo of repositories) {
@@ -1495,22 +1345,52 @@ function formatLabel(item, showRepositoryName, isSameAsRepo) {
1495
1345
  }
1496
1346
  return `${item.repositoryName} / ${item.packageName}`;
1497
1347
  }
1348
+ function getValueColor(value, key, theme) {
1349
+ const effectiveValue = key === "deadCode" ? 100 - value : value;
1350
+ if (effectiveValue >= 80) return theme.colors.success;
1351
+ if (effectiveValue >= 60) return theme.colors.warning;
1352
+ return theme.colors.error;
1353
+ }
1498
1354
  function RepositoryQualityGridItem({
1499
1355
  item,
1500
- theme: theme2,
1356
+ theme,
1501
1357
  onClick,
1358
+ onVertexClick,
1502
1359
  showRepositoryName = true,
1360
+ selectedMetric,
1503
1361
  className
1504
1362
  }) {
1363
+ const [hoveredVertex, setHoveredVertex] = React2.useState(null);
1505
1364
  const isSameAsRepo = item.packageName === item.repositoryName;
1506
1365
  const label = formatLabel(item, showRepositoryName, isSameAsRepo);
1507
1366
  const tierColors = {
1508
- none: theme2.colors.muted,
1509
- bronze: theme2.colors.warning,
1510
- silver: theme2.colors.secondary,
1511
- gold: theme2.colors.accent,
1512
- platinum: theme2.colors.primary
1367
+ none: theme.colors.muted,
1368
+ bronze: theme.colors.warning,
1369
+ silver: theme.colors.secondary,
1370
+ gold: theme.colors.accent,
1371
+ platinum: theme.colors.primary
1513
1372
  };
1373
+ const displayInfo = React2.useMemo(() => {
1374
+ if (selectedMetric) {
1375
+ const option = METRIC_OPTIONS.find((o) => o.key === selectedMetric);
1376
+ if (option) {
1377
+ const value = item.metrics[selectedMetric];
1378
+ return {
1379
+ label: option.label,
1380
+ value,
1381
+ valueColor: getValueColor(value, selectedMetric, theme)
1382
+ };
1383
+ }
1384
+ }
1385
+ if (hoveredVertex) {
1386
+ return {
1387
+ label: hoveredVertex.label,
1388
+ value: hoveredVertex.value,
1389
+ valueColor: getValueColor(hoveredVertex.value, hoveredVertex.key, theme)
1390
+ };
1391
+ }
1392
+ return null;
1393
+ }, [selectedMetric, hoveredVertex, item.metrics, theme]);
1514
1394
  return /* @__PURE__ */ jsxs(
1515
1395
  "div",
1516
1396
  {
@@ -1523,11 +1403,10 @@ function RepositoryQualityGridItem({
1523
1403
  gap: 8,
1524
1404
  padding: 12,
1525
1405
  borderRadius: 8,
1526
- backgroundColor: theme2.colors.surface,
1527
- border: `1px solid ${theme2.colors.border}`,
1406
+ backgroundColor: theme.colors.surface,
1407
+ border: `1px solid ${theme.colors.border}`,
1528
1408
  cursor: onClick ? "pointer" : "default",
1529
- transition: "all 0.2s ease",
1530
- minWidth: 100
1409
+ transition: "all 0.2s ease"
1531
1410
  },
1532
1411
  onMouseEnter: (e) => {
1533
1412
  if (onClick) {
@@ -1536,18 +1415,65 @@ function RepositoryQualityGridItem({
1536
1415
  }
1537
1416
  },
1538
1417
  onMouseLeave: (e) => {
1539
- e.currentTarget.style.borderColor = theme2.colors.border;
1418
+ e.currentTarget.style.borderColor = theme.colors.border;
1540
1419
  e.currentTarget.style.transform = "translateY(0)";
1420
+ setHoveredVertex(null);
1541
1421
  },
1542
1422
  children: [
1543
1423
  /* @__PURE__ */ jsx(
1544
- QualityHexagonCompact,
1424
+ "div",
1425
+ {
1426
+ style: {
1427
+ height: 24,
1428
+ display: "flex",
1429
+ alignItems: "center",
1430
+ justifyContent: "center",
1431
+ gap: 8,
1432
+ width: "100%",
1433
+ minHeight: 24
1434
+ },
1435
+ children: displayInfo ? /* @__PURE__ */ jsxs(Fragment, { children: [
1436
+ /* @__PURE__ */ jsx(
1437
+ "span",
1438
+ {
1439
+ style: {
1440
+ fontSize: 14,
1441
+ fontWeight: 500,
1442
+ color: theme.colors.text
1443
+ },
1444
+ children: displayInfo.label
1445
+ }
1446
+ ),
1447
+ /* @__PURE__ */ jsxs(
1448
+ "span",
1449
+ {
1450
+ style: {
1451
+ fontSize: 14,
1452
+ fontWeight: 600,
1453
+ color: displayInfo.valueColor
1454
+ },
1455
+ children: [
1456
+ displayInfo.value,
1457
+ "%"
1458
+ ]
1459
+ }
1460
+ )
1461
+ ] }) : /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: theme.colors.textMuted }, children: "Hover a corner" })
1462
+ }
1463
+ ),
1464
+ /* @__PURE__ */ jsx("div", { style: { width: 200, height: 200 }, children: /* @__PURE__ */ jsx(
1465
+ QualityHexagon,
1545
1466
  {
1546
1467
  metrics: item.metrics,
1547
1468
  tier: item.tier,
1548
- theme: theme2
1469
+ theme,
1470
+ showLabels: false,
1471
+ showValues: false,
1472
+ onVertexHover: setHoveredVertex,
1473
+ onVertexLeave: () => setHoveredVertex(null),
1474
+ onVertexClick: onVertexClick ? (vertex) => onVertexClick(item, vertex) : void 0
1549
1475
  }
1550
- ),
1476
+ ) }),
1551
1477
  /* @__PURE__ */ jsxs(
1552
1478
  "div",
1553
1479
  {
@@ -1555,8 +1481,7 @@ function RepositoryQualityGridItem({
1555
1481
  display: "flex",
1556
1482
  flexDirection: "column",
1557
1483
  alignItems: "center",
1558
- gap: 2,
1559
- maxWidth: 120
1484
+ gap: 2
1560
1485
  },
1561
1486
  children: [
1562
1487
  /* @__PURE__ */ jsx(
@@ -1565,14 +1490,9 @@ function RepositoryQualityGridItem({
1565
1490
  style: {
1566
1491
  fontSize: 12,
1567
1492
  fontWeight: 500,
1568
- color: theme2.colors.text,
1569
- textAlign: "center",
1570
- overflow: "hidden",
1571
- textOverflow: "ellipsis",
1572
- whiteSpace: "nowrap",
1573
- maxWidth: "100%"
1493
+ color: theme.colors.text,
1494
+ textAlign: "center"
1574
1495
  },
1575
- title: label,
1576
1496
  children: label
1577
1497
  }
1578
1498
  ),
@@ -1581,7 +1501,7 @@ function RepositoryQualityGridItem({
1581
1501
  {
1582
1502
  style: {
1583
1503
  fontSize: 10,
1584
- color: theme2.colors.textMuted
1504
+ color: theme.colors.textMuted
1585
1505
  },
1586
1506
  children: [
1587
1507
  "v",
@@ -1596,22 +1516,40 @@ function RepositoryQualityGridItem({
1596
1516
  }
1597
1517
  );
1598
1518
  }
1519
+ function calculateAverageScore(metrics) {
1520
+ const adjusted = { ...metrics };
1521
+ adjusted.deadCode = 100 - adjusted.deadCode;
1522
+ return Object.values(adjusted).reduce((a, b) => a + b, 0) / 6;
1523
+ }
1599
1524
  function RepositoryQualityGrid({
1600
1525
  repositories,
1601
- theme: theme2,
1526
+ theme,
1602
1527
  onItemClick,
1528
+ onVertexClick,
1603
1529
  className,
1604
1530
  showRepositoryName = true,
1605
1531
  showSummary = true
1606
1532
  }) {
1533
+ const [selectedMetric, setSelectedMetric] = React2.useState(null);
1607
1534
  const items = React2.useMemo(() => flattenRepositories(repositories), [repositories]);
1608
1535
  const overallTier = React2.useMemo(() => calculateOverallTier(items), [items]);
1536
+ const sortedItems = React2.useMemo(() => {
1537
+ return [...items].sort((a, b) => {
1538
+ if (selectedMetric) {
1539
+ const aVal = selectedMetric === "deadCode" ? 100 - a.metrics[selectedMetric] : a.metrics[selectedMetric];
1540
+ const bVal = selectedMetric === "deadCode" ? 100 - b.metrics[selectedMetric] : b.metrics[selectedMetric];
1541
+ return bVal - aVal;
1542
+ } else {
1543
+ return calculateAverageScore(b.metrics) - calculateAverageScore(a.metrics);
1544
+ }
1545
+ });
1546
+ }, [items, selectedMetric]);
1609
1547
  const tierColors = {
1610
- none: theme2.colors.muted,
1611
- bronze: theme2.colors.warning,
1612
- silver: theme2.colors.secondary,
1613
- gold: theme2.colors.accent,
1614
- platinum: theme2.colors.primary
1548
+ none: theme.colors.muted,
1549
+ bronze: theme.colors.warning,
1550
+ silver: theme.colors.secondary,
1551
+ gold: theme.colors.accent,
1552
+ platinum: theme.colors.primary
1615
1553
  };
1616
1554
  const tierLabels = {
1617
1555
  none: "No Data",
@@ -1628,8 +1566,8 @@ function RepositoryQualityGrid({
1628
1566
  style: {
1629
1567
  padding: 40,
1630
1568
  textAlign: "center",
1631
- color: theme2.colors.textMuted,
1632
- backgroundColor: theme2.colors.background,
1569
+ color: theme.colors.textMuted,
1570
+ backgroundColor: theme.colors.background,
1633
1571
  borderRadius: 8
1634
1572
  },
1635
1573
  children: "No repositories to display"
@@ -1644,8 +1582,8 @@ function RepositoryQualityGrid({
1644
1582
  display: "flex",
1645
1583
  flexDirection: "column",
1646
1584
  gap: 16,
1647
- backgroundColor: theme2.colors.background,
1648
- fontFamily: theme2.fonts.body
1585
+ backgroundColor: theme.colors.background,
1586
+ fontFamily: theme.fonts.body
1649
1587
  },
1650
1588
  children: [
1651
1589
  showSummary && /* @__PURE__ */ jsxs(
@@ -1655,10 +1593,11 @@ function RepositoryQualityGrid({
1655
1593
  display: "flex",
1656
1594
  alignItems: "center",
1657
1595
  justifyContent: "space-between",
1596
+ flexWrap: "wrap",
1597
+ gap: 12,
1658
1598
  padding: "12px 16px",
1659
- backgroundColor: theme2.colors.surface,
1660
- borderRadius: 8,
1661
- border: `1px solid ${theme2.colors.border}`
1599
+ backgroundColor: theme.colors.surface,
1600
+ border: `1px solid ${theme.colors.border}`
1662
1601
  },
1663
1602
  children: [
1664
1603
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
@@ -1668,7 +1607,7 @@ function RepositoryQualityGrid({
1668
1607
  style: {
1669
1608
  fontSize: 14,
1670
1609
  fontWeight: 500,
1671
- color: theme2.colors.text
1610
+ color: theme.colors.text
1672
1611
  },
1673
1612
  children: [
1674
1613
  items.length,
@@ -1677,13 +1616,13 @@ function RepositoryQualityGrid({
1677
1616
  ]
1678
1617
  }
1679
1618
  ),
1680
- /* @__PURE__ */ jsx("span", { style: { color: theme2.colors.textMuted }, children: "•" }),
1619
+ /* @__PURE__ */ jsx("span", { style: { color: theme.colors.textMuted }, children: "•" }),
1681
1620
  /* @__PURE__ */ jsxs(
1682
1621
  "span",
1683
1622
  {
1684
1623
  style: {
1685
1624
  fontSize: 14,
1686
- color: theme2.colors.textMuted
1625
+ color: theme.colors.textMuted
1687
1626
  },
1688
1627
  children: [
1689
1628
  repositories.length,
@@ -1693,44 +1632,67 @@ function RepositoryQualityGrid({
1693
1632
  }
1694
1633
  )
1695
1634
  ] }),
1696
- /* @__PURE__ */ jsxs(
1697
- "div",
1698
- {
1699
- style: {
1700
- display: "flex",
1701
- alignItems: "center",
1702
- gap: 8,
1703
- padding: "4px 12px",
1704
- backgroundColor: theme2.colors.backgroundLight,
1705
- borderRadius: 16,
1706
- border: `1px solid ${tierColors[overallTier]}`
1707
- },
1708
- children: [
1709
- /* @__PURE__ */ jsx(
1710
- "span",
1711
- {
1712
- style: {
1713
- width: 8,
1714
- height: 8,
1715
- borderRadius: "50%",
1716
- backgroundColor: tierColors[overallTier]
1635
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
1636
+ /* @__PURE__ */ jsxs(
1637
+ "select",
1638
+ {
1639
+ value: selectedMetric ?? "",
1640
+ onChange: (e) => setSelectedMetric(e.target.value ? e.target.value : null),
1641
+ style: {
1642
+ padding: "4px 8px",
1643
+ fontSize: 13,
1644
+ backgroundColor: theme.colors.background,
1645
+ color: theme.colors.text,
1646
+ border: `1px solid ${theme.colors.border}`,
1647
+ borderRadius: 4,
1648
+ cursor: "pointer",
1649
+ outline: "none"
1650
+ },
1651
+ children: [
1652
+ /* @__PURE__ */ jsx("option", { value: "", children: "Select metric..." }),
1653
+ METRIC_OPTIONS.map((option) => /* @__PURE__ */ jsx("option", { value: option.key, children: option.label }, option.key))
1654
+ ]
1655
+ }
1656
+ ),
1657
+ /* @__PURE__ */ jsxs(
1658
+ "div",
1659
+ {
1660
+ style: {
1661
+ display: "flex",
1662
+ alignItems: "center",
1663
+ gap: 8,
1664
+ padding: "4px 12px",
1665
+ backgroundColor: theme.colors.backgroundLight,
1666
+ borderRadius: 16,
1667
+ border: `1px solid ${tierColors[overallTier]}`
1668
+ },
1669
+ children: [
1670
+ /* @__PURE__ */ jsx(
1671
+ "span",
1672
+ {
1673
+ style: {
1674
+ width: 8,
1675
+ height: 8,
1676
+ borderRadius: "50%",
1677
+ backgroundColor: tierColors[overallTier]
1678
+ }
1717
1679
  }
1718
- }
1719
- ),
1720
- /* @__PURE__ */ jsx(
1721
- "span",
1722
- {
1723
- style: {
1724
- fontSize: 13,
1725
- fontWeight: 500,
1726
- color: tierColors[overallTier]
1727
- },
1728
- children: tierLabels[overallTier]
1729
- }
1730
- )
1731
- ]
1732
- }
1733
- )
1680
+ ),
1681
+ /* @__PURE__ */ jsx(
1682
+ "span",
1683
+ {
1684
+ style: {
1685
+ fontSize: 13,
1686
+ fontWeight: 500,
1687
+ color: tierColors[overallTier]
1688
+ },
1689
+ children: tierLabels[overallTier]
1690
+ }
1691
+ )
1692
+ ]
1693
+ }
1694
+ )
1695
+ ] })
1734
1696
  ]
1735
1697
  }
1736
1698
  ),
@@ -1738,17 +1700,20 @@ function RepositoryQualityGrid({
1738
1700
  "div",
1739
1701
  {
1740
1702
  style: {
1741
- display: "flex",
1742
- flexWrap: "wrap",
1743
- gap: 12
1703
+ display: "grid",
1704
+ gridTemplateColumns: "repeat(auto-fill, minmax(220px, 1fr))",
1705
+ gap: 12,
1706
+ padding: 16
1744
1707
  },
1745
- children: items.map((item) => /* @__PURE__ */ jsx(
1708
+ children: sortedItems.map((item) => /* @__PURE__ */ jsx(
1746
1709
  RepositoryQualityGridItem,
1747
1710
  {
1748
1711
  item,
1749
- theme: theme2,
1712
+ theme,
1750
1713
  onClick: onItemClick ? () => onItemClick(item) : void 0,
1751
- showRepositoryName
1714
+ onVertexClick,
1715
+ showRepositoryName,
1716
+ selectedMetric
1752
1717
  },
1753
1718
  item.key
1754
1719
  ))
@@ -1758,6 +1723,158 @@ function RepositoryQualityGrid({
1758
1723
  }
1759
1724
  );
1760
1725
  }
1726
+ const mockRepositories = [
1727
+ {
1728
+ id: "platform",
1729
+ name: "platform",
1730
+ packages: [
1731
+ {
1732
+ name: "@org/core",
1733
+ version: "2.0.0",
1734
+ metrics: { tests: 94, deadCode: 4, linting: 98, formatting: 100, types: 97, documentation: 90 }
1735
+ },
1736
+ {
1737
+ name: "@org/ui-components",
1738
+ version: "2.0.0",
1739
+ metrics: { tests: 85, deadCode: 8, linting: 95, formatting: 98, types: 92, documentation: 80 }
1740
+ },
1741
+ {
1742
+ name: "@org/hooks",
1743
+ version: "2.0.0",
1744
+ metrics: { tests: 88, deadCode: 6, linting: 96, formatting: 100, types: 94, documentation: 85 }
1745
+ }
1746
+ ]
1747
+ },
1748
+ {
1749
+ id: "backend",
1750
+ name: "backend-services",
1751
+ packages: [
1752
+ {
1753
+ name: "backend-services",
1754
+ version: "1.5.0",
1755
+ metrics: { tests: 80, deadCode: 15, linting: 90, formatting: 95, types: 85, documentation: 72 }
1756
+ }
1757
+ ]
1758
+ },
1759
+ {
1760
+ id: "docs",
1761
+ name: "documentation-site",
1762
+ packages: [
1763
+ {
1764
+ name: "documentation-site",
1765
+ version: "1.0.0",
1766
+ metrics: { tests: 45, deadCode: 25, linting: 75, formatting: 85, types: 60, documentation: 95 }
1767
+ }
1768
+ ]
1769
+ }
1770
+ ];
1771
+ const RepositoryQualityGridPanelContent = ({
1772
+ context,
1773
+ events
1774
+ }) => {
1775
+ var _a;
1776
+ const { theme } = useTheme();
1777
+ const qualitySlice = context.getSlice("repositoriesQuality");
1778
+ const hasQualitySlice = context.hasSlice("repositoriesQuality");
1779
+ const isLoading = (qualitySlice == null ? void 0 : qualitySlice.loading) ?? false;
1780
+ const repositories = React2__default.useMemo(() => {
1781
+ var _a2;
1782
+ if ((_a2 = qualitySlice == null ? void 0 : qualitySlice.data) == null ? void 0 : _a2.repositories) {
1783
+ return qualitySlice.data.repositories;
1784
+ }
1785
+ if (hasQualitySlice) {
1786
+ return [];
1787
+ }
1788
+ return mockRepositories;
1789
+ }, [(_a = qualitySlice == null ? void 0 : qualitySlice.data) == null ? void 0 : _a.repositories, hasQualitySlice]);
1790
+ const handleItemClick = (item) => {
1791
+ events.emit({
1792
+ type: "principal-ade.repository-quality-grid:item:click",
1793
+ source: "principal-ade.repository-quality-grid-panel",
1794
+ timestamp: Date.now(),
1795
+ payload: {
1796
+ repositoryId: item.repositoryId,
1797
+ repositoryName: item.repositoryName,
1798
+ packageName: item.packageName,
1799
+ tier: item.tier
1800
+ }
1801
+ });
1802
+ };
1803
+ const handleVertexClick = (item, vertex) => {
1804
+ events.emit({
1805
+ type: "principal-ade.repository-quality-grid:vertex:click",
1806
+ source: "principal-ade.repository-quality-grid-panel",
1807
+ timestamp: Date.now(),
1808
+ payload: {
1809
+ repositoryId: item.repositoryId,
1810
+ repositoryName: item.repositoryName,
1811
+ packageName: item.packageName,
1812
+ metric: vertex.key,
1813
+ label: vertex.label,
1814
+ value: vertex.value
1815
+ }
1816
+ });
1817
+ };
1818
+ const handleRefresh = async () => {
1819
+ if (context.hasSlice("repositoriesQuality")) {
1820
+ await context.refresh("workspace", "repositoriesQuality");
1821
+ }
1822
+ };
1823
+ React2__default.useEffect(() => {
1824
+ const unsubscribers = [
1825
+ events.on("principal-ade.repository-quality-grid:refresh", async () => {
1826
+ await handleRefresh();
1827
+ })
1828
+ ];
1829
+ return () => unsubscribers.forEach((unsub) => unsub());
1830
+ }, [events, context]);
1831
+ return /* @__PURE__ */ jsx(
1832
+ "div",
1833
+ {
1834
+ style: {
1835
+ fontFamily: theme.fonts.body,
1836
+ height: "100%",
1837
+ minHeight: 0,
1838
+ backgroundColor: theme.colors.background,
1839
+ color: theme.colors.text,
1840
+ overflowY: "auto",
1841
+ boxSizing: "border-box"
1842
+ },
1843
+ children: isLoading ? /* @__PURE__ */ jsx(
1844
+ "div",
1845
+ {
1846
+ style: {
1847
+ padding: 40,
1848
+ textAlign: "center",
1849
+ color: theme.colors.textMuted
1850
+ },
1851
+ children: "Loading repository quality metrics..."
1852
+ }
1853
+ ) : repositories.length === 0 ? /* @__PURE__ */ jsx(
1854
+ "div",
1855
+ {
1856
+ style: {
1857
+ padding: 40,
1858
+ textAlign: "center",
1859
+ color: theme.colors.textMuted
1860
+ },
1861
+ children: "No repositories with quality metrics found."
1862
+ }
1863
+ ) : /* @__PURE__ */ jsx(
1864
+ RepositoryQualityGrid,
1865
+ {
1866
+ repositories,
1867
+ theme,
1868
+ onItemClick: handleItemClick,
1869
+ onVertexClick: handleVertexClick,
1870
+ showRepositoryName: true,
1871
+ showSummary: true
1872
+ }
1873
+ )
1874
+ }
1875
+ );
1876
+ };
1877
+ const RepositoryQualityGridPanel = RepositoryQualityGridPanelContent;
1761
1878
  const panels = [
1762
1879
  {
1763
1880
  metadata: {
@@ -1784,6 +1901,28 @@ const panels = [
1784
1901
  onUnmount: async (_context) => {
1785
1902
  console.log("Quality Hexagon Panel unmounting");
1786
1903
  }
1904
+ },
1905
+ {
1906
+ metadata: {
1907
+ id: "principal-ade.repository-quality-grid-panel",
1908
+ name: "Repository Quality Grid",
1909
+ icon: "⬡",
1910
+ version: "0.1.0",
1911
+ author: "Principal ADE",
1912
+ description: "Display quality metrics for multiple repositories in a flat grid layout. Supports filtering by metric type, sorting, and comparing quality across projects.",
1913
+ slices: ["repositoriesQuality"],
1914
+ tools: []
1915
+ },
1916
+ component: RepositoryQualityGridPanel,
1917
+ onMount: async (context) => {
1918
+ console.log("Repository Quality Grid Panel mounted");
1919
+ if (context.hasSlice("repositoriesQuality") && !context.isSliceLoading("repositoriesQuality")) {
1920
+ await context.refresh("workspace", "repositoriesQuality");
1921
+ }
1922
+ },
1923
+ onUnmount: async (_context) => {
1924
+ console.log("Repository Quality Grid Panel unmounting");
1925
+ }
1787
1926
  }
1788
1927
  ];
1789
1928
  const onPackageLoad = async () => {
@@ -1800,6 +1939,7 @@ export {
1800
1939
  QualityHexagonPanel,
1801
1940
  RepositoryQualityGrid,
1802
1941
  RepositoryQualityGridItem,
1942
+ RepositoryQualityGridPanel,
1803
1943
  calculateQualityTier,
1804
1944
  onPackageLoad,
1805
1945
  onPackageUnload,