From 4edc660606222853e6880a671ad5a66ecb8a55fb Mon Sep 17 00:00:00 2001 From: Cho-P4 Date: Thu, 23 Jul 2026 01:17:22 +0800 Subject: [PATCH] fix(ui): preserve card radius in MUI sx --- docs/material-you-maintenance.md | 49 +++++++++++++++++++ .../manga/components/cards/MangaGridCard.tsx | 4 +- .../manga/components/details/Thumbnail.tsx | 40 ++++++++------- .../components/DesktopSideBar.tsx | 4 +- .../theme/services/MaterialYouPalette.test.ts | 2 +- .../theme/services/MaterialYouPalette.ts | 4 +- .../services/MaterialYouVisualTokens.test.ts | 1 + .../theme/services/MaterialYouVisualTokens.ts | 8 ++- src/features/theme/services/ThemeCreator.ts | 5 +- 9 files changed, 82 insertions(+), 35 deletions(-) create mode 100644 docs/material-you-maintenance.md diff --git a/docs/material-you-maintenance.md b/docs/material-you-maintenance.md new file mode 100644 index 00000000..4b88f836 --- /dev/null +++ b/docs/material-you-maintenance.md @@ -0,0 +1,49 @@ +# Material You fork maintenance + +This branch keeps Suwayomi behavior upstream-compatible and confines the visual fork to theme services, navigation components, manga presentation, and reader chrome. + +## Update from upstream + +```sh +git fetch origin +git switch codex/material-you +git rebase origin/master +pnpm test:material-you +pnpm format:check +pnpm lint +pnpm tsc +pnpm build +``` + +Resolve upstream conflicts by preserving server requests, GraphQL types, routing, reader state, and settings behavior. Material You additions should remain presentation-only. + +## Build + +Use Node 24 or newer and pnpm 11.1.2 or newer: + +```sh +pnpm setup +pnpm build +``` + +The deployable static application is written to `build/`. + +## NAS deployment + +Suwayomi uses the custom WebUI directory: + +```text +/vol4/1000/docker-software/manga-stack/suwayomi/webUI +``` + +Before each release, copy `server.conf` and the complete live `webUI/` directory into a timestamped directory below: + +```text +/vol4/1000/docker-software/.integration-backups/ +``` + +Upload `build/` to a sibling staging directory, compare file counts and hashes, then rename the staging directory to `webUI` in one operation. Keep `webUI.flavor=CUSTOM` and the automatic WebUI update interval at `0`. Recreate only the `suwayomi` service and force-refresh browser/PWA caches. + +## Rollback + +Stop or recreate only Suwayomi, move the failed live `webUI/` aside, restore the backed-up `webUI/` and `server.conf`, then recreate Suwayomi. Verify HTTP 200, the GraphQL `{ __typename }` query, hashed assets, and container logs before closing the rollback. diff --git a/src/features/manga/components/cards/MangaGridCard.tsx b/src/features/manga/components/cards/MangaGridCard.tsx index 670178d2..ba37ae0e 100644 --- a/src/features/manga/components/cards/MangaGridCard.tsx +++ b/src/features/manga/components/cards/MangaGridCard.tsx @@ -79,7 +79,7 @@ export const MangaGridCard = memo( flexDirection: 'column', m: 0.25, outline: selected ? '4px solid' : undefined, - borderRadius: MATERIAL_YOU_VISUALS.cardRadius, + borderRadius: MATERIAL_YOU_VISUALS.cardRadiusCss, outlineColor: (theme) => theme.palette.primary.main, backgroundColor: (theme) => (selected ? theme.palette.primary.main : undefined), transition: `transform ${MATERIAL_YOU_VISUALS.standardTransition}, filter ${MATERIAL_YOU_VISUALS.standardTransition}`, @@ -106,7 +106,7 @@ export const MangaGridCard = memo( // force standard aspect ratio of manga covers aspectRatio: MANGA_COVER_ASPECT_RATIO, display: 'flex', - borderRadius: MATERIAL_YOU_VISUALS.cardRadius, + borderRadius: MATERIAL_YOU_VISUALS.cardRadiusCss, }} > { - if (!isDynamicColorResultCurrent(url, latestUrlRef.current, aborted)) { - return; - } + ]) + .then(([palette, averageColor]) => { + if (!isDynamicColorResultCurrent(url, latestUrlRef.current, aborted)) { + return; + } - if ( - !palette.Vibrant || - !palette.DarkVibrant || - !palette.LightVibrant || - !palette.LightMuted || - !palette.Muted || - !palette.DarkMuted - ) { - return; - } + if ( + !palette.Vibrant || + !palette.DarkVibrant || + !palette.LightVibrant || + !palette.LightMuted || + !palette.Muted || + !palette.DarkMuted + ) { + return; + } - setDynamicColor({ - ...palette, - average: averageColor, - } as TAppThemeContext['dynamicColor']); - }).catch(noOp); + setDynamicColor({ + ...palette, + average: averageColor, + } as TAppThemeContext['dynamicColor']); + }) + .catch(noOp); }; }; diff --git a/src/features/navigation-bar/components/DesktopSideBar.tsx b/src/features/navigation-bar/components/DesktopSideBar.tsx index a3dacd84..4f3b98bc 100644 --- a/src/features/navigation-bar/components/DesktopSideBar.tsx +++ b/src/features/navigation-bar/components/DesktopSideBar.tsx @@ -64,9 +64,7 @@ export const DesktopSideBar = ({ navBarItems }: { navBarItems: NavbarItem[] }) = sx={{ pt: 'env(safe-area-inset-top)', pl: 'env(safe-area-inset-left)', - width: isCollapsed - ? MATERIAL_YOU_NAVIGATION.railWidth - : MATERIAL_YOU_NAVIGATION.expandedWidth, + width: isCollapsed ? MATERIAL_YOU_NAVIGATION.railWidth : MATERIAL_YOU_NAVIGATION.expandedWidth, }} > diff --git a/src/features/theme/services/MaterialYouPalette.test.ts b/src/features/theme/services/MaterialYouPalette.test.ts index 9632c8e8..3b08b875 100644 --- a/src/features/theme/services/MaterialYouPalette.test.ts +++ b/src/features/theme/services/MaterialYouPalette.test.ts @@ -8,7 +8,7 @@ import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; -import { createMaterialYouScheme, MATERIAL_YOU_STABLE_SEED } from './MaterialYouPalette.ts'; +import { createMaterialYouScheme, MATERIAL_YOU_STABLE_SEED } from '@/features/theme/services/MaterialYouPalette.ts'; describe('createMaterialYouScheme', () => { it('creates a deterministic stable blue light scheme', () => { diff --git a/src/features/theme/services/MaterialYouPalette.ts b/src/features/theme/services/MaterialYouPalette.ts index 08a5fdab..f1440fe1 100644 --- a/src/features/theme/services/MaterialYouPalette.ts +++ b/src/features/theme/services/MaterialYouPalette.ts @@ -50,7 +50,9 @@ const tone = (seed: string, lightness: number, saturationDelta: number = 0): str const shiftedTone = (seed: string, hueDelta: number, lightness: number): string => { const parsed = parseToHsl(seed); - return toUpperHex(hsl((parsed.hue + hueDelta + 360) % 360, coerceIn(parsed.saturation * 0.58, 0.28, 0.62), lightness)); + return toUpperHex( + hsl((parsed.hue + hueDelta + 360) % 360, coerceIn(parsed.saturation * 0.58, 0.28, 0.62), lightness), + ); }; export const createMaterialYouScheme = ( diff --git a/src/features/theme/services/MaterialYouVisualTokens.test.ts b/src/features/theme/services/MaterialYouVisualTokens.test.ts index 7a963bed..55bd2352 100644 --- a/src/features/theme/services/MaterialYouVisualTokens.test.ts +++ b/src/features/theme/services/MaterialYouVisualTokens.test.ts @@ -16,6 +16,7 @@ import { describe('Material You visual tokens', () => { it('defines lifted cards and low-distraction reader chrome', () => { assert.equal(MATERIAL_YOU_VISUALS.cardRadius, 20); + assert.equal(MATERIAL_YOU_VISUALS.cardRadiusCss, '20px'); assert.equal(MATERIAL_YOU_VISUALS.cardHoverLift, -4); assert.equal(MATERIAL_YOU_VISUALS.detailRadius, 24); assert.equal(MATERIAL_YOU_VISUALS.readerChromeOpacity, 0.92); diff --git a/src/features/theme/services/MaterialYouVisualTokens.ts b/src/features/theme/services/MaterialYouVisualTokens.ts index 6dd603a6..d1f947d2 100644 --- a/src/features/theme/services/MaterialYouVisualTokens.ts +++ b/src/features/theme/services/MaterialYouVisualTokens.ts @@ -8,14 +8,12 @@ export const MATERIAL_YOU_VISUALS = { cardRadius: 20, + cardRadiusCss: '20px', cardHoverLift: -4, detailRadius: 24, readerChromeOpacity: 0.92, standardTransition: '180ms cubic-bezier(0.2, 0, 0, 1)', } as const; -export const isDynamicColorResultCurrent = ( - requestedUrl: string, - currentUrl: string, - aborted: boolean, -): boolean => !aborted && requestedUrl === currentUrl; +export const isDynamicColorResultCurrent = (requestedUrl: string, currentUrl: string, aborted: boolean): boolean => + !aborted && requestedUrl === currentUrl; diff --git a/src/features/theme/services/ThemeCreator.ts b/src/features/theme/services/ThemeCreator.ts index ddae1c9c..45c07c75 100644 --- a/src/features/theme/services/ThemeCreator.ts +++ b/src/features/theme/services/ThemeCreator.ts @@ -34,10 +34,7 @@ const getVibrantColorForTheme = ( return mode === ThemeMode.LIGHT ? palette.DarkVibrant.hex : palette.LightVibrant.hex; }; -const getThemeSeed = ( - appTheme: AppTheme['muiTheme'], - mode: Exclude, -): string => { +const getThemeSeed = (appTheme: AppTheme['muiTheme'], mode: Exclude): string => { const colorScheme = appTheme.colorSchemes?.[mode]; if (typeof colorScheme !== 'object') { return MATERIAL_YOU_STABLE_SEED;