Skip to content

Commit 640ee2c

Browse files
committed
fix
1 parent 3ef7381 commit 640ee2c

File tree

3 files changed

+92
-24
lines changed

3 files changed

+92
-24
lines changed

packages/react-doctor/src/cli.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const program = new Command()
5454
scoreOnly: isScoreOnly,
5555
};
5656

57+
<<<<<<< Updated upstream
5758
const shouldSkipPrompts =
5859
flags.yes ||
5960
Boolean(process.env.CI) ||
@@ -65,6 +66,19 @@ const program = new Command()
6566
Boolean(process.env.AMP_HOME) ||
6667
Boolean(process.env.AMI) ||
6768
!process.stdin.isTTY;
69+
=======
70+
const isAutomatedEnvironment = [
71+
process.env.CI,
72+
process.env.CLAUDECODE,
73+
process.env.CURSOR_TRACE_ID,
74+
process.env.CURSOR_AGENT,
75+
process.env.CODEX_CI,
76+
process.env.OPENCODE,
77+
process.env.AMP_HOME,
78+
process.env.AMI,
79+
].some(Boolean);
80+
const shouldSkipPrompts = flags.yes || isAutomatedEnvironment || !process.stdin.isTTY;
81+
>>>>>>> Stashed changes
6882
const projectDirectories = await selectProjects(
6983
resolvedDirectory,
7084
flags.project,
@@ -185,7 +199,7 @@ const fixCommand = new Command("fix")
185199
.action(fixAction);
186200

187201
const installAmiCommand = new Command("install-ami")
188-
.description("Open Ami to auto-fix react-doctor issues")
202+
.description("Install Ami and open it to auto-fix issues")
189203
.argument("[directory]", "project directory", ".")
190204
.action(fixAction);
191205

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"use client";
2+
3+
import { useEffect, useState } from "react";
4+
5+
const PERFECT_SCORE = 100;
6+
const SCORE_GOOD_THRESHOLD = 75;
7+
const SCORE_OK_THRESHOLD = 50;
8+
const SCORE_BAR_WIDTH = 30;
9+
const SCORE_FRAME_COUNT = 20;
10+
const SCORE_FRAME_DELAY_MS = 30;
11+
12+
const easeOutCubic = (progress: number) => 1 - Math.pow(1 - progress, 3);
13+
14+
const getScoreColorClass = (score: number): string => {
15+
if (score >= SCORE_GOOD_THRESHOLD) return "text-green-400";
16+
if (score >= SCORE_OK_THRESHOLD) return "text-yellow-500";
17+
return "text-red-400";
18+
};
19+
20+
const getScoreLabel = (score: number): string => {
21+
if (score >= SCORE_GOOD_THRESHOLD) return "Great";
22+
if (score >= SCORE_OK_THRESHOLD) return "Needs work";
23+
return "Critical";
24+
};
25+
26+
const ScoreBar = ({ score }: { score: number }) => {
27+
const filledCount = Math.round((score / PERFECT_SCORE) * SCORE_BAR_WIDTH);
28+
const emptyCount = SCORE_BAR_WIDTH - filledCount;
29+
const colorClass = getScoreColorClass(score);
30+
31+
return (
32+
<>
33+
<span className={colorClass}>{"\u2588".repeat(filledCount)}</span>
34+
<span className="text-neutral-600">{"\u2591".repeat(emptyCount)}</span>
35+
</>
36+
);
37+
};
38+
39+
const AnimatedScore = ({ targetScore }: { targetScore: number }) => {
40+
const [animatedScore, setAnimatedScore] = useState(0);
41+
42+
useEffect(() => {
43+
let cancelled = false;
44+
let frame = 0;
45+
46+
const animate = () => {
47+
if (cancelled || frame > SCORE_FRAME_COUNT) return;
48+
setAnimatedScore(Math.round(easeOutCubic(frame / SCORE_FRAME_COUNT) * targetScore));
49+
frame++;
50+
setTimeout(animate, SCORE_FRAME_DELAY_MS);
51+
};
52+
53+
animate();
54+
return () => {
55+
cancelled = true;
56+
};
57+
}, [targetScore]);
58+
59+
const colorClass = getScoreColorClass(animatedScore);
60+
61+
return (
62+
<>
63+
<div className="mb-2 pl-2">
64+
<span className={colorClass}>{animatedScore}</span>
65+
{` / ${PERFECT_SCORE} `}
66+
<span className={colorClass}>{getScoreLabel(animatedScore)}</span>
67+
</div>
68+
<div className="mb-6 pl-2">
69+
<ScoreBar score={animatedScore} />
70+
</div>
71+
</>
72+
);
73+
};
74+
75+
export default AnimatedScore;

packages/website/src/app/share/page.tsx

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import type { Metadata } from "next";
2+
import AnimatedScore from "./animated-score";
23

34
const PERFECT_SCORE = 100;
45
const SCORE_GOOD_THRESHOLD = 75;
56
const SCORE_OK_THRESHOLD = 50;
6-
const SCORE_BAR_WIDTH = 30;
77
const COMMAND = "npx -y react-doctor@latest .";
88
const SHARE_BASE_URL = "https://www.react.doctor/share";
99
const X_ICON_PATH =
@@ -49,19 +49,6 @@ const DoctorFace = ({ score }: { score: number }) => {
4949
);
5050
};
5151

52-
const ScoreBar = ({ score }: { score: number }) => {
53-
const filledCount = Math.round((score / PERFECT_SCORE) * SCORE_BAR_WIDTH);
54-
const emptyCount = SCORE_BAR_WIDTH - filledCount;
55-
const colorClass = getScoreColorClass(score);
56-
57-
return (
58-
<span>
59-
<span className={colorClass}>{"\u2588".repeat(filledCount)}</span>
60-
<span className="text-neutral-600">{"\u2591".repeat(emptyCount)}</span>
61-
</span>
62-
);
63-
};
64-
6552
export const generateMetadata = async ({
6653
searchParams,
6754
}: {
@@ -104,7 +91,6 @@ const SharePage = async ({ searchParams }: { searchParams: Promise<ShareSearchPa
10491
const errorCount = Math.max(0, Number(resolvedParams.e) || 0);
10592
const warningCount = Math.max(0, Number(resolvedParams.w) || 0);
10693
const fileCount = Math.max(0, Number(resolvedParams.f) || 0);
107-
const colorClass = getScoreColorClass(score);
10894
const label = getScoreLabel(score);
10995

11096
const shareSearchParams = new URLSearchParams();
@@ -127,14 +113,7 @@ const SharePage = async ({ searchParams }: { searchParams: Promise<ShareSearchPa
127113
</div>
128114
</div>
129115

130-
<div className="mb-2 pl-2">
131-
<span className={colorClass}>{score}</span>
132-
{` / ${PERFECT_SCORE} `}
133-
<span className={colorClass}>{getScoreLabel(score)}</span>
134-
</div>
135-
<div className="mb-6 pl-2">
136-
<ScoreBar score={score} />
137-
</div>
116+
<AnimatedScore targetScore={score} />
138117

139118
{(errorCount > 0 || warningCount > 0 || fileCount > 0) && (
140119
<div className="mb-8 pl-2">

0 commit comments

Comments
 (0)