aiseo-audit 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -27,7 +27,7 @@ var import_commander = require("commander");
27
27
 
28
28
  // src/modules/analyzer/constants.ts
29
29
  var DOMAIN_SIGNAL_TIMEOUT_CAP = 5e3;
30
- var VERSION = true ? "1.0.0" : "0.0.0";
30
+ var VERSION = true ? "1.1.0" : "0.0.0";
31
31
 
32
32
  // src/modules/fetcher/constants.ts
33
33
  var MAX_RESPONSE_SIZE = 10 * 1024 * 1024;
@@ -1998,43 +1998,70 @@ function buildGaugeSvg(score, size = "small") {
1998
1998
  </svg>`;
1999
1999
  }
2000
2000
  function buildMultiSegmentGauge(overallScore, grade, totalPoints, maxPoints, categories) {
2001
- const radius = 56;
2002
- const circumference = 2 * Math.PI * radius;
2003
- const strokeWidth = 10;
2004
- const gapDeg = 1;
2005
- const totalGapDeg = gapDeg * categories.length;
2006
- const availableDeg = 360 - totalGapDeg;
2001
+ const radius = 80;
2002
+ const strokeWidth = 14;
2003
+ const pad2 = 8;
2004
+ const size = (radius + strokeWidth / 2 + pad2) * 2;
2005
+ const cx = size / 2;
2006
+ const cy = size / 2;
2007
+ const circ = 2 * Math.PI * radius;
2007
2008
  const textColor = scoreTextColorHex(overallScore);
2008
- const segments = [];
2009
- let currentAngle = -90;
2009
+ const trackCircle = `<circle cx="${cx}" cy="${cy}" r="${radius}" fill="none" stroke="#e8e8e8" stroke-width="${strokeWidth}"/>`;
2010
+ const arcs = [];
2011
+ let consumed = 0;
2012
+ let segIdx = 0;
2010
2013
  for (const cat of categories) {
2011
- const segmentDeg = maxPoints > 0 ? cat.maxScore / maxPoints * availableDeg : 0;
2012
- const segmentCirc = segmentDeg / 360 * circumference;
2013
- const fillRatio = cat.maxScore > 0 ? cat.score / cat.maxScore : 0;
2014
- const filledCirc = segmentCirc * fillRatio;
2015
- const catPct = cat.maxScore > 0 ? Math.round(fillRatio * 100) : 0;
2014
+ const catDeg = maxPoints > 0 ? cat.score / maxPoints * 360 : 0;
2015
+ if (catDeg < 0.1) {
2016
+ consumed += catDeg;
2017
+ continue;
2018
+ }
2019
+ const catPct = cat.maxScore > 0 ? Math.round(cat.score / cat.maxScore * 100) : 0;
2016
2020
  const color = scoreColorHex(catPct);
2017
- segments.push(
2018
- `<circle cx="60" cy="60" r="${radius}" fill="none" stroke="#e8e8e8" stroke-width="${strokeWidth}"
2019
- stroke-dasharray="${segmentCirc} ${circumference - segmentCirc}"
2020
- transform="rotate(${currentAngle} 60 60)"/>`
2021
+ const arcLen = catDeg / 360 * circ;
2022
+ const offset = circ * 0.25 - consumed / 360 * circ;
2023
+ const catName = escapeHtml(cat.name);
2024
+ const idx = segIdx;
2025
+ arcs.push(
2026
+ `<circle cx="${cx}" cy="${cy}" r="${radius}" fill="none"
2027
+ stroke="${color}" stroke-width="${strokeWidth}"
2028
+ stroke-dasharray="${arcLen.toFixed(2)} ${(circ - arcLen).toFixed(2)}"
2029
+ stroke-dashoffset="${offset.toFixed(2)}"
2030
+ class="seg-arc" data-idx="${idx}"
2031
+ onmouseenter="document.getElementById('seg-pop-${idx}').style.display='flex'"
2032
+ onmouseleave="document.getElementById('seg-pop-${idx}').style.display='none'"/>`
2021
2033
  );
2022
- if (filledCirc > 0) {
2023
- segments.push(
2024
- `<circle cx="60" cy="60" r="${radius}" fill="none" stroke="${color}" stroke-width="${strokeWidth}"
2025
- stroke-dasharray="${filledCirc} ${circumference - filledCirc}"
2026
- transform="rotate(${currentAngle} 60 60)"/>`
2027
- );
2028
- }
2029
- currentAngle += segmentDeg + gapDeg;
2034
+ consumed += catDeg;
2035
+ const divRad = consumed / 360 * 2 * Math.PI - Math.PI / 2;
2036
+ const half = strokeWidth / 2 + 1;
2037
+ const dx = Math.cos(divRad);
2038
+ const dy = Math.sin(divRad);
2039
+ arcs.push(
2040
+ `<line x1="${(cx + (radius - half) * dx).toFixed(2)}" y1="${(cy + (radius - half) * dy).toFixed(2)}"
2041
+ x2="${(cx + (radius + half) * dx).toFixed(2)}" y2="${(cy + (radius + half) * dy).toFixed(2)}"
2042
+ stroke="#fff" stroke-width="2" pointer-events="none"/>`
2043
+ );
2044
+ segIdx++;
2030
2045
  }
2046
+ const popovers = categories.filter((cat) => cat.score > 0).map((cat, i) => {
2047
+ const catPct = cat.maxScore > 0 ? Math.round(cat.score / cat.maxScore * 100) : 0;
2048
+ const color = scoreColorHex(catPct);
2049
+ return `<div id="seg-pop-${i}" class="seg-popover">
2050
+ <span class="seg-popover-dot" style="background:${color}"></span>
2051
+ <span class="seg-popover-name">${escapeHtml(cat.name)}</span>
2052
+ <span class="seg-popover-score">${catPct}%</span>
2053
+ <span class="seg-popover-pts">${cat.score}/${cat.maxScore} pts</span>
2054
+ </div>`;
2055
+ }).join("");
2031
2056
  return `<div class="overall-gauge-wrap">
2032
- <svg class="gauge" viewBox="0 0 120 120" width="160" height="160">
2033
- ${segments.join("\n ")}
2034
- <text x="60" y="54" text-anchor="middle" font-size="36" font-weight="700" fill="${textColor}">${overallScore}</text>
2035
- <text x="60" y="70" text-anchor="middle" font-size="12" font-weight="600" fill="${textColor}">${escapeHtml(grade)}</text>
2036
- <text x="60" y="84" text-anchor="middle" font-size="9" fill="#999">${totalPoints}/${maxPoints} pts</text>
2057
+ <svg class="gauge" viewBox="0 0 ${size} ${size}" width="${size}" height="${size}">
2058
+ ${trackCircle}
2059
+ ${arcs.join("\n ")}
2060
+ <text x="${cx}" y="${cy - 8}" text-anchor="middle" font-size="40" font-weight="700" fill="${textColor}">${overallScore}</text>
2061
+ <text x="${cx}" y="${cy + 14}" text-anchor="middle" font-size="14" font-weight="600" fill="${textColor}">${escapeHtml(grade)}</text>
2062
+ <text x="${cx}" y="${cy + 30}" text-anchor="middle" font-size="11" fill="#999">${totalPoints}/${maxPoints} pts</text>
2037
2063
  </svg>
2064
+ <div class="seg-popovers">${popovers}</div>
2038
2065
  <div class="score-scale">
2039
2066
  <span class="scale-fail">0-49</span>
2040
2067
  <span class="scale-average">50-89</span>
@@ -2222,15 +2249,57 @@ body {
2222
2249
  display: flex;
2223
2250
  align-items: center;
2224
2251
  flex-direction: column;
2225
- padding: 24px 0 20px;
2252
+ padding: 32px 0 24px;
2226
2253
  border-bottom: 1px solid var(--border);
2227
2254
  }
2228
2255
  .overall-gauge-wrap {
2229
2256
  display: flex;
2230
2257
  flex-direction: column;
2231
2258
  align-items: center;
2259
+ position: relative;
2260
+ }
2261
+ .overall-gauge-wrap .gauge {
2262
+ display: block;
2263
+ overflow: visible;
2264
+ }
2265
+ .seg-arc {
2266
+ cursor: pointer;
2267
+ transition: stroke-width 0.15s ease, filter 0.15s ease;
2268
+ }
2269
+ .seg-arc:hover {
2270
+ stroke-width: 20;
2271
+ filter: brightness(1.1);
2272
+ }
2273
+
2274
+ /* Segment popovers */
2275
+ .seg-popovers {
2276
+ position: relative;
2277
+ min-height: 36px;
2278
+ display: flex;
2279
+ justify-content: center;
2280
+ margin-top: 4px;
2281
+ }
2282
+ .seg-popover {
2283
+ display: none;
2284
+ align-items: center;
2285
+ gap: 8px;
2286
+ background: #fff;
2287
+ border: 1px solid var(--border);
2288
+ border-radius: 8px;
2289
+ padding: 8px 14px;
2290
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
2291
+ font-size: 13px;
2292
+ white-space: nowrap;
2293
+ }
2294
+ .seg-popover-dot {
2295
+ width: 10px;
2296
+ height: 10px;
2297
+ border-radius: 50%;
2298
+ flex-shrink: 0;
2232
2299
  }
2233
- .overall-gauge-wrap .gauge { display: block; }
2300
+ .seg-popover-name { font-weight: 600; }
2301
+ .seg-popover-score { font-weight: 700; }
2302
+ .seg-popover-pts { color: var(--text-secondary); }
2234
2303
 
2235
2304
  /* Score scale legend */
2236
2305
  .score-scale {
package/dist/cli.mjs CHANGED
@@ -3,7 +3,7 @@ import { Command } from "commander";
3
3
 
4
4
  // src/modules/analyzer/constants.ts
5
5
  var DOMAIN_SIGNAL_TIMEOUT_CAP = 5e3;
6
- var VERSION = true ? "1.0.0" : "0.0.0";
6
+ var VERSION = true ? "1.1.0" : "0.0.0";
7
7
 
8
8
  // src/modules/fetcher/constants.ts
9
9
  var MAX_RESPONSE_SIZE = 10 * 1024 * 1024;
@@ -1974,43 +1974,70 @@ function buildGaugeSvg(score, size = "small") {
1974
1974
  </svg>`;
1975
1975
  }
1976
1976
  function buildMultiSegmentGauge(overallScore, grade, totalPoints, maxPoints, categories) {
1977
- const radius = 56;
1978
- const circumference = 2 * Math.PI * radius;
1979
- const strokeWidth = 10;
1980
- const gapDeg = 1;
1981
- const totalGapDeg = gapDeg * categories.length;
1982
- const availableDeg = 360 - totalGapDeg;
1977
+ const radius = 80;
1978
+ const strokeWidth = 14;
1979
+ const pad2 = 8;
1980
+ const size = (radius + strokeWidth / 2 + pad2) * 2;
1981
+ const cx = size / 2;
1982
+ const cy = size / 2;
1983
+ const circ = 2 * Math.PI * radius;
1983
1984
  const textColor = scoreTextColorHex(overallScore);
1984
- const segments = [];
1985
- let currentAngle = -90;
1985
+ const trackCircle = `<circle cx="${cx}" cy="${cy}" r="${radius}" fill="none" stroke="#e8e8e8" stroke-width="${strokeWidth}"/>`;
1986
+ const arcs = [];
1987
+ let consumed = 0;
1988
+ let segIdx = 0;
1986
1989
  for (const cat of categories) {
1987
- const segmentDeg = maxPoints > 0 ? cat.maxScore / maxPoints * availableDeg : 0;
1988
- const segmentCirc = segmentDeg / 360 * circumference;
1989
- const fillRatio = cat.maxScore > 0 ? cat.score / cat.maxScore : 0;
1990
- const filledCirc = segmentCirc * fillRatio;
1991
- const catPct = cat.maxScore > 0 ? Math.round(fillRatio * 100) : 0;
1990
+ const catDeg = maxPoints > 0 ? cat.score / maxPoints * 360 : 0;
1991
+ if (catDeg < 0.1) {
1992
+ consumed += catDeg;
1993
+ continue;
1994
+ }
1995
+ const catPct = cat.maxScore > 0 ? Math.round(cat.score / cat.maxScore * 100) : 0;
1992
1996
  const color = scoreColorHex(catPct);
1993
- segments.push(
1994
- `<circle cx="60" cy="60" r="${radius}" fill="none" stroke="#e8e8e8" stroke-width="${strokeWidth}"
1995
- stroke-dasharray="${segmentCirc} ${circumference - segmentCirc}"
1996
- transform="rotate(${currentAngle} 60 60)"/>`
1997
+ const arcLen = catDeg / 360 * circ;
1998
+ const offset = circ * 0.25 - consumed / 360 * circ;
1999
+ const catName = escapeHtml(cat.name);
2000
+ const idx = segIdx;
2001
+ arcs.push(
2002
+ `<circle cx="${cx}" cy="${cy}" r="${radius}" fill="none"
2003
+ stroke="${color}" stroke-width="${strokeWidth}"
2004
+ stroke-dasharray="${arcLen.toFixed(2)} ${(circ - arcLen).toFixed(2)}"
2005
+ stroke-dashoffset="${offset.toFixed(2)}"
2006
+ class="seg-arc" data-idx="${idx}"
2007
+ onmouseenter="document.getElementById('seg-pop-${idx}').style.display='flex'"
2008
+ onmouseleave="document.getElementById('seg-pop-${idx}').style.display='none'"/>`
1997
2009
  );
1998
- if (filledCirc > 0) {
1999
- segments.push(
2000
- `<circle cx="60" cy="60" r="${radius}" fill="none" stroke="${color}" stroke-width="${strokeWidth}"
2001
- stroke-dasharray="${filledCirc} ${circumference - filledCirc}"
2002
- transform="rotate(${currentAngle} 60 60)"/>`
2003
- );
2004
- }
2005
- currentAngle += segmentDeg + gapDeg;
2010
+ consumed += catDeg;
2011
+ const divRad = consumed / 360 * 2 * Math.PI - Math.PI / 2;
2012
+ const half = strokeWidth / 2 + 1;
2013
+ const dx = Math.cos(divRad);
2014
+ const dy = Math.sin(divRad);
2015
+ arcs.push(
2016
+ `<line x1="${(cx + (radius - half) * dx).toFixed(2)}" y1="${(cy + (radius - half) * dy).toFixed(2)}"
2017
+ x2="${(cx + (radius + half) * dx).toFixed(2)}" y2="${(cy + (radius + half) * dy).toFixed(2)}"
2018
+ stroke="#fff" stroke-width="2" pointer-events="none"/>`
2019
+ );
2020
+ segIdx++;
2006
2021
  }
2022
+ const popovers = categories.filter((cat) => cat.score > 0).map((cat, i) => {
2023
+ const catPct = cat.maxScore > 0 ? Math.round(cat.score / cat.maxScore * 100) : 0;
2024
+ const color = scoreColorHex(catPct);
2025
+ return `<div id="seg-pop-${i}" class="seg-popover">
2026
+ <span class="seg-popover-dot" style="background:${color}"></span>
2027
+ <span class="seg-popover-name">${escapeHtml(cat.name)}</span>
2028
+ <span class="seg-popover-score">${catPct}%</span>
2029
+ <span class="seg-popover-pts">${cat.score}/${cat.maxScore} pts</span>
2030
+ </div>`;
2031
+ }).join("");
2007
2032
  return `<div class="overall-gauge-wrap">
2008
- <svg class="gauge" viewBox="0 0 120 120" width="160" height="160">
2009
- ${segments.join("\n ")}
2010
- <text x="60" y="54" text-anchor="middle" font-size="36" font-weight="700" fill="${textColor}">${overallScore}</text>
2011
- <text x="60" y="70" text-anchor="middle" font-size="12" font-weight="600" fill="${textColor}">${escapeHtml(grade)}</text>
2012
- <text x="60" y="84" text-anchor="middle" font-size="9" fill="#999">${totalPoints}/${maxPoints} pts</text>
2033
+ <svg class="gauge" viewBox="0 0 ${size} ${size}" width="${size}" height="${size}">
2034
+ ${trackCircle}
2035
+ ${arcs.join("\n ")}
2036
+ <text x="${cx}" y="${cy - 8}" text-anchor="middle" font-size="40" font-weight="700" fill="${textColor}">${overallScore}</text>
2037
+ <text x="${cx}" y="${cy + 14}" text-anchor="middle" font-size="14" font-weight="600" fill="${textColor}">${escapeHtml(grade)}</text>
2038
+ <text x="${cx}" y="${cy + 30}" text-anchor="middle" font-size="11" fill="#999">${totalPoints}/${maxPoints} pts</text>
2013
2039
  </svg>
2040
+ <div class="seg-popovers">${popovers}</div>
2014
2041
  <div class="score-scale">
2015
2042
  <span class="scale-fail">0-49</span>
2016
2043
  <span class="scale-average">50-89</span>
@@ -2198,15 +2225,57 @@ body {
2198
2225
  display: flex;
2199
2226
  align-items: center;
2200
2227
  flex-direction: column;
2201
- padding: 24px 0 20px;
2228
+ padding: 32px 0 24px;
2202
2229
  border-bottom: 1px solid var(--border);
2203
2230
  }
2204
2231
  .overall-gauge-wrap {
2205
2232
  display: flex;
2206
2233
  flex-direction: column;
2207
2234
  align-items: center;
2235
+ position: relative;
2236
+ }
2237
+ .overall-gauge-wrap .gauge {
2238
+ display: block;
2239
+ overflow: visible;
2240
+ }
2241
+ .seg-arc {
2242
+ cursor: pointer;
2243
+ transition: stroke-width 0.15s ease, filter 0.15s ease;
2244
+ }
2245
+ .seg-arc:hover {
2246
+ stroke-width: 20;
2247
+ filter: brightness(1.1);
2248
+ }
2249
+
2250
+ /* Segment popovers */
2251
+ .seg-popovers {
2252
+ position: relative;
2253
+ min-height: 36px;
2254
+ display: flex;
2255
+ justify-content: center;
2256
+ margin-top: 4px;
2257
+ }
2258
+ .seg-popover {
2259
+ display: none;
2260
+ align-items: center;
2261
+ gap: 8px;
2262
+ background: #fff;
2263
+ border: 1px solid var(--border);
2264
+ border-radius: 8px;
2265
+ padding: 8px 14px;
2266
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
2267
+ font-size: 13px;
2268
+ white-space: nowrap;
2269
+ }
2270
+ .seg-popover-dot {
2271
+ width: 10px;
2272
+ height: 10px;
2273
+ border-radius: 50%;
2274
+ flex-shrink: 0;
2208
2275
  }
2209
- .overall-gauge-wrap .gauge { display: block; }
2276
+ .seg-popover-name { font-weight: 600; }
2277
+ .seg-popover-score { font-weight: 700; }
2278
+ .seg-popover-pts { color: var(--text-secondary); }
2210
2279
 
2211
2280
  /* Score scale legend */
2212
2281
  .score-scale {
package/dist/index.js CHANGED
@@ -1440,7 +1440,7 @@ var import_zod = require("zod");
1440
1440
 
1441
1441
  // src/modules/analyzer/constants.ts
1442
1442
  var DOMAIN_SIGNAL_TIMEOUT_CAP = 5e3;
1443
- var VERSION = true ? "1.0.0" : "0.0.0";
1443
+ var VERSION = true ? "1.1.0" : "0.0.0";
1444
1444
 
1445
1445
  // src/modules/fetcher/schema.ts
1446
1446
  var FetchOptionsSchema = import_zod.z.object({
@@ -1999,43 +1999,70 @@ function buildGaugeSvg(score, size = "small") {
1999
1999
  </svg>`;
2000
2000
  }
2001
2001
  function buildMultiSegmentGauge(overallScore, grade, totalPoints, maxPoints, categories) {
2002
- const radius = 56;
2003
- const circumference = 2 * Math.PI * radius;
2004
- const strokeWidth = 10;
2005
- const gapDeg = 1;
2006
- const totalGapDeg = gapDeg * categories.length;
2007
- const availableDeg = 360 - totalGapDeg;
2002
+ const radius = 80;
2003
+ const strokeWidth = 14;
2004
+ const pad2 = 8;
2005
+ const size = (radius + strokeWidth / 2 + pad2) * 2;
2006
+ const cx = size / 2;
2007
+ const cy = size / 2;
2008
+ const circ = 2 * Math.PI * radius;
2008
2009
  const textColor = scoreTextColorHex(overallScore);
2009
- const segments = [];
2010
- let currentAngle = -90;
2010
+ const trackCircle = `<circle cx="${cx}" cy="${cy}" r="${radius}" fill="none" stroke="#e8e8e8" stroke-width="${strokeWidth}"/>`;
2011
+ const arcs = [];
2012
+ let consumed = 0;
2013
+ let segIdx = 0;
2011
2014
  for (const cat of categories) {
2012
- const segmentDeg = maxPoints > 0 ? cat.maxScore / maxPoints * availableDeg : 0;
2013
- const segmentCirc = segmentDeg / 360 * circumference;
2014
- const fillRatio = cat.maxScore > 0 ? cat.score / cat.maxScore : 0;
2015
- const filledCirc = segmentCirc * fillRatio;
2016
- const catPct = cat.maxScore > 0 ? Math.round(fillRatio * 100) : 0;
2015
+ const catDeg = maxPoints > 0 ? cat.score / maxPoints * 360 : 0;
2016
+ if (catDeg < 0.1) {
2017
+ consumed += catDeg;
2018
+ continue;
2019
+ }
2020
+ const catPct = cat.maxScore > 0 ? Math.round(cat.score / cat.maxScore * 100) : 0;
2017
2021
  const color = scoreColorHex(catPct);
2018
- segments.push(
2019
- `<circle cx="60" cy="60" r="${radius}" fill="none" stroke="#e8e8e8" stroke-width="${strokeWidth}"
2020
- stroke-dasharray="${segmentCirc} ${circumference - segmentCirc}"
2021
- transform="rotate(${currentAngle} 60 60)"/>`
2022
+ const arcLen = catDeg / 360 * circ;
2023
+ const offset = circ * 0.25 - consumed / 360 * circ;
2024
+ const catName = escapeHtml(cat.name);
2025
+ const idx = segIdx;
2026
+ arcs.push(
2027
+ `<circle cx="${cx}" cy="${cy}" r="${radius}" fill="none"
2028
+ stroke="${color}" stroke-width="${strokeWidth}"
2029
+ stroke-dasharray="${arcLen.toFixed(2)} ${(circ - arcLen).toFixed(2)}"
2030
+ stroke-dashoffset="${offset.toFixed(2)}"
2031
+ class="seg-arc" data-idx="${idx}"
2032
+ onmouseenter="document.getElementById('seg-pop-${idx}').style.display='flex'"
2033
+ onmouseleave="document.getElementById('seg-pop-${idx}').style.display='none'"/>`
2022
2034
  );
2023
- if (filledCirc > 0) {
2024
- segments.push(
2025
- `<circle cx="60" cy="60" r="${radius}" fill="none" stroke="${color}" stroke-width="${strokeWidth}"
2026
- stroke-dasharray="${filledCirc} ${circumference - filledCirc}"
2027
- transform="rotate(${currentAngle} 60 60)"/>`
2028
- );
2029
- }
2030
- currentAngle += segmentDeg + gapDeg;
2035
+ consumed += catDeg;
2036
+ const divRad = consumed / 360 * 2 * Math.PI - Math.PI / 2;
2037
+ const half = strokeWidth / 2 + 1;
2038
+ const dx = Math.cos(divRad);
2039
+ const dy = Math.sin(divRad);
2040
+ arcs.push(
2041
+ `<line x1="${(cx + (radius - half) * dx).toFixed(2)}" y1="${(cy + (radius - half) * dy).toFixed(2)}"
2042
+ x2="${(cx + (radius + half) * dx).toFixed(2)}" y2="${(cy + (radius + half) * dy).toFixed(2)}"
2043
+ stroke="#fff" stroke-width="2" pointer-events="none"/>`
2044
+ );
2045
+ segIdx++;
2031
2046
  }
2047
+ const popovers = categories.filter((cat) => cat.score > 0).map((cat, i) => {
2048
+ const catPct = cat.maxScore > 0 ? Math.round(cat.score / cat.maxScore * 100) : 0;
2049
+ const color = scoreColorHex(catPct);
2050
+ return `<div id="seg-pop-${i}" class="seg-popover">
2051
+ <span class="seg-popover-dot" style="background:${color}"></span>
2052
+ <span class="seg-popover-name">${escapeHtml(cat.name)}</span>
2053
+ <span class="seg-popover-score">${catPct}%</span>
2054
+ <span class="seg-popover-pts">${cat.score}/${cat.maxScore} pts</span>
2055
+ </div>`;
2056
+ }).join("");
2032
2057
  return `<div class="overall-gauge-wrap">
2033
- <svg class="gauge" viewBox="0 0 120 120" width="160" height="160">
2034
- ${segments.join("\n ")}
2035
- <text x="60" y="54" text-anchor="middle" font-size="36" font-weight="700" fill="${textColor}">${overallScore}</text>
2036
- <text x="60" y="70" text-anchor="middle" font-size="12" font-weight="600" fill="${textColor}">${escapeHtml(grade)}</text>
2037
- <text x="60" y="84" text-anchor="middle" font-size="9" fill="#999">${totalPoints}/${maxPoints} pts</text>
2058
+ <svg class="gauge" viewBox="0 0 ${size} ${size}" width="${size}" height="${size}">
2059
+ ${trackCircle}
2060
+ ${arcs.join("\n ")}
2061
+ <text x="${cx}" y="${cy - 8}" text-anchor="middle" font-size="40" font-weight="700" fill="${textColor}">${overallScore}</text>
2062
+ <text x="${cx}" y="${cy + 14}" text-anchor="middle" font-size="14" font-weight="600" fill="${textColor}">${escapeHtml(grade)}</text>
2063
+ <text x="${cx}" y="${cy + 30}" text-anchor="middle" font-size="11" fill="#999">${totalPoints}/${maxPoints} pts</text>
2038
2064
  </svg>
2065
+ <div class="seg-popovers">${popovers}</div>
2039
2066
  <div class="score-scale">
2040
2067
  <span class="scale-fail">0-49</span>
2041
2068
  <span class="scale-average">50-89</span>
@@ -2223,15 +2250,57 @@ body {
2223
2250
  display: flex;
2224
2251
  align-items: center;
2225
2252
  flex-direction: column;
2226
- padding: 24px 0 20px;
2253
+ padding: 32px 0 24px;
2227
2254
  border-bottom: 1px solid var(--border);
2228
2255
  }
2229
2256
  .overall-gauge-wrap {
2230
2257
  display: flex;
2231
2258
  flex-direction: column;
2232
2259
  align-items: center;
2260
+ position: relative;
2261
+ }
2262
+ .overall-gauge-wrap .gauge {
2263
+ display: block;
2264
+ overflow: visible;
2265
+ }
2266
+ .seg-arc {
2267
+ cursor: pointer;
2268
+ transition: stroke-width 0.15s ease, filter 0.15s ease;
2269
+ }
2270
+ .seg-arc:hover {
2271
+ stroke-width: 20;
2272
+ filter: brightness(1.1);
2273
+ }
2274
+
2275
+ /* Segment popovers */
2276
+ .seg-popovers {
2277
+ position: relative;
2278
+ min-height: 36px;
2279
+ display: flex;
2280
+ justify-content: center;
2281
+ margin-top: 4px;
2282
+ }
2283
+ .seg-popover {
2284
+ display: none;
2285
+ align-items: center;
2286
+ gap: 8px;
2287
+ background: #fff;
2288
+ border: 1px solid var(--border);
2289
+ border-radius: 8px;
2290
+ padding: 8px 14px;
2291
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
2292
+ font-size: 13px;
2293
+ white-space: nowrap;
2294
+ }
2295
+ .seg-popover-dot {
2296
+ width: 10px;
2297
+ height: 10px;
2298
+ border-radius: 50%;
2299
+ flex-shrink: 0;
2233
2300
  }
2234
- .overall-gauge-wrap .gauge { display: block; }
2301
+ .seg-popover-name { font-weight: 600; }
2302
+ .seg-popover-score { font-weight: 700; }
2303
+ .seg-popover-pts { color: var(--text-secondary); }
2235
2304
 
2236
2305
  /* Score scale legend */
2237
2306
  .score-scale {
package/dist/index.mjs CHANGED
@@ -1402,7 +1402,7 @@ import { z } from "zod";
1402
1402
 
1403
1403
  // src/modules/analyzer/constants.ts
1404
1404
  var DOMAIN_SIGNAL_TIMEOUT_CAP = 5e3;
1405
- var VERSION = true ? "1.0.0" : "0.0.0";
1405
+ var VERSION = true ? "1.1.0" : "0.0.0";
1406
1406
 
1407
1407
  // src/modules/fetcher/schema.ts
1408
1408
  var FetchOptionsSchema = z.object({
@@ -1961,43 +1961,70 @@ function buildGaugeSvg(score, size = "small") {
1961
1961
  </svg>`;
1962
1962
  }
1963
1963
  function buildMultiSegmentGauge(overallScore, grade, totalPoints, maxPoints, categories) {
1964
- const radius = 56;
1965
- const circumference = 2 * Math.PI * radius;
1966
- const strokeWidth = 10;
1967
- const gapDeg = 1;
1968
- const totalGapDeg = gapDeg * categories.length;
1969
- const availableDeg = 360 - totalGapDeg;
1964
+ const radius = 80;
1965
+ const strokeWidth = 14;
1966
+ const pad2 = 8;
1967
+ const size = (radius + strokeWidth / 2 + pad2) * 2;
1968
+ const cx = size / 2;
1969
+ const cy = size / 2;
1970
+ const circ = 2 * Math.PI * radius;
1970
1971
  const textColor = scoreTextColorHex(overallScore);
1971
- const segments = [];
1972
- let currentAngle = -90;
1972
+ const trackCircle = `<circle cx="${cx}" cy="${cy}" r="${radius}" fill="none" stroke="#e8e8e8" stroke-width="${strokeWidth}"/>`;
1973
+ const arcs = [];
1974
+ let consumed = 0;
1975
+ let segIdx = 0;
1973
1976
  for (const cat of categories) {
1974
- const segmentDeg = maxPoints > 0 ? cat.maxScore / maxPoints * availableDeg : 0;
1975
- const segmentCirc = segmentDeg / 360 * circumference;
1976
- const fillRatio = cat.maxScore > 0 ? cat.score / cat.maxScore : 0;
1977
- const filledCirc = segmentCirc * fillRatio;
1978
- const catPct = cat.maxScore > 0 ? Math.round(fillRatio * 100) : 0;
1977
+ const catDeg = maxPoints > 0 ? cat.score / maxPoints * 360 : 0;
1978
+ if (catDeg < 0.1) {
1979
+ consumed += catDeg;
1980
+ continue;
1981
+ }
1982
+ const catPct = cat.maxScore > 0 ? Math.round(cat.score / cat.maxScore * 100) : 0;
1979
1983
  const color = scoreColorHex(catPct);
1980
- segments.push(
1981
- `<circle cx="60" cy="60" r="${radius}" fill="none" stroke="#e8e8e8" stroke-width="${strokeWidth}"
1982
- stroke-dasharray="${segmentCirc} ${circumference - segmentCirc}"
1983
- transform="rotate(${currentAngle} 60 60)"/>`
1984
+ const arcLen = catDeg / 360 * circ;
1985
+ const offset = circ * 0.25 - consumed / 360 * circ;
1986
+ const catName = escapeHtml(cat.name);
1987
+ const idx = segIdx;
1988
+ arcs.push(
1989
+ `<circle cx="${cx}" cy="${cy}" r="${radius}" fill="none"
1990
+ stroke="${color}" stroke-width="${strokeWidth}"
1991
+ stroke-dasharray="${arcLen.toFixed(2)} ${(circ - arcLen).toFixed(2)}"
1992
+ stroke-dashoffset="${offset.toFixed(2)}"
1993
+ class="seg-arc" data-idx="${idx}"
1994
+ onmouseenter="document.getElementById('seg-pop-${idx}').style.display='flex'"
1995
+ onmouseleave="document.getElementById('seg-pop-${idx}').style.display='none'"/>`
1984
1996
  );
1985
- if (filledCirc > 0) {
1986
- segments.push(
1987
- `<circle cx="60" cy="60" r="${radius}" fill="none" stroke="${color}" stroke-width="${strokeWidth}"
1988
- stroke-dasharray="${filledCirc} ${circumference - filledCirc}"
1989
- transform="rotate(${currentAngle} 60 60)"/>`
1990
- );
1991
- }
1992
- currentAngle += segmentDeg + gapDeg;
1997
+ consumed += catDeg;
1998
+ const divRad = consumed / 360 * 2 * Math.PI - Math.PI / 2;
1999
+ const half = strokeWidth / 2 + 1;
2000
+ const dx = Math.cos(divRad);
2001
+ const dy = Math.sin(divRad);
2002
+ arcs.push(
2003
+ `<line x1="${(cx + (radius - half) * dx).toFixed(2)}" y1="${(cy + (radius - half) * dy).toFixed(2)}"
2004
+ x2="${(cx + (radius + half) * dx).toFixed(2)}" y2="${(cy + (radius + half) * dy).toFixed(2)}"
2005
+ stroke="#fff" stroke-width="2" pointer-events="none"/>`
2006
+ );
2007
+ segIdx++;
1993
2008
  }
2009
+ const popovers = categories.filter((cat) => cat.score > 0).map((cat, i) => {
2010
+ const catPct = cat.maxScore > 0 ? Math.round(cat.score / cat.maxScore * 100) : 0;
2011
+ const color = scoreColorHex(catPct);
2012
+ return `<div id="seg-pop-${i}" class="seg-popover">
2013
+ <span class="seg-popover-dot" style="background:${color}"></span>
2014
+ <span class="seg-popover-name">${escapeHtml(cat.name)}</span>
2015
+ <span class="seg-popover-score">${catPct}%</span>
2016
+ <span class="seg-popover-pts">${cat.score}/${cat.maxScore} pts</span>
2017
+ </div>`;
2018
+ }).join("");
1994
2019
  return `<div class="overall-gauge-wrap">
1995
- <svg class="gauge" viewBox="0 0 120 120" width="160" height="160">
1996
- ${segments.join("\n ")}
1997
- <text x="60" y="54" text-anchor="middle" font-size="36" font-weight="700" fill="${textColor}">${overallScore}</text>
1998
- <text x="60" y="70" text-anchor="middle" font-size="12" font-weight="600" fill="${textColor}">${escapeHtml(grade)}</text>
1999
- <text x="60" y="84" text-anchor="middle" font-size="9" fill="#999">${totalPoints}/${maxPoints} pts</text>
2020
+ <svg class="gauge" viewBox="0 0 ${size} ${size}" width="${size}" height="${size}">
2021
+ ${trackCircle}
2022
+ ${arcs.join("\n ")}
2023
+ <text x="${cx}" y="${cy - 8}" text-anchor="middle" font-size="40" font-weight="700" fill="${textColor}">${overallScore}</text>
2024
+ <text x="${cx}" y="${cy + 14}" text-anchor="middle" font-size="14" font-weight="600" fill="${textColor}">${escapeHtml(grade)}</text>
2025
+ <text x="${cx}" y="${cy + 30}" text-anchor="middle" font-size="11" fill="#999">${totalPoints}/${maxPoints} pts</text>
2000
2026
  </svg>
2027
+ <div class="seg-popovers">${popovers}</div>
2001
2028
  <div class="score-scale">
2002
2029
  <span class="scale-fail">0-49</span>
2003
2030
  <span class="scale-average">50-89</span>
@@ -2185,15 +2212,57 @@ body {
2185
2212
  display: flex;
2186
2213
  align-items: center;
2187
2214
  flex-direction: column;
2188
- padding: 24px 0 20px;
2215
+ padding: 32px 0 24px;
2189
2216
  border-bottom: 1px solid var(--border);
2190
2217
  }
2191
2218
  .overall-gauge-wrap {
2192
2219
  display: flex;
2193
2220
  flex-direction: column;
2194
2221
  align-items: center;
2222
+ position: relative;
2223
+ }
2224
+ .overall-gauge-wrap .gauge {
2225
+ display: block;
2226
+ overflow: visible;
2227
+ }
2228
+ .seg-arc {
2229
+ cursor: pointer;
2230
+ transition: stroke-width 0.15s ease, filter 0.15s ease;
2231
+ }
2232
+ .seg-arc:hover {
2233
+ stroke-width: 20;
2234
+ filter: brightness(1.1);
2235
+ }
2236
+
2237
+ /* Segment popovers */
2238
+ .seg-popovers {
2239
+ position: relative;
2240
+ min-height: 36px;
2241
+ display: flex;
2242
+ justify-content: center;
2243
+ margin-top: 4px;
2244
+ }
2245
+ .seg-popover {
2246
+ display: none;
2247
+ align-items: center;
2248
+ gap: 8px;
2249
+ background: #fff;
2250
+ border: 1px solid var(--border);
2251
+ border-radius: 8px;
2252
+ padding: 8px 14px;
2253
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
2254
+ font-size: 13px;
2255
+ white-space: nowrap;
2256
+ }
2257
+ .seg-popover-dot {
2258
+ width: 10px;
2259
+ height: 10px;
2260
+ border-radius: 50%;
2261
+ flex-shrink: 0;
2195
2262
  }
2196
- .overall-gauge-wrap .gauge { display: block; }
2263
+ .seg-popover-name { font-weight: 600; }
2264
+ .seg-popover-score { font-weight: 700; }
2265
+ .seg-popover-pts { color: var(--text-secondary); }
2197
2266
 
2198
2267
  /* Score scale legend */
2199
2268
  .score-scale {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiseo-audit",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Lighthouse for AI SEO. Audit any webpage for AI search readiness. 7 categories, 30+ factors, research-backed scoring. Deterministic, engine-agnostic, zero API keys.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",