fix(ui): preserve card radius in MUI sx
This commit is contained in:
49
docs/material-you-maintenance.md
Normal file
49
docs/material-you-maintenance.md
Normal file
@@ -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.
|
||||||
@@ -79,7 +79,7 @@ export const MangaGridCard = memo(
|
|||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
m: 0.25,
|
m: 0.25,
|
||||||
outline: selected ? '4px solid' : undefined,
|
outline: selected ? '4px solid' : undefined,
|
||||||
borderRadius: MATERIAL_YOU_VISUALS.cardRadius,
|
borderRadius: MATERIAL_YOU_VISUALS.cardRadiusCss,
|
||||||
outlineColor: (theme) => theme.palette.primary.main,
|
outlineColor: (theme) => theme.palette.primary.main,
|
||||||
backgroundColor: (theme) => (selected ? theme.palette.primary.main : undefined),
|
backgroundColor: (theme) => (selected ? theme.palette.primary.main : undefined),
|
||||||
transition: `transform ${MATERIAL_YOU_VISUALS.standardTransition}, filter ${MATERIAL_YOU_VISUALS.standardTransition}`,
|
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
|
// force standard aspect ratio of manga covers
|
||||||
aspectRatio: MANGA_COVER_ASPECT_RATIO,
|
aspectRatio: MANGA_COVER_ASPECT_RATIO,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
borderRadius: MATERIAL_YOU_VISUALS.cardRadius,
|
borderRadius: MATERIAL_YOU_VISUALS.cardRadiusCss,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CardActionArea
|
<CardActionArea
|
||||||
|
|||||||
@@ -87,27 +87,29 @@ export const Thumbnail = ({
|
|||||||
[0, 0, 0, 255, 75],
|
[0, 0, 0, 255, 75],
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
]).then(([palette, averageColor]) => {
|
])
|
||||||
if (!isDynamicColorResultCurrent(url, latestUrlRef.current, aborted)) {
|
.then(([palette, averageColor]) => {
|
||||||
return;
|
if (!isDynamicColorResultCurrent(url, latestUrlRef.current, aborted)) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!palette.Vibrant ||
|
!palette.Vibrant ||
|
||||||
!palette.DarkVibrant ||
|
!palette.DarkVibrant ||
|
||||||
!palette.LightVibrant ||
|
!palette.LightVibrant ||
|
||||||
!palette.LightMuted ||
|
!palette.LightMuted ||
|
||||||
!palette.Muted ||
|
!palette.Muted ||
|
||||||
!palette.DarkMuted
|
!palette.DarkMuted
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setDynamicColor({
|
setDynamicColor({
|
||||||
...palette,
|
...palette,
|
||||||
average: averageColor,
|
average: averageColor,
|
||||||
} as TAppThemeContext['dynamicColor']);
|
} as TAppThemeContext['dynamicColor']);
|
||||||
}).catch(noOp);
|
})
|
||||||
|
.catch(noOp);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -64,9 +64,7 @@ export const DesktopSideBar = ({ navBarItems }: { navBarItems: NavbarItem[] }) =
|
|||||||
sx={{
|
sx={{
|
||||||
pt: 'env(safe-area-inset-top)',
|
pt: 'env(safe-area-inset-top)',
|
||||||
pl: 'env(safe-area-inset-left)',
|
pl: 'env(safe-area-inset-left)',
|
||||||
width: isCollapsed
|
width: isCollapsed ? MATERIAL_YOU_NAVIGATION.railWidth : MATERIAL_YOU_NAVIGATION.expandedWidth,
|
||||||
? MATERIAL_YOU_NAVIGATION.railWidth
|
|
||||||
: MATERIAL_YOU_NAVIGATION.expandedWidth,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DrawerHeader sx={{ justifyContent: isCollapsed ? 'center' : 'flex-end' }}>
|
<DrawerHeader sx={{ justifyContent: isCollapsed ? 'center' : 'flex-end' }}>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { describe, it } from 'node:test';
|
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', () => {
|
describe('createMaterialYouScheme', () => {
|
||||||
it('creates a deterministic stable blue light scheme', () => {
|
it('creates a deterministic stable blue light scheme', () => {
|
||||||
|
|||||||
@@ -50,7 +50,9 @@ const tone = (seed: string, lightness: number, saturationDelta: number = 0): str
|
|||||||
const shiftedTone = (seed: string, hueDelta: number, lightness: number): string => {
|
const shiftedTone = (seed: string, hueDelta: number, lightness: number): string => {
|
||||||
const parsed = parseToHsl(seed);
|
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 = (
|
export const createMaterialYouScheme = (
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
describe('Material You visual tokens', () => {
|
describe('Material You visual tokens', () => {
|
||||||
it('defines lifted cards and low-distraction reader chrome', () => {
|
it('defines lifted cards and low-distraction reader chrome', () => {
|
||||||
assert.equal(MATERIAL_YOU_VISUALS.cardRadius, 20);
|
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.cardHoverLift, -4);
|
||||||
assert.equal(MATERIAL_YOU_VISUALS.detailRadius, 24);
|
assert.equal(MATERIAL_YOU_VISUALS.detailRadius, 24);
|
||||||
assert.equal(MATERIAL_YOU_VISUALS.readerChromeOpacity, 0.92);
|
assert.equal(MATERIAL_YOU_VISUALS.readerChromeOpacity, 0.92);
|
||||||
|
|||||||
@@ -8,14 +8,12 @@
|
|||||||
|
|
||||||
export const MATERIAL_YOU_VISUALS = {
|
export const MATERIAL_YOU_VISUALS = {
|
||||||
cardRadius: 20,
|
cardRadius: 20,
|
||||||
|
cardRadiusCss: '20px',
|
||||||
cardHoverLift: -4,
|
cardHoverLift: -4,
|
||||||
detailRadius: 24,
|
detailRadius: 24,
|
||||||
readerChromeOpacity: 0.92,
|
readerChromeOpacity: 0.92,
|
||||||
standardTransition: '180ms cubic-bezier(0.2, 0, 0, 1)',
|
standardTransition: '180ms cubic-bezier(0.2, 0, 0, 1)',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const isDynamicColorResultCurrent = (
|
export const isDynamicColorResultCurrent = (requestedUrl: string, currentUrl: string, aborted: boolean): boolean =>
|
||||||
requestedUrl: string,
|
!aborted && requestedUrl === currentUrl;
|
||||||
currentUrl: string,
|
|
||||||
aborted: boolean,
|
|
||||||
): boolean => !aborted && requestedUrl === currentUrl;
|
|
||||||
|
|||||||
@@ -34,10 +34,7 @@ const getVibrantColorForTheme = (
|
|||||||
return mode === ThemeMode.LIGHT ? palette.DarkVibrant.hex : palette.LightVibrant.hex;
|
return mode === ThemeMode.LIGHT ? palette.DarkVibrant.hex : palette.LightVibrant.hex;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getThemeSeed = (
|
const getThemeSeed = (appTheme: AppTheme['muiTheme'], mode: Exclude<ThemeMode, ThemeMode.SYSTEM>): string => {
|
||||||
appTheme: AppTheme['muiTheme'],
|
|
||||||
mode: Exclude<ThemeMode, ThemeMode.SYSTEM>,
|
|
||||||
): string => {
|
|
||||||
const colorScheme = appTheme.colorSchemes?.[mode];
|
const colorScheme = appTheme.colorSchemes?.[mode];
|
||||||
if (typeof colorScheme !== 'object') {
|
if (typeof colorScheme !== 'object') {
|
||||||
return MATERIAL_YOU_STABLE_SEED;
|
return MATERIAL_YOU_STABLE_SEED;
|
||||||
|
|||||||
Reference in New Issue
Block a user