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

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,5 +1,6 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
- import React2, { forwardRef, createElement, createContext, useState, useEffect, useContext } from "react";
2
+ import * as React2 from "react";
3
+ import React2__default, { forwardRef, createElement, createContext, useState, useEffect, useContext } from "react";
3
4
  /**
4
5
  * @license lucide-react v0.552.0 - ISC
5
6
  *
@@ -105,7 +106,34 @@ const createLucideIcon = (iconName, iconNode) => {
105
106
  * This source code is licensed under the ISC license.
106
107
  * See the LICENSE file in the root directory of this source tree.
107
108
  */
108
- const __iconNode = [
109
+ const __iconNode$4 = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
110
+ const Check = createLucideIcon("check", __iconNode$4);
111
+ /**
112
+ * @license lucide-react v0.552.0 - ISC
113
+ *
114
+ * This source code is licensed under the ISC license.
115
+ * See the LICENSE file in the root directory of this source tree.
116
+ */
117
+ const __iconNode$3 = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
118
+ const ChevronRight = createLucideIcon("chevron-right", __iconNode$3);
119
+ /**
120
+ * @license lucide-react v0.552.0 - ISC
121
+ *
122
+ * This source code is licensed under the ISC license.
123
+ * See the LICENSE file in the root directory of this source tree.
124
+ */
125
+ const __iconNode$2 = [
126
+ ["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2", key: "17jyea" }],
127
+ ["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
128
+ ];
129
+ const Copy = createLucideIcon("copy", __iconNode$2);
130
+ /**
131
+ * @license lucide-react v0.552.0 - ISC
132
+ *
133
+ * This source code is licensed under the ISC license.
134
+ * See the LICENSE file in the root directory of this source tree.
135
+ */
136
+ const __iconNode$1 = [
109
137
  [
110
138
  "path",
111
139
  {
@@ -114,7 +142,18 @@ const __iconNode = [
114
142
  }
115
143
  ]
116
144
  ];
117
- const Hexagon = createLucideIcon("hexagon", __iconNode);
145
+ const Hexagon = createLucideIcon("hexagon", __iconNode$1);
146
+ /**
147
+ * @license lucide-react v0.552.0 - ISC
148
+ *
149
+ * This source code is licensed under the ISC license.
150
+ * See the LICENSE file in the root directory of this source tree.
151
+ */
152
+ const __iconNode = [
153
+ ["path", { d: "M12 19h8", key: "baeox8" }],
154
+ ["path", { d: "m4 17 6-6-6-6", key: "1yngyt" }]
155
+ ];
156
+ const Terminal = createLucideIcon("terminal", __iconNode);
118
157
  var terminalTheme = {
119
158
  space: [0, 4, 8, 16, 32, 64, 128, 256, 512],
120
159
  fonts: {
@@ -296,7 +335,7 @@ var ThemeProvider = ({
296
335
  initialMode
297
336
  }) => {
298
337
  const [mode, setMode] = useState(initialMode);
299
- const activeTheme = React2.useMemo(() => {
338
+ const activeTheme = React2__default.useMemo(() => {
300
339
  if (!mode || !customTheme.modes || !customTheme.modes[mode]) {
301
340
  return customTheme;
302
341
  }
@@ -325,7 +364,7 @@ var ThemeProvider = ({
325
364
  mode,
326
365
  setMode
327
366
  };
328
- return /* @__PURE__ */ React2.createElement(ThemeContextSingleton.Provider, {
367
+ return /* @__PURE__ */ React2__default.createElement(ThemeContextSingleton.Provider, {
329
368
  value
330
369
  }, children);
331
370
  };
@@ -726,6 +765,202 @@ function QualityHexagonDetailed({
726
765
  }
727
766
  );
728
767
  }
768
+ function QualityHexagonExpandable({
769
+ metrics,
770
+ tier,
771
+ theme: theme2,
772
+ className,
773
+ packageName,
774
+ packageVersion,
775
+ onRefresh,
776
+ isRefreshing = false,
777
+ defaultExpanded = false
778
+ }) {
779
+ const [expanded, setExpanded] = React2.useState(defaultExpanded);
780
+ const themeColors = getThemeColors(theme2);
781
+ const colors = themeColors.tierColors[tier] ?? themeColors.tierColors.none;
782
+ const metricConfig = getMetricConfig(themeColors);
783
+ const hasHeader = packageName || onRefresh;
784
+ return /* @__PURE__ */ jsxs(
785
+ "div",
786
+ {
787
+ className: cn(className),
788
+ style: {
789
+ display: "flex",
790
+ flexDirection: "column",
791
+ gap: 12,
792
+ padding: 16,
793
+ borderRadius: 8,
794
+ backgroundColor: colors.bg,
795
+ flex: "1 1 200px"
796
+ },
797
+ children: [
798
+ hasHeader && /* @__PURE__ */ jsxs("div", { style: {
799
+ display: "flex",
800
+ alignItems: "center",
801
+ justifyContent: "space-between",
802
+ gap: 12
803
+ }, children: [
804
+ packageName ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 2 }, children: [
805
+ packageName.startsWith("@") && packageName.includes("/") ? /* @__PURE__ */ jsxs(Fragment, { children: [
806
+ /* @__PURE__ */ jsx("span", { style: {
807
+ fontSize: 12,
808
+ color: theme2.colors.textMuted
809
+ }, children: packageName.split("/")[0] }),
810
+ /* @__PURE__ */ jsx("span", { style: {
811
+ fontSize: 14,
812
+ fontWeight: 500,
813
+ color: colors.stroke
814
+ }, children: packageName.split("/")[1] })
815
+ ] }) : /* @__PURE__ */ jsx("span", { style: {
816
+ fontSize: 14,
817
+ fontWeight: 500,
818
+ color: colors.stroke
819
+ }, children: packageName }),
820
+ packageVersion && /* @__PURE__ */ jsxs("span", { style: {
821
+ fontSize: 12,
822
+ color: theme2.colors.textMuted
823
+ }, children: [
824
+ "v",
825
+ packageVersion
826
+ ] })
827
+ ] }) : /* @__PURE__ */ jsx("span", {}),
828
+ onRefresh && /* @__PURE__ */ jsx(
829
+ "button",
830
+ {
831
+ onClick: onRefresh,
832
+ disabled: isRefreshing,
833
+ style: {
834
+ padding: 6,
835
+ display: "flex",
836
+ alignItems: "center",
837
+ justifyContent: "center",
838
+ border: `1px solid ${theme2.colors.border}`,
839
+ borderRadius: 4,
840
+ background: theme2.colors.surface,
841
+ color: theme2.colors.textMuted,
842
+ cursor: isRefreshing ? "not-allowed" : "pointer",
843
+ opacity: isRefreshing ? 0.6 : 1
844
+ },
845
+ title: "Refresh",
846
+ children: /* @__PURE__ */ jsxs(
847
+ "svg",
848
+ {
849
+ width: "14",
850
+ height: "14",
851
+ viewBox: "0 0 24 24",
852
+ fill: "none",
853
+ stroke: "currentColor",
854
+ strokeWidth: "2",
855
+ strokeLinecap: "round",
856
+ strokeLinejoin: "round",
857
+ style: {
858
+ animation: isRefreshing ? "spin 1s linear infinite" : "none"
859
+ },
860
+ children: [
861
+ /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }),
862
+ /* @__PURE__ */ jsx("path", { d: "M3 3v5h5" }),
863
+ /* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16" }),
864
+ /* @__PURE__ */ jsx("path", { d: "M16 16h5v5" })
865
+ ]
866
+ }
867
+ )
868
+ }
869
+ )
870
+ ] }),
871
+ /* @__PURE__ */ jsx(
872
+ "div",
873
+ {
874
+ onClick: () => setExpanded(!expanded),
875
+ style: {
876
+ cursor: "pointer",
877
+ display: "flex",
878
+ justifyContent: "center",
879
+ alignItems: "center"
880
+ },
881
+ children: /* @__PURE__ */ jsx("div", { style: { width: 200, height: 200 }, children: /* @__PURE__ */ jsx(
882
+ QualityHexagon,
883
+ {
884
+ metrics,
885
+ tier,
886
+ theme: theme2,
887
+ showLabels: true,
888
+ showValues: false
889
+ }
890
+ ) })
891
+ }
892
+ ),
893
+ /* @__PURE__ */ jsx(
894
+ "div",
895
+ {
896
+ style: {
897
+ display: "grid",
898
+ gridTemplateRows: expanded ? "1fr" : "0fr",
899
+ transition: "grid-template-rows 0.3s ease"
900
+ },
901
+ children: /* @__PURE__ */ jsx("div", { style: { overflow: "hidden" }, children: /* @__PURE__ */ jsx("div", { style: {
902
+ display: "flex",
903
+ flexDirection: "column",
904
+ gap: 8,
905
+ padding: "8px 24px",
906
+ borderTop: `1px solid ${theme2.colors.border}`,
907
+ marginTop: 8
908
+ }, children: metricConfig.map(({ key, label, color }) => {
909
+ const value = metrics[key];
910
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }, children: [
911
+ /* @__PURE__ */ jsxs("span", { style: {
912
+ fontSize: 14,
913
+ color: theme2.colors.textMuted
914
+ }, children: [
915
+ label,
916
+ key === "deadCode" ? " ↓" : ""
917
+ ] }),
918
+ /* @__PURE__ */ jsxs("span", { style: {
919
+ fontSize: 14,
920
+ fontWeight: 500,
921
+ color
922
+ }, children: [
923
+ value,
924
+ "%"
925
+ ] })
926
+ ] }, key);
927
+ }) }) })
928
+ }
929
+ ),
930
+ /* @__PURE__ */ jsx(
931
+ "div",
932
+ {
933
+ onClick: () => setExpanded(!expanded),
934
+ style: {
935
+ display: "flex",
936
+ justifyContent: "center",
937
+ cursor: "pointer",
938
+ padding: 4
939
+ },
940
+ children: /* @__PURE__ */ jsx(
941
+ "svg",
942
+ {
943
+ width: "16",
944
+ height: "16",
945
+ viewBox: "0 0 24 24",
946
+ fill: "none",
947
+ stroke: theme2.colors.textMuted,
948
+ strokeWidth: "2",
949
+ strokeLinecap: "round",
950
+ strokeLinejoin: "round",
951
+ style: {
952
+ transform: expanded ? "rotate(180deg)" : "rotate(0deg)",
953
+ transition: "transform 0.3s ease"
954
+ },
955
+ children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
956
+ }
957
+ )
958
+ }
959
+ )
960
+ ]
961
+ }
962
+ );
963
+ }
729
964
  function calculateQualityTier(metrics) {
730
965
  const metricsForAverage = { ...metrics };
731
966
  metricsForAverage.deadCode = 100 - metricsForAverage.deadCode;
@@ -736,6 +971,322 @@ function calculateQualityTier(metrics) {
736
971
  if (average >= 40) return "bronze";
737
972
  return "none";
738
973
  }
974
+ const WORKFLOW_FILE_PATH = ".github/workflows/quality-lens.yml";
975
+ function checkFileExistsInTree(treeData, targetPath) {
976
+ if (!(treeData == null ? void 0 : treeData.allFiles)) return false;
977
+ const normalizedTarget = targetPath.replace(/^\//, "").toLowerCase();
978
+ return treeData.allFiles.some((file) => {
979
+ const filePath = (file.relativePath || file.path || "").toLowerCase();
980
+ return filePath.endsWith(normalizedTarget) || filePath === normalizedTarget;
981
+ });
982
+ }
983
+ const CommandLine = ({ command, theme: theme2 }) => {
984
+ const [copied, setCopied] = React2__default.useState(false);
985
+ const handleCopy = async () => {
986
+ try {
987
+ await navigator.clipboard.writeText(command);
988
+ setCopied(true);
989
+ setTimeout(() => setCopied(false), 2e3);
990
+ } catch {
991
+ console.log("Copy:", command);
992
+ }
993
+ };
994
+ return /* @__PURE__ */ jsxs(
995
+ "div",
996
+ {
997
+ style: {
998
+ display: "flex",
999
+ alignItems: "center",
1000
+ justifyContent: "space-between",
1001
+ gap: 12,
1002
+ padding: "10px 14px",
1003
+ borderRadius: 6,
1004
+ backgroundColor: theme2.colors.background,
1005
+ border: `1px solid ${theme2.colors.border}`,
1006
+ fontFamily: "monospace",
1007
+ fontSize: 13
1008
+ },
1009
+ children: [
1010
+ /* @__PURE__ */ jsx("code", { style: { color: theme2.colors.text }, children: command }),
1011
+ /* @__PURE__ */ jsx(
1012
+ "button",
1013
+ {
1014
+ onClick: handleCopy,
1015
+ style: {
1016
+ display: "flex",
1017
+ alignItems: "center",
1018
+ justifyContent: "center",
1019
+ padding: 4,
1020
+ border: "none",
1021
+ backgroundColor: "transparent",
1022
+ color: theme2.colors.textMuted,
1023
+ cursor: "pointer"
1024
+ },
1025
+ title: "Copy command",
1026
+ children: copied ? /* @__PURE__ */ jsx(Check, { size: 16, color: theme2.colors.success }) : /* @__PURE__ */ jsx(Copy, { size: 16 })
1027
+ }
1028
+ )
1029
+ ]
1030
+ }
1031
+ );
1032
+ };
1033
+ const QualityEmptyState = ({
1034
+ theme: theme2,
1035
+ hasWorkflow
1036
+ }) => {
1037
+ if (hasWorkflow) {
1038
+ return /* @__PURE__ */ jsxs(
1039
+ "div",
1040
+ {
1041
+ style: {
1042
+ display: "flex",
1043
+ flexDirection: "column",
1044
+ padding: "16px 0",
1045
+ gap: 16,
1046
+ width: "100%"
1047
+ },
1048
+ children: [
1049
+ /* @__PURE__ */ jsx(
1050
+ "p",
1051
+ {
1052
+ style: {
1053
+ margin: 0,
1054
+ fontSize: 14,
1055
+ color: theme2.colors.textMuted,
1056
+ lineHeight: 1.5
1057
+ },
1058
+ children: "Quality metrics will appear here after your next CI run completes."
1059
+ }
1060
+ ),
1061
+ /* @__PURE__ */ jsxs(
1062
+ "div",
1063
+ {
1064
+ style: {
1065
+ display: "flex",
1066
+ alignItems: "center",
1067
+ gap: 8,
1068
+ padding: "10px 14px",
1069
+ borderRadius: 6,
1070
+ backgroundColor: `${theme2.colors.success}15`,
1071
+ color: theme2.colors.success,
1072
+ fontSize: 13
1073
+ },
1074
+ children: [
1075
+ /* @__PURE__ */ jsx(Check, { size: 16 }),
1076
+ /* @__PURE__ */ jsxs("span", { children: [
1077
+ "Workflow detected at ",
1078
+ WORKFLOW_FILE_PATH
1079
+ ] })
1080
+ ]
1081
+ }
1082
+ )
1083
+ ]
1084
+ }
1085
+ );
1086
+ }
1087
+ return /* @__PURE__ */ jsxs(
1088
+ "div",
1089
+ {
1090
+ style: {
1091
+ display: "flex",
1092
+ flexDirection: "column",
1093
+ padding: "16px 0",
1094
+ gap: 16,
1095
+ width: "100%"
1096
+ },
1097
+ children: [
1098
+ /* @__PURE__ */ jsx(
1099
+ "p",
1100
+ {
1101
+ style: {
1102
+ margin: 0,
1103
+ fontSize: 14,
1104
+ color: theme2.colors.textMuted,
1105
+ lineHeight: 1.5
1106
+ },
1107
+ children: "Track your code quality with automated analysis of tests, linting, types, formatting, dead code, and documentation."
1108
+ }
1109
+ ),
1110
+ /* @__PURE__ */ jsxs(
1111
+ "div",
1112
+ {
1113
+ style: {
1114
+ display: "flex",
1115
+ flexDirection: "column",
1116
+ gap: 16,
1117
+ padding: 20,
1118
+ borderRadius: 8,
1119
+ backgroundColor: theme2.colors.surface,
1120
+ border: `1px solid ${theme2.colors.border}`
1121
+ },
1122
+ children: [
1123
+ /* @__PURE__ */ jsxs(
1124
+ "div",
1125
+ {
1126
+ style: {
1127
+ display: "flex",
1128
+ alignItems: "center",
1129
+ gap: 10,
1130
+ marginBottom: 4
1131
+ },
1132
+ children: [
1133
+ /* @__PURE__ */ jsx(Terminal, { size: 20, color: theme2.colors.text }),
1134
+ /* @__PURE__ */ jsx(
1135
+ "h4",
1136
+ {
1137
+ style: {
1138
+ margin: 0,
1139
+ fontSize: 15,
1140
+ fontWeight: 600,
1141
+ color: theme2.colors.text
1142
+ },
1143
+ children: "Get Started"
1144
+ }
1145
+ )
1146
+ ]
1147
+ }
1148
+ ),
1149
+ /* @__PURE__ */ jsxs("div", { children: [
1150
+ /* @__PURE__ */ jsxs(
1151
+ "div",
1152
+ {
1153
+ style: {
1154
+ display: "flex",
1155
+ alignItems: "center",
1156
+ gap: 8,
1157
+ marginBottom: 8,
1158
+ fontSize: 13,
1159
+ color: theme2.colors.textMuted
1160
+ },
1161
+ children: [
1162
+ /* @__PURE__ */ jsx(
1163
+ "span",
1164
+ {
1165
+ style: {
1166
+ display: "flex",
1167
+ alignItems: "center",
1168
+ justifyContent: "center",
1169
+ width: 20,
1170
+ height: 20,
1171
+ borderRadius: "50%",
1172
+ backgroundColor: theme2.colors.primary,
1173
+ color: theme2.colors.background,
1174
+ fontSize: 11,
1175
+ fontWeight: 600
1176
+ },
1177
+ children: "1"
1178
+ }
1179
+ ),
1180
+ /* @__PURE__ */ jsx("span", { children: "Install the CLI" })
1181
+ ]
1182
+ }
1183
+ ),
1184
+ /* @__PURE__ */ jsx(
1185
+ CommandLine,
1186
+ {
1187
+ command: "npm install -g @principal-ai/quality-lens-cli",
1188
+ theme: theme2
1189
+ }
1190
+ )
1191
+ ] }),
1192
+ /* @__PURE__ */ jsxs("div", { children: [
1193
+ /* @__PURE__ */ jsxs(
1194
+ "div",
1195
+ {
1196
+ style: {
1197
+ display: "flex",
1198
+ alignItems: "center",
1199
+ gap: 8,
1200
+ marginBottom: 8,
1201
+ fontSize: 13,
1202
+ color: theme2.colors.textMuted
1203
+ },
1204
+ children: [
1205
+ /* @__PURE__ */ jsx(
1206
+ "span",
1207
+ {
1208
+ style: {
1209
+ display: "flex",
1210
+ alignItems: "center",
1211
+ justifyContent: "center",
1212
+ width: 20,
1213
+ height: 20,
1214
+ borderRadius: "50%",
1215
+ backgroundColor: theme2.colors.primary,
1216
+ color: theme2.colors.background,
1217
+ fontSize: 11,
1218
+ fontWeight: 600
1219
+ },
1220
+ children: "2"
1221
+ }
1222
+ ),
1223
+ /* @__PURE__ */ jsx("span", { children: "Check what quality tools are available" })
1224
+ ]
1225
+ }
1226
+ ),
1227
+ /* @__PURE__ */ jsx(CommandLine, { command: "quality-lens list", theme: theme2 })
1228
+ ] }),
1229
+ /* @__PURE__ */ jsxs("div", { children: [
1230
+ /* @__PURE__ */ jsxs(
1231
+ "div",
1232
+ {
1233
+ style: {
1234
+ display: "flex",
1235
+ alignItems: "center",
1236
+ gap: 8,
1237
+ marginBottom: 8,
1238
+ fontSize: 13,
1239
+ color: theme2.colors.textMuted
1240
+ },
1241
+ children: [
1242
+ /* @__PURE__ */ jsx(
1243
+ "span",
1244
+ {
1245
+ style: {
1246
+ display: "flex",
1247
+ alignItems: "center",
1248
+ justifyContent: "center",
1249
+ width: 20,
1250
+ height: 20,
1251
+ borderRadius: "50%",
1252
+ backgroundColor: theme2.colors.primary,
1253
+ color: theme2.colors.background,
1254
+ fontSize: 11,
1255
+ fontWeight: 600
1256
+ },
1257
+ children: "3"
1258
+ }
1259
+ ),
1260
+ /* @__PURE__ */ jsx("span", { children: "Set up the GitHub Action" })
1261
+ ]
1262
+ }
1263
+ ),
1264
+ /* @__PURE__ */ jsx(CommandLine, { command: "quality-lens init", theme: theme2 })
1265
+ ] }),
1266
+ /* @__PURE__ */ jsxs(
1267
+ "div",
1268
+ {
1269
+ style: {
1270
+ display: "flex",
1271
+ alignItems: "center",
1272
+ gap: 6,
1273
+ paddingTop: 8,
1274
+ fontSize: 13,
1275
+ color: theme2.colors.textMuted
1276
+ },
1277
+ children: [
1278
+ /* @__PURE__ */ jsx(ChevronRight, { size: 14 }),
1279
+ /* @__PURE__ */ jsx("span", { children: "Then commit and push to start tracking quality" })
1280
+ ]
1281
+ }
1282
+ )
1283
+ ]
1284
+ }
1285
+ )
1286
+ ]
1287
+ }
1288
+ );
1289
+ };
739
1290
  const mockPackages = [
740
1291
  {
741
1292
  name: "@principal-ade/code-quality-panels",
@@ -756,11 +1307,15 @@ const QualityHexagonPanelContent = ({
756
1307
  }) => {
757
1308
  var _a;
758
1309
  const { theme: theme2 } = useTheme();
759
- const [refreshingPackages, setRefreshingPackages] = React2.useState(/* @__PURE__ */ new Set());
1310
+ const [refreshingPackages, setRefreshingPackages] = React2__default.useState(/* @__PURE__ */ new Set());
760
1311
  const qualitySlice = context.getSlice("quality");
761
1312
  const hasQualitySlice = context.hasSlice("quality");
762
1313
  const isLoading = (qualitySlice == null ? void 0 : qualitySlice.loading) ?? false;
763
- const packages = React2.useMemo(() => {
1314
+ const fileTreeSlice = context.getSlice("fileTree");
1315
+ const hasWorkflow = React2__default.useMemo(() => {
1316
+ return checkFileExistsInTree((fileTreeSlice == null ? void 0 : fileTreeSlice.data) ?? void 0, WORKFLOW_FILE_PATH);
1317
+ }, [fileTreeSlice == null ? void 0 : fileTreeSlice.data]);
1318
+ const packages = React2__default.useMemo(() => {
764
1319
  var _a2;
765
1320
  if ((_a2 = qualitySlice == null ? void 0 : qualitySlice.data) == null ? void 0 : _a2.packages) {
766
1321
  return qualitySlice.data.packages;
@@ -770,20 +1325,6 @@ const QualityHexagonPanelContent = ({
770
1325
  }
771
1326
  return mockPackages;
772
1327
  }, [(_a = qualitySlice == null ? void 0 : qualitySlice.data) == null ? void 0 : _a.packages, hasQualitySlice]);
773
- const handleRefreshPackage = async (packageName) => {
774
- setRefreshingPackages((prev) => new Set(prev).add(packageName));
775
- try {
776
- if (context.hasSlice("quality")) {
777
- await context.refresh("repository", "quality");
778
- }
779
- } finally {
780
- setRefreshingPackages((prev) => {
781
- const next = new Set(prev);
782
- next.delete(packageName);
783
- return next;
784
- });
785
- }
786
- };
787
1328
  const handleRefreshAll = async () => {
788
1329
  const allNames = packages.map((p) => p.name);
789
1330
  setRefreshingPackages(new Set(allNames));
@@ -795,7 +1336,7 @@ const QualityHexagonPanelContent = ({
795
1336
  setRefreshingPackages(/* @__PURE__ */ new Set());
796
1337
  }
797
1338
  };
798
- React2.useEffect(() => {
1339
+ React2__default.useEffect(() => {
799
1340
  const unsubscribers = [
800
1341
  events.on("principal-ade.quality-panel:refresh", async () => {
801
1342
  await handleRefreshAll();
@@ -820,94 +1361,403 @@ const QualityHexagonPanelContent = ({
820
1361
  documentation: acc.documentation + pkg.metrics.documentation / packages.length
821
1362
  }), { tests: 0, deadCode: 0, linting: 0, formatting: 0, types: 0, documentation: 0 })
822
1363
  ) : "none";
823
- return /* @__PURE__ */ jsxs(
1364
+ return /* @__PURE__ */ jsx(
824
1365
  "div",
825
1366
  {
826
1367
  style: {
827
- padding: 20,
828
1368
  fontFamily: theme2.fonts.body,
829
1369
  height: "100%",
830
- display: "flex",
831
- flexDirection: "column",
832
- gap: 16,
1370
+ minHeight: 0,
833
1371
  backgroundColor: theme2.colors.background,
834
1372
  color: theme2.colors.text,
835
1373
  overflowY: "auto",
836
1374
  boxSizing: "border-box"
837
1375
  },
1376
+ children: /* @__PURE__ */ jsxs(
1377
+ "div",
1378
+ {
1379
+ style: {
1380
+ padding: 20,
1381
+ display: "flex",
1382
+ flexDirection: "column",
1383
+ gap: 16
1384
+ },
1385
+ children: [
1386
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
1387
+ /* @__PURE__ */ jsx(Hexagon, { size: 24, color: tierColors[overallTier] }),
1388
+ /* @__PURE__ */ jsx(
1389
+ "h2",
1390
+ {
1391
+ style: {
1392
+ margin: 0,
1393
+ fontSize: 20,
1394
+ fontWeight: 600,
1395
+ color: theme2.colors.text
1396
+ },
1397
+ children: "Code Quality"
1398
+ }
1399
+ ),
1400
+ /* @__PURE__ */ jsx(
1401
+ "span",
1402
+ {
1403
+ title: "Platinum: 90%+ avg | Gold: 75%+ | Silver: 60%+ | Bronze: 40%+",
1404
+ style: {
1405
+ display: "inline-flex",
1406
+ alignItems: "center",
1407
+ justifyContent: "center",
1408
+ width: 18,
1409
+ height: 18,
1410
+ borderRadius: "50%",
1411
+ border: `1px solid ${theme2.colors.border}`,
1412
+ fontSize: 12,
1413
+ color: theme2.colors.textMuted,
1414
+ cursor: "help"
1415
+ },
1416
+ children: "?"
1417
+ }
1418
+ ),
1419
+ packages.length > 1 && /* @__PURE__ */ jsxs("span", { style: {
1420
+ fontSize: 14,
1421
+ color: theme2.colors.textMuted
1422
+ }, children: [
1423
+ packages.length,
1424
+ " packages"
1425
+ ] })
1426
+ ] }),
1427
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 16, minHeight: 0 }, children: isLoading ? /* @__PURE__ */ jsx("div", { style: {
1428
+ padding: 40,
1429
+ textAlign: "center",
1430
+ color: theme2.colors.textMuted
1431
+ }, children: "Loading quality metrics..." }) : packages.length === 0 ? /* @__PURE__ */ jsx(
1432
+ QualityEmptyState,
1433
+ {
1434
+ theme: theme2,
1435
+ hasWorkflow
1436
+ }
1437
+ ) : packages.map((pkg) => {
1438
+ const tier = calculateQualityTier(pkg.metrics);
1439
+ return /* @__PURE__ */ jsx(
1440
+ QualityHexagonExpandable,
1441
+ {
1442
+ metrics: pkg.metrics,
1443
+ tier,
1444
+ theme: theme2,
1445
+ packageName: pkg.name,
1446
+ packageVersion: pkg.version
1447
+ },
1448
+ pkg.name
1449
+ );
1450
+ }) })
1451
+ ]
1452
+ }
1453
+ )
1454
+ }
1455
+ );
1456
+ };
1457
+ const QualityHexagonPanel = (props) => {
1458
+ return /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(QualityHexagonPanelContent, { ...props }) });
1459
+ };
1460
+ function flattenRepositories(repositories) {
1461
+ const items = [];
1462
+ for (const repo of repositories) {
1463
+ for (const pkg of repo.packages) {
1464
+ items.push({
1465
+ key: `${repo.id}:${pkg.name}`,
1466
+ repositoryId: repo.id,
1467
+ repositoryName: repo.name,
1468
+ packageName: pkg.name,
1469
+ version: pkg.version,
1470
+ metrics: pkg.metrics,
1471
+ tier: calculateQualityTier(pkg.metrics)
1472
+ });
1473
+ }
1474
+ }
1475
+ return items;
1476
+ }
1477
+ function calculateOverallTier(items) {
1478
+ if (items.length === 0) return "none";
1479
+ const avgMetrics = items.reduce(
1480
+ (acc, item) => ({
1481
+ tests: acc.tests + item.metrics.tests / items.length,
1482
+ deadCode: acc.deadCode + item.metrics.deadCode / items.length,
1483
+ linting: acc.linting + item.metrics.linting / items.length,
1484
+ formatting: acc.formatting + item.metrics.formatting / items.length,
1485
+ types: acc.types + item.metrics.types / items.length,
1486
+ documentation: acc.documentation + item.metrics.documentation / items.length
1487
+ }),
1488
+ { tests: 0, deadCode: 0, linting: 0, formatting: 0, types: 0, documentation: 0 }
1489
+ );
1490
+ return calculateQualityTier(avgMetrics);
1491
+ }
1492
+ function formatLabel(item, showRepositoryName, isSameAsRepo) {
1493
+ if (!showRepositoryName || isSameAsRepo) {
1494
+ return item.packageName;
1495
+ }
1496
+ return `${item.repositoryName} / ${item.packageName}`;
1497
+ }
1498
+ function RepositoryQualityGridItem({
1499
+ item,
1500
+ theme: theme2,
1501
+ onClick,
1502
+ showRepositoryName = true,
1503
+ className
1504
+ }) {
1505
+ const isSameAsRepo = item.packageName === item.repositoryName;
1506
+ const label = formatLabel(item, showRepositoryName, isSameAsRepo);
1507
+ 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
1513
+ };
1514
+ return /* @__PURE__ */ jsxs(
1515
+ "div",
1516
+ {
1517
+ className: cn(className),
1518
+ onClick,
1519
+ style: {
1520
+ display: "flex",
1521
+ flexDirection: "column",
1522
+ alignItems: "center",
1523
+ gap: 8,
1524
+ padding: 12,
1525
+ borderRadius: 8,
1526
+ backgroundColor: theme2.colors.surface,
1527
+ border: `1px solid ${theme2.colors.border}`,
1528
+ cursor: onClick ? "pointer" : "default",
1529
+ transition: "all 0.2s ease",
1530
+ minWidth: 100
1531
+ },
1532
+ onMouseEnter: (e) => {
1533
+ if (onClick) {
1534
+ e.currentTarget.style.borderColor = tierColors[item.tier];
1535
+ e.currentTarget.style.transform = "translateY(-2px)";
1536
+ }
1537
+ },
1538
+ onMouseLeave: (e) => {
1539
+ e.currentTarget.style.borderColor = theme2.colors.border;
1540
+ e.currentTarget.style.transform = "translateY(0)";
1541
+ },
838
1542
  children: [
839
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
840
- /* @__PURE__ */ jsx(Hexagon, { size: 24, color: tierColors[overallTier] }),
841
- /* @__PURE__ */ jsx(
842
- "h2",
843
- {
844
- style: {
845
- margin: 0,
846
- fontSize: 20,
847
- fontWeight: 600,
848
- color: theme2.colors.text
849
- },
850
- children: "Code Quality"
851
- }
852
- ),
853
- /* @__PURE__ */ jsx(
854
- "span",
855
- {
856
- title: "Platinum: 90%+ avg | Gold: 75%+ | Silver: 60%+ | Bronze: 40%+",
857
- style: {
858
- display: "inline-flex",
859
- alignItems: "center",
860
- justifyContent: "center",
861
- width: 18,
862
- height: 18,
863
- borderRadius: "50%",
864
- border: `1px solid ${theme2.colors.border}`,
865
- fontSize: 12,
866
- color: theme2.colors.textMuted,
867
- cursor: "help"
868
- },
869
- children: "?"
870
- }
871
- ),
872
- packages.length > 1 && /* @__PURE__ */ jsxs("span", { style: {
873
- fontSize: 14,
874
- color: theme2.colors.textMuted
875
- }, children: [
876
- packages.length,
877
- " packages"
878
- ] })
879
- ] }),
880
- /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: isLoading ? /* @__PURE__ */ jsx("div", { style: {
881
- padding: 40,
882
- textAlign: "center",
883
- color: theme2.colors.textMuted
884
- }, children: "Loading quality metrics..." }) : packages.length === 0 ? /* @__PURE__ */ jsx("div", { style: {
1543
+ /* @__PURE__ */ jsx(
1544
+ QualityHexagonCompact,
1545
+ {
1546
+ metrics: item.metrics,
1547
+ tier: item.tier,
1548
+ theme: theme2
1549
+ }
1550
+ ),
1551
+ /* @__PURE__ */ jsxs(
1552
+ "div",
1553
+ {
1554
+ style: {
1555
+ display: "flex",
1556
+ flexDirection: "column",
1557
+ alignItems: "center",
1558
+ gap: 2,
1559
+ maxWidth: 120
1560
+ },
1561
+ children: [
1562
+ /* @__PURE__ */ jsx(
1563
+ "span",
1564
+ {
1565
+ style: {
1566
+ fontSize: 12,
1567
+ fontWeight: 500,
1568
+ color: theme2.colors.text,
1569
+ textAlign: "center",
1570
+ overflow: "hidden",
1571
+ textOverflow: "ellipsis",
1572
+ whiteSpace: "nowrap",
1573
+ maxWidth: "100%"
1574
+ },
1575
+ title: label,
1576
+ children: label
1577
+ }
1578
+ ),
1579
+ item.version && /* @__PURE__ */ jsxs(
1580
+ "span",
1581
+ {
1582
+ style: {
1583
+ fontSize: 10,
1584
+ color: theme2.colors.textMuted
1585
+ },
1586
+ children: [
1587
+ "v",
1588
+ item.version
1589
+ ]
1590
+ }
1591
+ )
1592
+ ]
1593
+ }
1594
+ )
1595
+ ]
1596
+ }
1597
+ );
1598
+ }
1599
+ function RepositoryQualityGrid({
1600
+ repositories,
1601
+ theme: theme2,
1602
+ onItemClick,
1603
+ className,
1604
+ showRepositoryName = true,
1605
+ showSummary = true
1606
+ }) {
1607
+ const items = React2.useMemo(() => flattenRepositories(repositories), [repositories]);
1608
+ const overallTier = React2.useMemo(() => calculateOverallTier(items), [items]);
1609
+ 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
1615
+ };
1616
+ const tierLabels = {
1617
+ none: "No Data",
1618
+ bronze: "Bronze",
1619
+ silver: "Silver",
1620
+ gold: "Gold",
1621
+ platinum: "Platinum"
1622
+ };
1623
+ if (items.length === 0) {
1624
+ return /* @__PURE__ */ jsx(
1625
+ "div",
1626
+ {
1627
+ className: cn(className),
1628
+ style: {
885
1629
  padding: 40,
886
1630
  textAlign: "center",
887
- color: theme2.colors.textMuted
888
- }, children: "No packages found" }) : packages.map((pkg) => {
889
- const tier = calculateQualityTier(pkg.metrics);
890
- return /* @__PURE__ */ jsx(
891
- QualityHexagonDetailed,
892
- {
893
- metrics: pkg.metrics,
894
- tier,
895
- theme: theme2,
896
- packageName: pkg.name,
897
- packageVersion: pkg.version,
898
- onRefresh: () => handleRefreshPackage(pkg.name),
899
- isRefreshing: refreshingPackages.has(pkg.name)
1631
+ color: theme2.colors.textMuted,
1632
+ backgroundColor: theme2.colors.background,
1633
+ borderRadius: 8
1634
+ },
1635
+ children: "No repositories to display"
1636
+ }
1637
+ );
1638
+ }
1639
+ return /* @__PURE__ */ jsxs(
1640
+ "div",
1641
+ {
1642
+ className: cn(className),
1643
+ style: {
1644
+ display: "flex",
1645
+ flexDirection: "column",
1646
+ gap: 16,
1647
+ backgroundColor: theme2.colors.background,
1648
+ fontFamily: theme2.fonts.body
1649
+ },
1650
+ children: [
1651
+ showSummary && /* @__PURE__ */ jsxs(
1652
+ "div",
1653
+ {
1654
+ style: {
1655
+ display: "flex",
1656
+ alignItems: "center",
1657
+ justifyContent: "space-between",
1658
+ padding: "12px 16px",
1659
+ backgroundColor: theme2.colors.surface,
1660
+ borderRadius: 8,
1661
+ border: `1px solid ${theme2.colors.border}`
900
1662
  },
901
- pkg.name
902
- );
903
- }) })
1663
+ children: [
1664
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
1665
+ /* @__PURE__ */ jsxs(
1666
+ "span",
1667
+ {
1668
+ style: {
1669
+ fontSize: 14,
1670
+ fontWeight: 500,
1671
+ color: theme2.colors.text
1672
+ },
1673
+ children: [
1674
+ items.length,
1675
+ " ",
1676
+ items.length === 1 ? "package" : "packages"
1677
+ ]
1678
+ }
1679
+ ),
1680
+ /* @__PURE__ */ jsx("span", { style: { color: theme2.colors.textMuted }, children: "•" }),
1681
+ /* @__PURE__ */ jsxs(
1682
+ "span",
1683
+ {
1684
+ style: {
1685
+ fontSize: 14,
1686
+ color: theme2.colors.textMuted
1687
+ },
1688
+ children: [
1689
+ repositories.length,
1690
+ " ",
1691
+ repositories.length === 1 ? "repository" : "repositories"
1692
+ ]
1693
+ }
1694
+ )
1695
+ ] }),
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]
1717
+ }
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
+ )
1734
+ ]
1735
+ }
1736
+ ),
1737
+ /* @__PURE__ */ jsx(
1738
+ "div",
1739
+ {
1740
+ style: {
1741
+ display: "flex",
1742
+ flexWrap: "wrap",
1743
+ gap: 12
1744
+ },
1745
+ children: items.map((item) => /* @__PURE__ */ jsx(
1746
+ RepositoryQualityGridItem,
1747
+ {
1748
+ item,
1749
+ theme: theme2,
1750
+ onClick: onItemClick ? () => onItemClick(item) : void 0,
1751
+ showRepositoryName
1752
+ },
1753
+ item.key
1754
+ ))
1755
+ }
1756
+ )
904
1757
  ]
905
1758
  }
906
1759
  );
907
- };
908
- const QualityHexagonPanel = (props) => {
909
- return /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(QualityHexagonPanelContent, { ...props }) });
910
- };
1760
+ }
911
1761
  const panels = [
912
1762
  {
913
1763
  metadata: {
@@ -946,7 +1796,10 @@ export {
946
1796
  QualityHexagon,
947
1797
  QualityHexagonCompact,
948
1798
  QualityHexagonDetailed,
1799
+ QualityHexagonExpandable,
949
1800
  QualityHexagonPanel,
1801
+ RepositoryQualityGrid,
1802
+ RepositoryQualityGridItem,
950
1803
  calculateQualityTier,
951
1804
  onPackageLoad,
952
1805
  onPackageUnload,