├── public ├── robots.txt ├── logo.png ├── logo.psd ├── favicon.ico ├── screenshot.png ├── star-icon.png ├── StarTrackDemo.gif ├── StarTrackDemo-old.gif ├── manifest.json └── index.html ├── src ├── components │ ├── Chart │ │ ├── index.ts │ │ └── Chart.tsx │ ├── Footer │ │ ├── index.ts │ │ ├── Footer.test.tsx │ │ └── Footer.tsx │ ├── TopNav │ │ ├── index.ts │ │ ├── TopNav.test.tsx │ │ └── TopNav.tsx │ ├── URLBox │ │ ├── index.ts │ │ ├── URLBox.test.tsx │ │ └── URLBox.tsx │ ├── GitHubAuth │ │ ├── index.ts │ │ ├── GitHubAuthBtn.tsx │ │ ├── GitHubAuthBtn.test.tsx │ │ ├── GitHubAuth.tsx │ │ ├── GitHubAuth.test.tsx │ │ ├── LoggedIn.tsx │ │ ├── LoggedIn.test.tsx │ │ ├── GitHubAuthForm.test.tsx │ │ └── GitHubAuthForm.tsx │ ├── RepoStats │ │ ├── index.ts │ │ ├── StatsGridProps.ts │ │ ├── RepoChip.tsx │ │ ├── RepoChip.test.tsx │ │ ├── StatsGridSmallScreen.tsx │ │ ├── DownloadData.tsx │ │ ├── StatsGridSmallScreen.test.tsx │ │ ├── StatsGridLargeScreen.tsx │ │ ├── RepoStats.tsx │ │ ├── StatsGridLargeScreen.test.tsx │ │ ├── DownloadData.test.tsx │ │ └── RepoStats.test.tsx │ ├── MainContainer │ │ └── index.ts │ ├── RepoChips │ │ ├── index.ts │ │ ├── RepoChipContainer.tsx │ │ ├── RepoChip.tsx │ │ ├── RepoChipContainer.test.tsx │ │ └── RepoChip.test.tsx │ ├── RepoDetailsInput │ │ ├── index.ts │ │ ├── RepoDetailsInputProps.ts │ │ ├── RepoDetailsInput.tsx │ │ ├── RepoDetailsInput.test.tsx │ │ ├── LoadingButton.test.tsx │ │ ├── LoadingButton.tsx │ │ ├── RepoDetailsInputMobile.tsx │ │ ├── RepoDetailsInputDesktop.tsx │ │ ├── RepoDetailsInputMobile.test.tsx │ │ └── RepoDetailsInputDesktop.test.tsx │ └── Forecast │ │ ├── index.ts │ │ ├── ForecastRow.tsx │ │ ├── ForecastInfo.ts │ │ ├── Forecast.tsx │ │ ├── ForecastInfo.test.ts │ │ ├── ForecastRow.test.tsx │ │ └── Forecast.test.tsx ├── routes │ ├── MainPage │ │ ├── index.ts │ │ ├── MainPage.tsx │ │ └── MainPage.test.tsx │ ├── Preload │ │ ├── index.ts │ │ ├── PreloadTypes.ts │ │ ├── RepoLoader.tsx │ │ ├── Preload.test.tsx │ │ ├── Preload.tsx │ │ └── RepoLoader.test.tsx │ └── ErrorPage │ │ ├── index.ts │ │ ├── ErrorPage.test.tsx │ │ ├── Illustration.tsx │ │ └── ErrorPage.tsx ├── react-app-env.d.ts ├── utils │ ├── StarData.ts │ ├── RepoInfo.ts │ ├── Constants.ts │ ├── StringUtils.ts │ ├── StringUtils.test.ts │ ├── FileUtils.ts │ ├── test.tsx │ ├── RepoInfoExporter.ts │ ├── StargazerLoader.ts │ ├── FilteUtils.test.ts │ ├── StargazerLoader.test.ts │ ├── RepoInfoExporter.test.ts │ ├── StargazerStats.test.ts │ └── StargazerStats.ts ├── setupTests.ts ├── reportWebVitals.ts ├── index.tsx ├── shared │ ├── Tooltip.tsx │ ├── Theme.ts │ ├── ThemeProvider.tsx │ ├── ThemeProvider.test.tsx │ ├── AlertContext.tsx │ ├── AlertContext.test.tsx │ ├── ProgressContext.tsx │ └── ProgressContext.test.tsx ├── App.test.tsx └── App.tsx ├── .prettierignore ├── .prettierrc ├── .cspell.json ├── .github ├── dependabot.yml └── workflows │ ├── buildAndTest.yml │ ├── playwright.yml │ └── deploy.yml ├── dictionary.txt ├── SECURITY.md ├── .gitignore ├── tsconfig.json ├── .eslintrc.json ├── LICENSE ├── tests ├── utils.ts ├── auth.spec.ts ├── download.spec.ts ├── forecast.spec.ts └── basic.spec.ts ├── playwright.config.ts └── package.json /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seladb/StarTrack-js/HEAD/public/logo.png -------------------------------------------------------------------------------- /public/logo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seladb/StarTrack-js/HEAD/public/logo.psd -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seladb/StarTrack-js/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/components/Chart/index.ts: -------------------------------------------------------------------------------- 1 | import Chart from "./Chart"; 2 | 3 | export default Chart; 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore old source 2 | src_old 3 | old 4 | 5 | # Ignore artifacts 6 | build 7 | -------------------------------------------------------------------------------- /public/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seladb/StarTrack-js/HEAD/public/screenshot.png -------------------------------------------------------------------------------- /public/star-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seladb/StarTrack-js/HEAD/public/star-icon.png -------------------------------------------------------------------------------- /src/components/Footer/index.ts: -------------------------------------------------------------------------------- 1 | import Footer from "./Footer"; 2 | 3 | export default Footer; 4 | -------------------------------------------------------------------------------- /src/components/TopNav/index.ts: -------------------------------------------------------------------------------- 1 | import TopNav from "./TopNav"; 2 | 3 | export default TopNav; 4 | -------------------------------------------------------------------------------- /src/components/URLBox/index.ts: -------------------------------------------------------------------------------- 1 | import URLBox from "./URLBox"; 2 | 3 | export default URLBox; 4 | -------------------------------------------------------------------------------- /public/StarTrackDemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seladb/StarTrack-js/HEAD/public/StarTrackDemo.gif -------------------------------------------------------------------------------- /src/routes/MainPage/index.ts: -------------------------------------------------------------------------------- 1 | import MainPage from "./MainPage"; 2 | 3 | export default MainPage; 4 | -------------------------------------------------------------------------------- /src/routes/Preload/index.ts: -------------------------------------------------------------------------------- 1 | import { Preload } from "./Preload"; 2 | 3 | export default Preload; 4 | -------------------------------------------------------------------------------- /src/routes/ErrorPage/index.ts: -------------------------------------------------------------------------------- 1 | import ErrorPage from "./ErrorPage"; 2 | 3 | export default ErrorPage; 4 | -------------------------------------------------------------------------------- /public/StarTrackDemo-old.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seladb/StarTrack-js/HEAD/public/StarTrackDemo-old.gif -------------------------------------------------------------------------------- /src/components/GitHubAuth/index.ts: -------------------------------------------------------------------------------- 1 | import GitHubAuth from "./GitHubAuth"; 2 | 3 | export { GitHubAuth }; 4 | -------------------------------------------------------------------------------- /src/components/RepoStats/index.ts: -------------------------------------------------------------------------------- 1 | import RepoStats from "./RepoStats"; 2 | 3 | export default RepoStats; 4 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line spaced-comment 2 | /// 3 | -------------------------------------------------------------------------------- /src/routes/Preload/PreloadTypes.ts: -------------------------------------------------------------------------------- 1 | export type RepoMetadata = { 2 | username: string; 3 | repo: string; 4 | }; 5 | -------------------------------------------------------------------------------- /src/components/MainContainer/index.ts: -------------------------------------------------------------------------------- 1 | import MainContainer from "./MainContainer"; 2 | 3 | export default MainContainer; 4 | -------------------------------------------------------------------------------- /src/components/RepoChips/index.ts: -------------------------------------------------------------------------------- 1 | import RepoChipContainer from "./RepoChipContainer"; 2 | 3 | export default RepoChipContainer; 4 | -------------------------------------------------------------------------------- /src/utils/StarData.ts: -------------------------------------------------------------------------------- 1 | export default interface StarData { 2 | timestamps: Array; 3 | starCounts: Array; 4 | } 5 | -------------------------------------------------------------------------------- /src/components/RepoDetailsInput/index.ts: -------------------------------------------------------------------------------- 1 | import RepoDetailsInput from "./RepoDetailsInput"; 2 | 3 | export default RepoDetailsInput; 4 | -------------------------------------------------------------------------------- /src/components/Forecast/index.ts: -------------------------------------------------------------------------------- 1 | import Forecast from "./Forecast"; 2 | import { ForecastInfo } from "./ForecastInfo"; 3 | 4 | export { ForecastInfo, Forecast }; 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "tabWidth": 2, 4 | "printWidth": 100, 5 | "singleQuote": false, 6 | "trailingComma": "all", 7 | "jsxSingleQuote": false, 8 | "bracketSpacing": true 9 | } 10 | -------------------------------------------------------------------------------- /src/components/RepoDetailsInput/RepoDetailsInputProps.ts: -------------------------------------------------------------------------------- 1 | export default interface RepoDetailsInputProps { 2 | loading: boolean; 3 | onGoClick: (username: string, repo: string) => void; 4 | onCancelClick: () => void; 5 | } 6 | -------------------------------------------------------------------------------- /.cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2", 3 | "language": "en", 4 | "dictionaries": ["startrack-js"], 5 | "dictionaryDefinitions": [ 6 | { 7 | "name": "startrack-js", 8 | "path": "./dictionary.txt" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/RepoInfo.ts: -------------------------------------------------------------------------------- 1 | import StarData from "./StarData"; 2 | 3 | export default interface RepoInfo { 4 | username: string; 5 | repo: string; 6 | color: { hsl: string; hex: string }; 7 | stargazerData: StarData; 8 | forecast?: StarData; 9 | } 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "monthly" 12 | -------------------------------------------------------------------------------- /src/utils/Constants.ts: -------------------------------------------------------------------------------- 1 | export const starTrackGitHubMaintainer = "seladb"; 2 | export const starTrackGitHubRepo = `https://github.com/${starTrackGitHubMaintainer}/startrack-js`; 3 | export const twitter = "https://twitter.com/seladb"; 4 | export const email = "pcapplusplus@gmail.com"; 5 | -------------------------------------------------------------------------------- /src/utils/StringUtils.ts: -------------------------------------------------------------------------------- 1 | export const slugify = (str: string) => { 2 | str = str 3 | .replace(/[^A-Za-z0-9 -]/g, "") // remove invalid chars 4 | .replace(/\s+/g, "-") // collapse whitespace and replace by - 5 | .replace(/-+/g, "-") // collapse dashes 6 | .toLowerCase(); 7 | return str; 8 | }; 9 | -------------------------------------------------------------------------------- /dictionary.txt: -------------------------------------------------------------------------------- 1 | seladb 2 | startrack 3 | pcapplusplus 4 | Overridable 5 | testid 6 | plotly 7 | Relayout 8 | xaxis 9 | yaxis 10 | modebar 11 | hovertemplate 12 | hoverlabel 13 | showlegend 14 | autorange 15 | fixedrange 16 | Formik 17 | datagrid 18 | camelcase 19 | slugified 20 | TTFB 21 | octicon 22 | pageview 23 | -------------------------------------------------------------------------------- /src/components/RepoStats/StatsGridProps.ts: -------------------------------------------------------------------------------- 1 | import RepoInfo from "../../utils/RepoInfo"; 2 | 3 | export type StatsData = Record; 4 | 5 | export interface RepoInfoWithStats extends Omit { 6 | stats: StatsData; 7 | } 8 | 9 | export default interface StatsGridProps { 10 | statInfos: Array; 11 | } 12 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | Thank you for helping to improve the security of StarTrack. This project is a small front-end application but we nevertheless appreciate your efforts to report any potential security issues. 4 | 5 | To report a security issue, please use the GitHub Security Advisory "[Report a Vulnerability](https://github.com/seladb/StarTrack-js/security/advisories/new)" tab. 6 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import "@testing-library/jest-dom"; 6 | import { TextEncoder, TextDecoder } from "node:util"; 7 | 8 | if (!global.TextEncoder) { 9 | global.TextEncoder = TextEncoder; 10 | } 11 | 12 | if (!global.TextDecoder) { 13 | global.TextDecoder = TextDecoder; 14 | } 15 | -------------------------------------------------------------------------------- /src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from "web-vitals"; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "StarTrack", 3 | "name": "GitHub Star History and Stats", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "star-icon.png", 12 | "type": "image/png", 13 | "sizes": "256x256" 14 | } 15 | ], 16 | "start_url": ".", 17 | "display": "standalone", 18 | "theme_color": "#000000", 19 | "background_color": "#ffffff" 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | node_modules/ 26 | 27 | # vscode 28 | .vscode 29 | 30 | # Playwright 31 | /test-results/ 32 | /playwright-report/ 33 | /blob-report/ 34 | /playwright/.cache/ 35 | -------------------------------------------------------------------------------- /src/utils/StringUtils.test.ts: -------------------------------------------------------------------------------- 1 | // cSpell:ignore capitalletters, specialchars 2 | 3 | import { slugify } from "./StringUtils"; 4 | 5 | describe(slugify, () => { 6 | it.each([ 7 | ["slugified", "slugified"], 8 | ["with space", "with-space"], 9 | ["with multiple spaces", "with-multiple-spaces"], 10 | ["CapitalLetterS", "capitalletters"], 11 | ["!@special*&()chars$#", "specialchars"], 12 | ["multiple---dashes", "multiple-dashes"], 13 | ])("slugify correctly", (str: string, expected: string) => { 14 | expect(slugify(str)).toBe(expected); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App"; 4 | import reportWebVitals from "./reportWebVitals"; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement); 7 | root.render( 8 | 9 | 10 | , 11 | ); 12 | 13 | // If you want to start measuring performance in your app, pass a function 14 | // to log results (for example: reportWebVitals(console.log)) 15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 16 | reportWebVitals(); 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2015", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"] 20 | } 21 | -------------------------------------------------------------------------------- /src/shared/Tooltip.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from "@mui/material/styles"; 2 | import Tooltip, { TooltipProps, tooltipClasses } from "@mui/material/Tooltip"; 3 | 4 | const StarTrackTooltip = styled(({ className, ...props }: TooltipProps) => ( 5 | 6 | ))(({ theme }) => ({ 7 | [`& .${tooltipClasses.arrow}`]: { 8 | color: theme.palette.secondary.main, 9 | }, 10 | [`& .${tooltipClasses.tooltip}`]: { 11 | backgroundColor: theme.palette.secondary.main, 12 | fontSize: theme.typography.fontSize, 13 | }, 14 | })); 15 | 16 | export default StarTrackTooltip; 17 | -------------------------------------------------------------------------------- /src/components/RepoStats/RepoChip.tsx: -------------------------------------------------------------------------------- 1 | import { Chip } from "@mui/material"; 2 | import * as GitHubUtils from "../../utils/GitHubUtils"; 3 | 4 | interface RepoChipProps { 5 | user: string; 6 | repo: string; 7 | color: string; 8 | } 9 | 10 | export default function RepoChip({ user, repo, color }: RepoChipProps) { 11 | const handleClick = () => { 12 | const url = GitHubUtils.getRepoUrl(user, repo); 13 | window.open(url, "_blank", "noreferrer"); 14 | }; 15 | 16 | return ( 17 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /src/components/RepoDetailsInput/RepoDetailsInput.tsx: -------------------------------------------------------------------------------- 1 | import { useTheme } from "@mui/material/styles"; 2 | import useMediaQuery from "@mui/material/useMediaQuery"; 3 | import RepoDetailsInputDesktop from "./RepoDetailsInputDesktop"; 4 | import RepoDetailsInputMobile from "./RepoDetailsInputMobile"; 5 | import RepoDetailsInputProps from "./RepoDetailsInputProps"; 6 | 7 | export default function RepoDetailsInput(props: RepoDetailsInputProps) { 8 | const theme = useTheme(); 9 | const smallScreen = useMediaQuery(theme.breakpoints.down(550)); 10 | 11 | return smallScreen ? ( 12 | 13 | ) : ( 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "jest": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaFeatures": { 11 | "jsx": true 12 | }, 13 | "ecmaVersion": "latest", 14 | "sourceType": "module" 15 | }, 16 | "plugins": ["@typescript-eslint", "react-hooks", "prettier"], 17 | "rules": { 18 | "react/react-in-jsx-scope": "off", 19 | "camelcase": "error", 20 | "spaced-comment": "error", 21 | "quotes": ["error", "double"], 22 | "no-duplicate-imports": "error" 23 | }, 24 | "settings": { 25 | "import/resolver": { 26 | "typescript": {} 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/routes/MainPage/MainPage.tsx: -------------------------------------------------------------------------------- 1 | import Footer from "../../components/Footer"; 2 | import MainContainer from "../../components/MainContainer"; 3 | import TopNav from "../../components/TopNav"; 4 | import { AlertContextProvider } from "../../shared/AlertContext"; 5 | import { ProgressProvider } from "../../shared/ProgressContext"; 6 | import { Box } from "@mui/material"; 7 | 8 | export default function MainPage() { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |