Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
3b1b18a
feat: add player level graphs derived from XP thresholds
marktoh Oct 19, 2026
163deb6
feat: add collapsible laning phase levels report
marktoh Oct 19, 2026
ab241b0
feat: add net worth and level at 10 minutes to laning table
marktoh Oct 19, 2026
b1d2d9a
feat: expand laning analysis and match graphs
marktoh Oct 20, 2026
bc76632
fix: use lane roles for laning analysis and clarify labels
marktoh Oct 20, 2026
d0fbe13
feat: add metric icons to match graph tooltips
marktoh Oct 21, 2026
0d8de6e
fix(laning): move heatmap below player table
marktoh Oct 21, 2026
3193092
feat(Laning): add LaningEvents, LaningTable, LastHitsTable, RoleCompa…
marktoh Oct 21, 2026
7ac669a
feat: add RoleComparison
marktoh Oct 21, 2026
af7bbea
feat(Laning): reorder sections
marktoh Oct 21, 2026
fde89e4
feat(laning): add replay K/D/A, percentage comparisons, and translations
marktoh Oct 21, 2026
1ec8bd1
style: give laning summary message a blue-tinted background
marktoh Oct 21, 2026
148f15e
feat: add lane replay comparisons and improve laning summaries
marktoh Oct 21, 2026
52e4bce
feat: use brighter radio button color
marktoh Oct 21, 2026
b5719eb
feat: use futuristic font for laning phase summary
marktoh Oct 21, 2026
9df7e44
feat: improve laning summaries, replay comparisons, and graph styling
marktoh Oct 23, 2026
5cdc577
feat(laning): group level six timings by timestamp
marktoh Oct 23, 2026
e504b1d
style(laning): adjust level six timing timestamps
marktoh Oct 24, 2026
85bf2ab
feat(match-header): enhance match summary and feedback
marktoh Oct 24, 2026
a7525f4
style(match): increase tab font size and align time icon sizes
marktoh Oct 24, 2026
e8d9c62
feat(ui): redesign recent matches and improve typography
marktoh Oct 24, 2026
18cd2f2
feat(player): add recent matches view toggle and refine rank display
marktoh Oct 25, 2026
56036ff
feat(match): enhance team headers and add region flags
marktoh Oct 25, 2026
2ee5b3a
feat(match): show header hero images on all tabs
marktoh Oct 25, 2026
6e85f34
style(theme): apply Dota-inspired black and gold palette
marktoh Oct 25, 2026
77271af
feat(header): highlight active navigation menu option
marktoh Oct 25, 2026
a730901
style(match): redesign header with victory banner and centered match …
marktoh Oct 25, 2026
98b1160
style: make homepage whyList icons gold
marktoh Oct 25, 2026
6927b2c
style(laning): refresh summary and breakdown panels
marktoh Oct 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 17 additions & 70 deletions package-lock.json

Large diffs are not rendered by default.

Binary file added public/assets/images/dota2/role-icons.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions src/actions/analyzeMatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,48 @@ import { skillshots } from "dotaconstants";
import { isSupport, getObsWardsPlaced, isRoshHero } from "../utility";
import store from "../store";

