diff --git a/package.json b/package.json index 0214391f..d2df7dbe 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "react-i18next": "^14.1.0", "react-markdown": "^9.0.1", "react-mosaic-component": "^6.1.0", - "recharts": "^2.12.2", + "recharts": "^2.14.1", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.0", "swr": "^2.2.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 74305c11..279ade9f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,7 +16,7 @@ importers: version: 16.5.0(@types/react-dom@18.3.2)(@types/react@18.3.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mantine/charts': specifier: ^7.6.2 - version: 7.6.2(@mantine/core@7.6.2(@mantine/hooks@7.6.2(react@18.3.1))(@types/react@18.3.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.6.2(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(recharts@2.12.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + version: 7.6.2(@mantine/core@7.6.2(@mantine/hooks@7.6.2(react@18.3.1))(@types/react@18.3.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.6.2(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(recharts@2.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@mantine/core': specifier: ^7.6.2 version: 7.6.2(@mantine/hooks@7.6.2(react@18.3.1))(@types/react@18.3.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -171,8 +171,8 @@ importers: specifier: ^6.1.0 version: 6.1.0(@types/hoist-non-react-statics@3.3.6)(@types/node@22.10.1)(@types/react@18.3.14)(dnd-core@16.0.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) recharts: - specifier: ^2.12.2 - version: 2.12.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^2.14.1 + version: 2.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) rehype-raw: specifier: ^7.0.0 version: 7.0.0 @@ -1277,8 +1277,8 @@ packages: '@types/d3-shape@3.1.6': resolution: {integrity: sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==} - '@types/d3-time@3.0.3': - resolution: {integrity: sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==} + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} '@types/d3-timer@3.0.2': resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} @@ -2366,6 +2366,9 @@ packages: react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-markdown@9.0.1: resolution: {integrity: sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==} peerDependencies: @@ -2429,8 +2432,8 @@ packages: peerDependencies: react: ^16.3.0 || ^17.0.0 || ^18.0.0 - react-smooth@4.0.0: - resolution: {integrity: sha512-2NMXOBY1uVUQx1jBeENGA497HK20y6CPGYL1ZnJLeoQ8rrc3UfmOM82sRxtzpcoCkUMy4CS0RGylfuVhuFjBgg==} + react-smooth@4.0.3: + resolution: {integrity: sha512-PyxIrra8WZWrMRFcCiJsZ+JqFaxEINAt+v/w++wQKQlmO99Eh3+JTLeKApdTsLX2roBdWYXqPsaS8sO4UmdzIg==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -2464,8 +2467,8 @@ packages: recharts-scale@0.4.5: resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} - recharts@2.12.2: - resolution: {integrity: sha512-9bpxjXSF5g81YsKkTSlaX7mM4b6oYI1mIYck6YkUcWuL3tomADccI51/6thY4LmvhYuRTwpfrOvE80Zc3oBRfQ==} + recharts@2.14.1: + resolution: {integrity: sha512-xtWulflkA+/xu4/QClBdtZYN30dbvTHjxjkh5XTMrH/CQ3WGDDPHHa/LLKCbgoqz0z3UaSH2/blV1i6VNMeh1g==} engines: {node: '>=14'} peerDependencies: react: ^16.0.0 || ^17.0.0 || ^18.0.0 @@ -3282,13 +3285,13 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - '@mantine/charts@7.6.2(@mantine/core@7.6.2(@mantine/hooks@7.6.2(react@18.3.1))(@types/react@18.3.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.6.2(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(recharts@2.12.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': + '@mantine/charts@7.6.2(@mantine/core@7.6.2(@mantine/hooks@7.6.2(react@18.3.1))(@types/react@18.3.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.6.2(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(recharts@2.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: '@mantine/core': 7.6.2(@mantine/hooks@7.6.2(react@18.3.1))(@types/react@18.3.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mantine/hooks': 7.6.2(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - recharts: 2.12.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + recharts: 2.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mantine/core@7.6.2(@mantine/hooks@7.6.2(react@18.3.1))(@types/react@18.3.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -3765,13 +3768,13 @@ snapshots: '@types/d3-scale@4.0.8': dependencies: - '@types/d3-time': 3.0.3 + '@types/d3-time': 3.0.4 '@types/d3-shape@3.1.6': dependencies: '@types/d3-path': 3.1.0 - '@types/d3-time@3.0.3': {} + '@types/d3-time@3.0.4': {} '@types/d3-timer@3.0.2': {} @@ -5202,6 +5205,8 @@ snapshots: react-is@18.2.0: {} + react-is@18.3.1: {} + react-markdown@9.0.1(@types/react@18.3.14)(react@18.3.1): dependencies: '@types/hast': 3.0.4 @@ -5283,7 +5288,7 @@ snapshots: dependencies: react: 18.3.1 - react-smooth@4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-smooth@4.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: fast-equals: 5.0.1 prop-types: 15.8.1 @@ -5326,15 +5331,15 @@ snapshots: dependencies: decimal.js-light: 2.5.1 - recharts@2.12.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + recharts@2.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: clsx: 2.1.0 eventemitter3: 4.0.7 lodash: 4.17.21 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-is: 16.13.1 - react-smooth: 4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-is: 18.3.1 + react-smooth: 4.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) recharts-scale: 0.4.5 tiny-invariant: 1.3.3 victory-vendor: 36.9.2 @@ -5648,7 +5653,7 @@ snapshots: '@types/d3-interpolate': 3.0.4 '@types/d3-scale': 4.0.8 '@types/d3-shape': 3.1.6 - '@types/d3-time': 3.0.3 + '@types/d3-time': 3.0.4 '@types/d3-timer': 3.0.2 d3-array: 3.2.4 d3-ease: 3.0.1 diff --git a/src/components/boards/Board.tsx b/src/components/boards/Board.tsx index 4a34e38e..ad71c311 100644 --- a/src/components/boards/Board.tsx +++ b/src/components/boards/Board.tsx @@ -182,10 +182,7 @@ function Board({ const [pendingMove, setPendingMove] = useState(null); const turn = pos?.turn || "white"; - const orientation = - movable === "white" || movable === "black" - ? movable - : headers.orientation || "white"; + const orientation = headers.orientation || "white"; const toggleOrientation = () => setHeaders({ ...headers, diff --git a/src/components/boards/BoardAnalysis.tsx b/src/components/boards/BoardAnalysis.tsx index eb4cf34d..507d6bb7 100644 --- a/src/components/boards/BoardAnalysis.tsx +++ b/src/components/boards/BoardAnalysis.tsx @@ -48,13 +48,7 @@ function BoardAnalysis() { const store = useContext(TreeStateContext)!; - const rootFen = useStore(store, (s) => s.root.fen); - const is960 = useStore(store, (s) => s.headers.variant === "Chess960"); const dirty = useStore(store, (s) => s.dirty); - const moves = useStore( - store, - useShallow((s) => getVariationLine(s.root, s.position, is960)), - ); const reset = useStore(store, (s) => s.reset); const clearShapes = useStore(store, (s) => s.clearShapes); @@ -133,7 +127,7 @@ function BoardAnalysis() { return ( <> - + s.headers.variant === "Chess960"); + const fen = useStore(store, (s) => s.root.fen); + + const moves = useStore( + store, + useShallow((s) => getVariationLine(s.root, s.position, is960)), + ); const [pos, error] = positionFromFen(fen); if (pos) { @@ -78,7 +81,7 @@ function EvalListener({ fen={fen} moves={moves} threat={threat} - chess960={chess960} + chess960={is960} /> )); } @@ -131,7 +134,7 @@ function EngineListener({ payload.engine === engine.name && payload.tab === activeTab && payload.fen === searchingFen && - payload.moves.join(",") === searchingMoves.join(",") && + equal(payload.moves, searchingMoves) && settings.enabled && !isGameOver ) { diff --git a/src/components/common/CompleteMoveCell.tsx b/src/components/common/CompleteMoveCell.tsx index 8f06320c..d7559a2a 100644 --- a/src/components/common/CompleteMoveCell.tsx +++ b/src/components/common/CompleteMoveCell.tsx @@ -4,7 +4,7 @@ import type { Annotation } from "@/utils/annotation"; import { hasMorePriority, stripClock } from "@/utils/chess"; import { type TreeNode, treeIterator } from "@/utils/treeReducer"; import { ActionIcon, Box, Menu, Portal, Tooltip } from "@mantine/core"; -import { shallowEqual, useClickOutside } from "@mantine/hooks"; +import { useClickOutside } from "@mantine/hooks"; import { IconArrowsJoin, IconChevronUp, @@ -13,6 +13,7 @@ import { IconFlag, IconX, } from "@tabler/icons-react"; +import equal from "fast-deep-equal"; import { useAtomValue } from "jotai"; import { memo, useContext, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -171,12 +172,12 @@ export default memo(CompleteMoveCell, (prev, next) => { prev.move === next.move && prev.fen === next.fen && prev.comment === next.comment && - shallowEqual(prev.annotations, next.annotations) && + equal(prev.annotations, next.annotations) && prev.showComments === next.showComments && prev.first === next.first && prev.isCurrentVariation === next.isCurrentVariation && prev.isStart === next.isStart && - shallowEqual(prev.movePath, next.movePath) && + equal(prev.movePath, next.movePath) && prev.halfMoves === next.halfMoves ); }); diff --git a/src/components/common/GameNotation.tsx b/src/components/common/GameNotation.tsx index abba2173..713d5020 100644 --- a/src/components/common/GameNotation.tsx +++ b/src/components/common/GameNotation.tsx @@ -15,7 +15,7 @@ import { Text, Tooltip, } from "@mantine/core"; -import { shallowEqual, useColorScheme, useToggle } from "@mantine/hooks"; +import { useColorScheme, useToggle } from "@mantine/hooks"; import { IconArrowRight, IconArrowsSplit, @@ -27,6 +27,7 @@ import { IconPlus, } from "@tabler/icons-react"; import { INITIAL_FEN } from "chessops/fen"; +import equal from "fast-deep-equal"; import { useAtom, useAtomValue } from "jotai"; import { memo, useContext, useEffect, useRef, useState } from "react"; import React from "react"; @@ -211,14 +212,11 @@ const RenderVariationTree = memo( fen={variation.fen} movePath={[...path, variations.indexOf(variation)]} showComments={showComments} - isCurrentVariation={shallowEqual( + isCurrentVariation={equal( [...path, variations.indexOf(variation)], currentPath, )} - isStart={shallowEqual( - [...path, variations.indexOf(variation)], - start, - )} + isStart={equal([...path, variations.indexOf(variation)], start)} first /> )) : []; + const newPath = [...path, 0]; return ( <> @@ -245,10 +244,10 @@ const RenderVariationTree = memo( halfMoves={variations[0].halfMoves} move={variations[0].san} fen={variations[0].fen} - movePath={[...path, 0]} + movePath={newPath} showComments={showComments} - isCurrentVariation={shallowEqual([...path, 0], currentPath)} - isStart={shallowEqual([...path, 0], start)} + isCurrentVariation={equal(newPath, currentPath)} + isStart={equal(newPath, start)} first={first} /> )} @@ -263,7 +262,7 @@ const RenderVariationTree = memo( showVariations={showVariations} start={start} showComments={showComments} - path={[...path, 0]} + path={newPath} /> )} @@ -276,8 +275,8 @@ const RenderVariationTree = memo( prev.first === next.first && prev.showVariations === next.showVariations && prev.showComments === next.showComments && - shallowEqual(prev.path, next.path) && - shallowEqual(prev.start, next.start) + equal(prev.path, next.path) && + equal(prev.start, next.start) ); }, ); diff --git a/src/components/panels/analysis/ReportPanel.tsx b/src/components/panels/analysis/ReportPanel.tsx index fcdf0278..34058c62 100644 --- a/src/components/panels/analysis/ReportPanel.tsx +++ b/src/components/panels/analysis/ReportPanel.tsx @@ -6,9 +6,10 @@ import { activeTabAtom } from "@/state/atoms"; import { ANNOTATION_INFO, isBasicAnnotation } from "@/utils/annotation"; import { getGameStats, getMainLine } from "@/utils/chess"; import { Grid, Group, Paper, ScrollArea, Stack, Text } from "@mantine/core"; -import { shallowEqual, useToggle } from "@mantine/hooks"; +import { useToggle } from "@mantine/hooks"; import { IconZoomCheck } from "@tabler/icons-react"; import cx from "clsx"; +import equal from "fast-deep-equal"; import { useAtomValue } from "jotai"; import React, { Suspense, useState } from "react"; import { memo, useContext, useMemo } from "react"; @@ -152,8 +153,8 @@ const GameStats = memo( }, (prev, next) => { return ( - shallowEqual(prev.whiteAnnotations, next.whiteAnnotations) && - shallowEqual(prev.blackAnnotations, next.blackAnnotations) + equal(prev.whiteAnnotations, next.whiteAnnotations) && + equal(prev.blackAnnotations, next.blackAnnotations) ); }, ); diff --git a/src/components/tabs/NewTabHome.tsx b/src/components/tabs/NewTabHome.tsx index 1690db40..9c1b1741 100644 --- a/src/components/tabs/NewTabHome.tsx +++ b/src/components/tabs/NewTabHome.tsx @@ -6,8 +6,8 @@ import { useAtom } from "jotai"; import { useState } from "react"; import ImportModal from "./ImportModal"; -import { useTranslation } from "react-i18next"; import { IconChess, IconFileImport, IconPuzzle } from "@tabler/icons-react"; +import { useTranslation } from "react-i18next"; import Chessboard from "../icons/Chessboard"; export default function NewTabHome({ id }: { id: string }) { diff --git a/src/state/atoms.ts b/src/state/atoms.ts index bd588deb..6d4bf5b9 100644 --- a/src/state/atoms.ts +++ b/src/state/atoms.ts @@ -22,6 +22,7 @@ import type { SuccessDatabaseInfo } from "@/utils/db"; import { getWinChance, normalizeScore } from "@/utils/score"; import { parseUci } from "chessops"; import { INITIAL_FEN, makeFen } from "chessops/fen"; +import equal from "fast-deep-equal"; import { type PrimitiveAtom, atom } from "jotai"; import { atomFamily, @@ -449,24 +450,22 @@ export const bestMovesFamily = atomFamily( ); bestMoves.set( n, - moves - .map((m) => { - const winChance = getWinChance( - normalizeScore(m.score.value, pos?.turn || "white"), - ); - return { - pv: m.uciMoves, - winChance, - }; - }) - .filter((m) => bestWinChange - m.winChance < 10), + moves.reduce<{ pv: string[]; winChance: number }[]>((acc, m) => { + const winChance = getWinChance( + normalizeScore(m.score.value, pos?.turn || "white"), + ); + if (bestWinChange - winChance < 10) { + acc.push({ pv: m.uciMoves, winChance }); + } + return acc; + }, []), ); } n++; } return bestMoves; }), - (a, b) => a.fen === b.fen && a.gameMoves.join(",") === b.gameMoves.join(","), + (a, b) => a.fen === b.fen && equal(a.gameMoves, b.gameMoves), ); export const tabEngineSettingsFamily = atomFamily(