mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-25 18:26:05 +08:00
feat: add option to disable image anti-aliasing (#14278)
## What this PR does Closes #13869 Adds a global setting to toggle image anti-aliasing in AFFiNE. When disabled, images are rendered using nearest-neighbor scaling (`image-rendering: pixelated`), preserving crisp pixels for pixel art, sprites, icons, and low-resolution images. ## Why Anti-aliasing causes small images to become blurry when scaled, making it difficult to work with pixel art and technical assets. ## How to test 1. Open Settings → Appearance → Images 2. Toggle “Smooth image rendering” 3. Observe image scaling behavior: - ON: smooth / anti-aliased - OFF: pixelated / nearest-neighbor ## Notes - Frontend-only change - No backend required # BEFORE <img width="1911" height="909" alt="Screenshot 2026-01-18 202651" src="https://github.com/user-attachments/assets/a40816c3-93fa-416d-90ec-38a919da182f" /> # AFTER <img width="1919" height="910" alt="Screenshot 2026-01-18 202705" src="https://github.com/user-attachments/assets/19fc348b-5f14-4e32-b6a8-a0905e569af5" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an Images section in Appearance with a toggle to switch image antialiasing on/off (setting is persisted). * **Style** * When antialiasing is turned off, images render with pixelated scaling for a crisp, non-smoothed look. * **Localization** * Added English labels and description for the new Images and antialiasing options. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
This commit is contained in:
@@ -17,6 +17,7 @@ export type AppSetting = {
|
|||||||
autoDownloadUpdate: boolean;
|
autoDownloadUpdate: boolean;
|
||||||
enableTelemetry: boolean;
|
enableTelemetry: boolean;
|
||||||
showLinkedDocInSidebar: boolean;
|
showLinkedDocInSidebar: boolean;
|
||||||
|
disableImageAntialiasing: boolean;
|
||||||
};
|
};
|
||||||
export const windowFrameStyleOptions: AppSetting['windowFrameStyle'][] = [
|
export const windowFrameStyleOptions: AppSetting['windowFrameStyle'][] = [
|
||||||
'frameless',
|
'frameless',
|
||||||
@@ -35,6 +36,7 @@ const appSettingBaseAtom = atomWithStorage<AppSetting>(
|
|||||||
autoDownloadUpdate: true,
|
autoDownloadUpdate: true,
|
||||||
enableTelemetry: true,
|
enableTelemetry: true,
|
||||||
showLinkedDocInSidebar: true,
|
showLinkedDocInSidebar: true,
|
||||||
|
disableImageAntialiasing: false,
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export function App() {
|
|||||||
BUILD_CONFIG.isElectron &&
|
BUILD_CONFIG.isElectron &&
|
||||||
environment.isMacOs &&
|
environment.isMacOs &&
|
||||||
appSettings.enableBlurBackground;
|
appSettings.enableBlurBackground;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FrameworkRoot framework={frameworkProvider}>
|
<FrameworkRoot framework={frameworkProvider}>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
|
|||||||
@@ -322,3 +322,7 @@ math {
|
|||||||
.ai-block-diff-deleted .affine-block-component {
|
.ai-block-diff-deleted .affine-block-component {
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html[data-image-antialiasing='off'] img {
|
||||||
|
image-rendering: pixelated;
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,11 +6,14 @@ import { Provider } from 'jotai';
|
|||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { useImageAntialiasing } from '../hooks/use-image-antialiasing';
|
||||||
|
|
||||||
export type AffineContextProps = PropsWithChildren<{
|
export type AffineContextProps = PropsWithChildren<{
|
||||||
store?: ReturnType<typeof createStore>;
|
store?: ReturnType<typeof createStore>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export function AffineContext(props: AffineContextProps) {
|
export function AffineContext(props: AffineContextProps) {
|
||||||
|
useImageAntialiasing();
|
||||||
return (
|
return (
|
||||||
<ProviderComposer
|
<ProviderComposer
|
||||||
contexts={useMemo(
|
contexts={useMemo(
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
import { useAppSettingHelper } from './affine/use-app-setting-helper';
|
||||||
|
|
||||||
|
export function useImageAntialiasing() {
|
||||||
|
const { appSettings } = useAppSettingHelper();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.documentElement.dataset.imageAntialiasing =
|
||||||
|
appSettings.disableImageAntialiasing ? 'off' : 'on';
|
||||||
|
}, [appSettings.disableImageAntialiasing]);
|
||||||
|
}
|
||||||
@@ -197,6 +197,23 @@ export const AppearanceSettings = () => {
|
|||||||
{enableThemeEditor ? <ThemeEditorSetting /> : null}
|
{enableThemeEditor ? <ThemeEditorSetting /> : null}
|
||||||
</SettingWrapper>
|
</SettingWrapper>
|
||||||
|
|
||||||
|
<SettingWrapper title={t['com.affine.appearanceSettings.images.title']()}>
|
||||||
|
<SettingRow
|
||||||
|
name={t['com.affine.appearanceSettings.images.antialiasing.title']()}
|
||||||
|
desc={t[
|
||||||
|
'com.affine.appearanceSettings.images.antialiasing.description'
|
||||||
|
]()}
|
||||||
|
data-testid="image-antialiasing-trigger"
|
||||||
|
>
|
||||||
|
<Switch
|
||||||
|
checked={!appSettings.disableImageAntialiasing}
|
||||||
|
onChange={checked =>
|
||||||
|
updateSettings('disableImageAntialiasing', !checked)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SettingRow>
|
||||||
|
</SettingWrapper>
|
||||||
|
|
||||||
{BUILD_CONFIG.isWeb && !environment.isMobile ? (
|
{BUILD_CONFIG.isWeb && !environment.isMobile ? (
|
||||||
<SettingWrapper title={t['com.affine.setting.appearance.links']()}>
|
<SettingWrapper title={t['com.affine.setting.appearance.links']()}>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"ar": 98,
|
"ar": 98,
|
||||||
"ca": 100,
|
"ca": 99,
|
||||||
"da": 4,
|
"da": 4,
|
||||||
"de": 99,
|
"de": 99,
|
||||||
"el-GR": 98,
|
"el-GR": 98,
|
||||||
"en": 100,
|
"en": 100,
|
||||||
"es-AR": 98,
|
"es-AR": 98,
|
||||||
"es-CL": 100,
|
"es-CL": 99,
|
||||||
"es": 98,
|
"es": 98,
|
||||||
"fa": 98,
|
"fa": 98,
|
||||||
"fr": 100,
|
"fr": 100,
|
||||||
@@ -19,9 +19,9 @@
|
|||||||
"pl": 100,
|
"pl": 100,
|
||||||
"pt-BR": 98,
|
"pt-BR": 98,
|
||||||
"ru": 100,
|
"ru": 100,
|
||||||
"sv-SE": 99,
|
"sv-SE": 98,
|
||||||
"uk": 98,
|
"uk": 98,
|
||||||
"ur": 2,
|
"ur": 2,
|
||||||
"zh-Hans": 100,
|
"zh-Hans": 100,
|
||||||
"zh-Hant": 99
|
"zh-Hant": 98
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -958,6 +958,18 @@ export function useAFFiNEI18N(): {
|
|||||||
* `Customize Theme`
|
* `Customize Theme`
|
||||||
*/
|
*/
|
||||||
["com.affine.appearanceSettings.customize-theme.title"](): string;
|
["com.affine.appearanceSettings.customize-theme.title"](): string;
|
||||||
|
/**
|
||||||
|
* `Images`
|
||||||
|
*/
|
||||||
|
["com.affine.appearanceSettings.images.title"](): string;
|
||||||
|
/**
|
||||||
|
* `Smooth image rendering`
|
||||||
|
*/
|
||||||
|
["com.affine.appearanceSettings.images.antialiasing.title"](): string;
|
||||||
|
/**
|
||||||
|
* `When disabled, images are rendered using nearest-neighbor scaling for crisp pixels.`
|
||||||
|
*/
|
||||||
|
["com.affine.appearanceSettings.images.antialiasing.description"](): string;
|
||||||
/**
|
/**
|
||||||
* `Reset all`
|
* `Reset all`
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -228,6 +228,9 @@
|
|||||||
"com.affine.appearanceSettings.color.title": "Colour mode",
|
"com.affine.appearanceSettings.color.title": "Colour mode",
|
||||||
"com.affine.appearanceSettings.customize-theme.description": "Edit all AFFiNE theme variables here",
|
"com.affine.appearanceSettings.customize-theme.description": "Edit all AFFiNE theme variables here",
|
||||||
"com.affine.appearanceSettings.customize-theme.title": "Customize Theme",
|
"com.affine.appearanceSettings.customize-theme.title": "Customize Theme",
|
||||||
|
"com.affine.appearanceSettings.images.title": "Images",
|
||||||
|
"com.affine.appearanceSettings.images.antialiasing.title": "Smooth image rendering",
|
||||||
|
"com.affine.appearanceSettings.images.antialiasing.description": "When disabled, images are rendered using nearest-neighbor scaling for crisp pixels.",
|
||||||
"com.affine.appearanceSettings.customize-theme.reset": "Reset all",
|
"com.affine.appearanceSettings.customize-theme.reset": "Reset all",
|
||||||
"com.affine.appearanceSettings.customize-theme.open": "Open Theme Editor",
|
"com.affine.appearanceSettings.customize-theme.open": "Open Theme Editor",
|
||||||
"com.affine.appearanceSettings.font.description": "Choose your font style",
|
"com.affine.appearanceSettings.font.description": "Choose your font style",
|
||||||
|
|||||||
Reference in New Issue
Block a user