/**
* Derives player advice using the active language dictionary from the store.
* @param match Match data used to evaluate advice checks.
* @param _pm Player data evaluated by each check.
* @returns An object keyed by check name: eff, farm_drought, skillshot, late_courier,
* wards, roshan, and rune_control. Each result contains a translated name, measured
* value, validity flag, reference target (top), score function, and optional grade flag.
* All checks are returned, including invalid ones. Score functions are not called here.
* @example
* ```ts
* const match = { duration: 1200 };
* const _pm = {
* hero_id: 1,
* lane_role: 2,
* lane_efficiency: 0.65,
* gold_t: Array.from({ length: 12 }, (_, minute) => minute * 300),
* obs_log: [],
* purchase: {},
* first_purchase_time: { flying_courier: 300 },
* runes: { 0: 5 },
* };
* ```
* With the store’s active language set to English:
* ```ts
* const advice = analyzeMatch(match, _pm);
* advice.eff; // { grade: true, name: "Lane efficiency", value: 0.65, top: 0.6, valid: true, score(raw) { return raw; } }
* advice.farm_drought.value; // 300
* advice.farm_drought.valid; // true
* advice.skillshot.value; // undefined
* advice.skillshot.valid; // false
* advice.late_courier.value; // 120
* advice.wards.value; // 0
* advice.wards.valid; // false
* advice.roshan.value; // 0
* advice.roshan.valid; // false
* advice.rune_control.value; // 5
* advice.rune_control.top; // 5
* advice.rune_control.valid; // true
* advice.eff.score(advice.eff.value); // 0.65
* advice.rune_control.score(advice.rune_control.value); // 1
* ```
*/
export default function analyzeMatch(match: any, _pm: any) {
const { strings } = store.getState().app;
// define condition check for each advice point
Expand Down
7 changes: 6 additions & 1 deletion src/components/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ const Explorer = React.lazy(() => import("../Explorer/Explorer"));
const darkTheme = createTheme({
palette: {
mode: "dark",
primary: { main: "#C8A45D", light: "#E0BF7D", dark: "#92733B" },
secondary: { main: "#B8A4D9" },
background: { default: "#101012", paper: "#1B1B1F" },
text: { primary: "#E8E2D6", secondary: "#A6A29A" },
divider: "#443A28",
},
components: {
MuiButton: {
Expand Down Expand Up @@ -122,7 +127,7 @@ const Back2Top = styled.button<Back2TopStylesProps>`
transition: opacity 0.3s ease-in-out;

&:hover {
background-color: rgb(26, 108, 239);
background-color: var(--od-accent-hover);
}

#back2TopTxt {
Expand Down
9 changes: 8 additions & 1 deletion src/components/Container/Container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Spacer } from "../Spacer/Spacer";

type ContainerProps = {
title?: string;
titleActions?: React.ReactNode;
subtitle?: string | React.ReactNode;
style?: any;
className?: string;
Expand All @@ -21,6 +22,7 @@ type ContainerProps = {

const Container = ({
title,
titleActions,
subtitle,
style,
className,
Expand All @@ -35,7 +37,12 @@ const Container = ({
}: ContainerProps) =>
!hide ? (
<div className={className} style={{ ...style }}>
{title && <Heading title={title} subtitle={subtitle} titleTo={titleTo} />}
{title && (titleActions ? (
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }}>
<Heading title={title} subtitle={subtitle} titleTo={titleTo} />
{titleActions}
</div>
) : <Heading title={title} subtitle={subtitle} titleTo={titleTo} />)}
{error && (
<>
<Spacer variant="1" />
Expand Down
30 changes: 25 additions & 5 deletions src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import SearchIcon from "@mui/icons-material/Search";
import { Toolbar } from "@mui/material";
import React, { useCallback, useEffect, useState, useRef } from "react";
import { useSelector } from "react-redux";
import { Link } from "react-router-dom";
import { NavLink } from "react-router-dom";
import styled from "styled-components";

import config from "../../config";
Expand Down Expand Up @@ -105,23 +105,43 @@ const MenuLogoWrapper = styled.div`
padding: 24px 0;
`;

const DrawerLink = styled(Link)`
const DrawerLink = styled(NavLink)`
color: ${constants.textColorPrimary};

&.active {
color: ${constants.primaryLinkColor};
}
&.active li {
background: var(--od-accent-soft);
box-shadow: inset 3px 0 0 ${constants.primaryLinkColor};
}

& li:hover {
background-color: rgba(0, 0, 0, 0.08);
transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
}

span {
font-family: ${constants.fontFamilyFuturistic};
font-size: ${constants.fontSizeSmall};
font-size: ${constants.fontSizeMedium};
}
`;

const LinkGroupLink = styled(Link)`
const LinkGroupLink = styled(NavLink)`
font-family: ${constants.fontFamilyFuturistic};
font-size: ${constants.fontSizeSmall} !important;
font-size: ${constants.fontSizeMedium} !important;
padding: 8px 0;
border-bottom: 2px solid transparent;

&.active {
color: ${constants.primaryLinkColor};
border-bottom-color: ${constants.primaryLinkColor};
}

&.active:hover {
color: ${constants.primaryLinkColor};
opacity: 1;
}
`;

const LinkGroup = ({ navbarPages }: { navbarPages: any[] }) => (
Expand Down
2 changes: 1 addition & 1 deletion src/components/Home/Why.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const StyledDiv = styled.div`
& svg {
width: 48px;
height: 48px;
fill: ${constants.colorBlue};
fill: ${constants.colorGolden};
}

& .headline {
Expand Down
4 changes: 3 additions & 1 deletion src/components/LoadingOverlay/LoadingOverlayBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import Spinner from "../Spinner/Spinner";

interface LoadingOverlayBaseProps {
text?: string;
textSize?: number;
paddingTop?: string;
alignItems?: "center" | "flex-start" | "flex-end";
}

const LoadingOverlayBase: React.FC<LoadingOverlayBaseProps> = ({
text,
textSize,
paddingTop = "30%",
alignItems = "flex-start",
}) => (
Expand All @@ -21,7 +23,7 @@ const LoadingOverlayBase: React.FC<LoadingOverlayBaseProps> = ({
justifyContent: "center",
}}
>
<Spinner text={text} />
<Spinner text={text} textSize={textSize} />
</div>
);

Expand Down
4 changes: 3 additions & 1 deletion src/components/LoadingOverlay/LoadingOverlayUpper30.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { LoadingOverlayBase } from "./LoadingOverlayBase";

interface LoadingOverlayUpper30Props {
text?: string;
textSize?: number;
}

const LoadingOverlayUpper30: React.FC<LoadingOverlayUpper30Props> = ({
text,
}) => <LoadingOverlayBase text={text} paddingTop="15%" />;
textSize,
}) => <LoadingOverlayBase text={text} textSize={textSize} paddingTop="15%" />;

export { LoadingOverlayUpper30 };
Loading
Loading