mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-25 14:28:51 +08:00
feat: add scroll wheel zoom setting (#9476)
### Changed Add `scroll wheel to zoom` setting option, when the option enables, user can zoom in and out with scroll wheel without pressing the cmd/ctrl key.
This commit is contained in:
@@ -2,13 +2,17 @@ import type { ExtensionType } from '@blocksuite/block-std';
|
|||||||
import { createIdentifier } from '@blocksuite/global/di';
|
import { createIdentifier } from '@blocksuite/global/di';
|
||||||
import type { DeepPartial } from '@blocksuite/global/utils';
|
import type { DeepPartial } from '@blocksuite/global/utils';
|
||||||
import type { Signal } from '@preact/signals-core';
|
import type { Signal } from '@preact/signals-core';
|
||||||
import type { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { NodePropsSchema } from '../utils/index.js';
|
import { NodePropsSchema } from '../utils/index.js';
|
||||||
|
|
||||||
export const EditorSettingSchema = NodePropsSchema;
|
export const GeneralSettingSchema = z
|
||||||
|
.object({
|
||||||
|
edgelessScrollZoom: z.boolean().default(false),
|
||||||
|
})
|
||||||
|
.merge(NodePropsSchema);
|
||||||
|
|
||||||
export type EditorSetting = z.infer<typeof EditorSettingSchema>;
|
export type EditorSetting = z.infer<typeof GeneralSettingSchema>;
|
||||||
|
|
||||||
export const EditorSettingProvider = createIdentifier<
|
export const EditorSettingProvider = createIdentifier<
|
||||||
Signal<DeepPartial<EditorSetting>>
|
Signal<DeepPartial<EditorSetting>>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import type {
|
|||||||
ShapeElementModel,
|
ShapeElementModel,
|
||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import {
|
import {
|
||||||
|
EditorSettingProvider,
|
||||||
EditPropsStore,
|
EditPropsStore,
|
||||||
FontLoaderService,
|
FontLoaderService,
|
||||||
ThemeProvider,
|
ThemeProvider,
|
||||||
@@ -371,8 +372,10 @@ export class EdgelessRootBlockComponent extends BlockComponent<
|
|||||||
private _initWheelEvent() {
|
private _initWheelEvent() {
|
||||||
this._disposables.add(
|
this._disposables.add(
|
||||||
this.dispatcher.add('wheel', ctx => {
|
this.dispatcher.add('wheel', ctx => {
|
||||||
|
const config = this.std.getOptional(EditorSettingProvider);
|
||||||
const state = ctx.get('defaultState');
|
const state = ctx.get('defaultState');
|
||||||
const e = state.event as WheelEvent;
|
const e = state.event as WheelEvent;
|
||||||
|
const edgelessScrollZoom = config?.peek().edgelessScrollZoom ?? false;
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@@ -380,7 +383,7 @@ export class EdgelessRootBlockComponent extends BlockComponent<
|
|||||||
if (viewport.locked) return;
|
if (viewport.locked) return;
|
||||||
|
|
||||||
// zoom
|
// zoom
|
||||||
if (isTouchPadPinchEvent(e)) {
|
if (isTouchPadPinchEvent(e) || edgelessScrollZoom) {
|
||||||
const rect = this.getBoundingClientRect();
|
const rect = this.getBoundingClientRect();
|
||||||
// Perform zooming relative to the mouse position
|
// Perform zooming relative to the mouse position
|
||||||
const [baseX, baseY] = this.gfx.viewport.toModelCoord(
|
const [baseX, baseY] = this.gfx.viewport.toModelCoord(
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ import type {
|
|||||||
PeekViewService,
|
PeekViewService,
|
||||||
} from '@blocksuite/affine-components/peek';
|
} from '@blocksuite/affine-components/peek';
|
||||||
import { PeekViewExtension } from '@blocksuite/affine-components/peek';
|
import { PeekViewExtension } from '@blocksuite/affine-components/peek';
|
||||||
|
import type { EditorSetting } from '@blocksuite/affine-shared/services';
|
||||||
import { BlockComponent } from '@blocksuite/block-std';
|
import { BlockComponent } from '@blocksuite/block-std';
|
||||||
import {
|
import {
|
||||||
ColorScheme,
|
ColorScheme,
|
||||||
type DocMode,
|
type DocMode,
|
||||||
type DocModeProvider,
|
type DocModeProvider,
|
||||||
|
GeneralSettingSchema,
|
||||||
type GenerateDocUrlService,
|
type GenerateDocUrlService,
|
||||||
matchFlavours,
|
matchFlavours,
|
||||||
type NotificationService,
|
type NotificationService,
|
||||||
@@ -19,7 +21,7 @@ import {
|
|||||||
import { Slot } from '@blocksuite/global/utils';
|
import { Slot } from '@blocksuite/global/utils';
|
||||||
import type { AffineEditorContainer } from '@blocksuite/presets';
|
import type { AffineEditorContainer } from '@blocksuite/presets';
|
||||||
import { type DocCollection } from '@blocksuite/store';
|
import { type DocCollection } from '@blocksuite/store';
|
||||||
import { signal } from '@preact/signals-core';
|
import { Signal, signal } from '@preact/signals-core';
|
||||||
import type { TemplateResult } from 'lit';
|
import type { TemplateResult } from 'lit';
|
||||||
|
|
||||||
import type { AttachmentViewerPanel } from './components/attachment-viewer-panel.js';
|
import type { AttachmentViewerPanel } from './components/attachment-viewer-panel.js';
|
||||||
@@ -204,3 +206,28 @@ export function mockGenerateDocUrlService(collection: DocCollection) {
|
|||||||
};
|
};
|
||||||
return generateDocUrlService;
|
return generateDocUrlService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mockEditorSetting() {
|
||||||
|
if (window.editorSetting$) return window.editorSetting$;
|
||||||
|
|
||||||
|
const initialVal = Object.entries(GeneralSettingSchema.shape).reduce(
|
||||||
|
(pre: EditorSetting, [key, schema]) => {
|
||||||
|
// @ts-expect-error key is EditorSetting field
|
||||||
|
pre[key as keyof EditorSetting] = schema.parse(undefined);
|
||||||
|
return pre;
|
||||||
|
},
|
||||||
|
{} as EditorSetting
|
||||||
|
);
|
||||||
|
|
||||||
|
const signal = new Signal<EditorSetting>(initialVal);
|
||||||
|
|
||||||
|
window.editorSetting$ = signal;
|
||||||
|
|
||||||
|
return signal;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
editorSetting$: Signal<EditorSetting>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
CommunityCanvasTextFonts,
|
CommunityCanvasTextFonts,
|
||||||
DocModeExtension,
|
DocModeExtension,
|
||||||
DocModeProvider,
|
DocModeProvider,
|
||||||
|
EditorSettingExtension,
|
||||||
FontConfigExtension,
|
FontConfigExtension,
|
||||||
GenerateDocUrlExtension,
|
GenerateDocUrlExtension,
|
||||||
GenerateDocUrlProvider,
|
GenerateDocUrlProvider,
|
||||||
@@ -26,6 +27,7 @@ import {
|
|||||||
} from '../../_common/history.js';
|
} from '../../_common/history.js';
|
||||||
import {
|
import {
|
||||||
mockDocModeService,
|
mockDocModeService,
|
||||||
|
mockEditorSetting,
|
||||||
mockGenerateDocUrlService,
|
mockGenerateDocUrlService,
|
||||||
mockNotificationService,
|
mockNotificationService,
|
||||||
mockParseDocUrlService,
|
mockParseDocUrlService,
|
||||||
@@ -126,6 +128,7 @@ export async function mountDefaultDocEditor(collection: DocCollection) {
|
|||||||
NotificationExtension(mockNotificationService(editor)),
|
NotificationExtension(mockNotificationService(editor)),
|
||||||
FontConfigExtension(CommunityCanvasTextFonts),
|
FontConfigExtension(CommunityCanvasTextFonts),
|
||||||
mockPeekViewExtension(attachmentViewerPanel),
|
mockPeekViewExtension(attachmentViewerPanel),
|
||||||
|
EditorSettingExtension(mockEditorSetting()),
|
||||||
];
|
];
|
||||||
return newSpec;
|
return newSpec;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,10 @@ import { effects as presetsEffects } from '@blocksuite/presets/effects';
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||||
import * as store from '@blocksuite/store';
|
import * as store from '@blocksuite/store';
|
||||||
|
|
||||||
import { mockDocModeService } from '../_common/mock-services.js';
|
import {
|
||||||
|
mockDocModeService,
|
||||||
|
mockEditorSetting,
|
||||||
|
} from '../_common/mock-services.js';
|
||||||
import { setupEdgelessTemplate } from '../_common/setup.js';
|
import { setupEdgelessTemplate } from '../_common/setup.js';
|
||||||
import {
|
import {
|
||||||
createStarterDocCollection,
|
createStarterDocCollection,
|
||||||
@@ -59,6 +62,7 @@ async function main() {
|
|||||||
},
|
},
|
||||||
defaultExtensions: (): ExtensionType[] => [
|
defaultExtensions: (): ExtensionType[] => [
|
||||||
FontConfigExtension(CommunityCanvasTextFonts),
|
FontConfigExtension(CommunityCanvasTextFonts),
|
||||||
|
blocks.EditorSettingExtension(mockEditorSetting()),
|
||||||
],
|
],
|
||||||
extensions: {
|
extensions: {
|
||||||
FontConfigExtension: FontConfigExtension(CommunityCanvasTextFonts),
|
FontConfigExtension: FontConfigExtension(CommunityCanvasTextFonts),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
AffineFormatBarWidget,
|
AffineFormatBarWidget,
|
||||||
CommunityCanvasTextFonts,
|
CommunityCanvasTextFonts,
|
||||||
DocModeProvider,
|
DocModeProvider,
|
||||||
|
EditorSettingExtension,
|
||||||
FontConfigExtension,
|
FontConfigExtension,
|
||||||
GenerateDocUrlExtension,
|
GenerateDocUrlExtension,
|
||||||
NotificationExtension,
|
NotificationExtension,
|
||||||
@@ -35,6 +36,7 @@ import {
|
|||||||
} from '../../_common/history.js';
|
} from '../../_common/history.js';
|
||||||
import {
|
import {
|
||||||
mockDocModeService,
|
mockDocModeService,
|
||||||
|
mockEditorSetting,
|
||||||
mockGenerateDocUrlService,
|
mockGenerateDocUrlService,
|
||||||
mockNotificationService,
|
mockNotificationService,
|
||||||
mockParseDocUrlService,
|
mockParseDocUrlService,
|
||||||
@@ -79,6 +81,7 @@ export async function mountDefaultDocEditor(collection: DocCollection) {
|
|||||||
GenerateDocUrlExtension(mockGenerateDocUrlService(collection)),
|
GenerateDocUrlExtension(mockGenerateDocUrlService(collection)),
|
||||||
NotificationExtension(mockNotificationService(editor)),
|
NotificationExtension(mockNotificationService(editor)),
|
||||||
OverrideThemeExtension(themeExtension),
|
OverrideThemeExtension(themeExtension),
|
||||||
|
EditorSettingExtension(mockEditorSetting()),
|
||||||
{
|
{
|
||||||
setup: di => {
|
setup: di => {
|
||||||
di.override(DocModeProvider, () =>
|
di.override(DocModeProvider, () =>
|
||||||
|
|||||||
@@ -120,6 +120,50 @@ test('zoom by mouse', async ({ page }) => {
|
|||||||
await assertEdgelessSelectedModelRect(page, zoomed);
|
await assertEdgelessSelectedModelRect(page, zoomed);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('zoom by mouse without ctrl pressed when edgelessScrollZoom is enabled', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await enterPlaygroundRoom(page);
|
||||||
|
await initEmptyEdgelessState(page);
|
||||||
|
|
||||||
|
await switchEditorMode(page);
|
||||||
|
await zoomResetByKeyboard(page);
|
||||||
|
await assertZoomLevel(page, 100);
|
||||||
|
|
||||||
|
await assertNoteXYWH(page, [0, 0, DEFAULT_NOTE_WIDTH, DEFAULT_NOTE_HEIGHT]);
|
||||||
|
|
||||||
|
await page.mouse.click(CENTER_X, CENTER_Y);
|
||||||
|
const original = [0, 0, DEFAULT_NOTE_WIDTH, DEFAULT_NOTE_HEIGHT];
|
||||||
|
await assertEdgelessSelectedModelRect(page, original);
|
||||||
|
|
||||||
|
// enable edgelessScrollZoom
|
||||||
|
await page.evaluate(() => {
|
||||||
|
window.editorSetting$.value = {
|
||||||
|
...window.editorSetting$.value,
|
||||||
|
edgelessScrollZoom: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// can zoom without ctrl pressed
|
||||||
|
await zoomByMouseWheel(page, 0, 125, false);
|
||||||
|
await assertZoomLevel(page, 90);
|
||||||
|
|
||||||
|
const zoomed = [0, 0, original[2] * 0.9, original[3] * 0.9];
|
||||||
|
await assertEdgelessSelectedModelRect(page, zoomed);
|
||||||
|
|
||||||
|
// disable edgelessScrollZoom
|
||||||
|
await page.evaluate(() => {
|
||||||
|
window.editorSetting$.value = {
|
||||||
|
...window.editorSetting$.value,
|
||||||
|
edgelessScrollZoom: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// can't zoom without ctrl pressed
|
||||||
|
await zoomByMouseWheel(page, 0, 125, false);
|
||||||
|
await assertZoomLevel(page, 90);
|
||||||
|
});
|
||||||
|
|
||||||
test('zoom by pinch', async ({ page }) => {
|
test('zoom by pinch', async ({ page }) => {
|
||||||
await enterPlaygroundRoom(page);
|
await enterPlaygroundRoom(page);
|
||||||
await initEmptyEdgelessState(page);
|
await initEmptyEdgelessState(page);
|
||||||
|
|||||||
@@ -825,11 +825,14 @@ export async function clickComponentToolbarMoreMenuButton(
|
|||||||
export async function zoomByMouseWheel(
|
export async function zoomByMouseWheel(
|
||||||
page: Page,
|
page: Page,
|
||||||
stepX: number,
|
stepX: number,
|
||||||
stepY: number
|
stepY: number,
|
||||||
|
pressedKey = true
|
||||||
) {
|
) {
|
||||||
await page.keyboard.down(SHORT_KEY);
|
if (pressedKey) await page.keyboard.down(SHORT_KEY);
|
||||||
|
|
||||||
await page.mouse.wheel(stepX, stepY);
|
await page.mouse.wheel(stepX, stepY);
|
||||||
await page.keyboard.up(SHORT_KEY);
|
|
||||||
|
if (pressedKey) await page.keyboard.up(SHORT_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
// touch screen is not supported by Playwright now
|
// touch screen is not supported by Playwright now
|
||||||
|
|||||||
+46
-24
@@ -1,10 +1,10 @@
|
|||||||
import { Menu, MenuItem, MenuTrigger } from '@affine/component';
|
import { Menu, MenuItem, MenuTrigger, Switch } from '@affine/component';
|
||||||
import { SettingRow } from '@affine/component/setting-components';
|
import { SettingRow } from '@affine/component/setting-components';
|
||||||
import { EditorSettingService } from '@affine/core/modules/editor-setting';
|
import { EditorSettingService } from '@affine/core/modules/editor-setting';
|
||||||
import type { EdgelessDefaultTheme } from '@affine/core/modules/editor-setting/schema';
|
import type { EdgelessDefaultTheme } from '@affine/core/modules/editor-setting/schema';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import { useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { menuTrigger } from '../style.css';
|
import { menuTrigger } from '../style.css';
|
||||||
|
|
||||||
@@ -78,29 +78,51 @@ export const GeneralEdgelessSetting = () => {
|
|||||||
});
|
});
|
||||||
}, [editorSetting, items, edgelessDefaultTheme]);
|
}, [editorSetting, items, edgelessDefaultTheme]);
|
||||||
|
|
||||||
|
const handleScrollZoomChange = useCallback(
|
||||||
|
(checked: boolean) => {
|
||||||
|
editorSetting.set('edgelessScrollZoom', checked);
|
||||||
|
},
|
||||||
|
[editorSetting]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingRow
|
<>
|
||||||
name={t[
|
<SettingRow
|
||||||
'com.affine.settings.editorSettings.page.edgeless-default-theme.title'
|
name={t[
|
||||||
]()}
|
'com.affine.settings.editorSettings.page.edgeless-default-theme.title'
|
||||||
desc={t[
|
]()}
|
||||||
'com.affine.settings.editorSettings.page.edgeless-default-theme.description'
|
desc={t[
|
||||||
]()}
|
'com.affine.settings.editorSettings.page.edgeless-default-theme.description'
|
||||||
>
|
]()}
|
||||||
<Menu
|
|
||||||
items={menuItems}
|
|
||||||
contentOptions={{
|
|
||||||
align: 'end',
|
|
||||||
sideOffset: 16,
|
|
||||||
style: {
|
|
||||||
width: '280px',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<MenuTrigger tooltip={currentTheme} className={menuTrigger}>
|
<Menu
|
||||||
{currentTheme}
|
items={menuItems}
|
||||||
</MenuTrigger>
|
contentOptions={{
|
||||||
</Menu>
|
align: 'end',
|
||||||
</SettingRow>
|
sideOffset: 16,
|
||||||
|
style: {
|
||||||
|
width: '280px',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuTrigger tooltip={currentTheme} className={menuTrigger}>
|
||||||
|
{currentTheme}
|
||||||
|
</MenuTrigger>
|
||||||
|
</Menu>
|
||||||
|
</SettingRow>
|
||||||
|
<SettingRow
|
||||||
|
name={t[
|
||||||
|
'com.affine.settings.editorSettings.page.edgeless-scroll-wheel-zoom.title'
|
||||||
|
]()}
|
||||||
|
desc={t[
|
||||||
|
'com.affine.settings.editorSettings.page.edgeless-scroll-wheel-zoom.description'
|
||||||
|
]()}
|
||||||
|
>
|
||||||
|
<Switch
|
||||||
|
checked={editorSetting.edgelessScrollZoom.$.value}
|
||||||
|
onChange={handleScrollZoomChange}
|
||||||
|
></Switch>
|
||||||
|
</SettingRow>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { NodePropsSchema } from '@blocksuite/affine-shared/utils';
|
import { GeneralSettingSchema } from '@blocksuite/affine-shared/services';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
export const BSEditorSettingSchema = NodePropsSchema;
|
export const BSEditorSettingSchema = GeneralSettingSchema;
|
||||||
|
|
||||||
export type FontFamily = 'Sans' | 'Serif' | 'Mono' | 'Custom';
|
export type FontFamily = 'Sans' | 'Serif' | 'Mono' | 'Custom';
|
||||||
export type EdgelessDefaultTheme = 'auto' | 'dark' | 'light' | 'specified';
|
export type EdgelessDefaultTheme = 'auto' | 'dark' | 'light' | 'specified';
|
||||||
@@ -32,5 +32,5 @@ export const EditorSettingSchema = BSEditorSettingSchema.merge(
|
|||||||
AffineEditorSettingSchema
|
AffineEditorSettingSchema
|
||||||
);
|
);
|
||||||
|
|
||||||
// eslint-disable-next-line no-redeclare
|
// oxlint-disable-next-line no-redeclare
|
||||||
export type EditorSettingSchema = z.infer<typeof EditorSettingSchema>;
|
export type EditorSettingSchema = z.infer<typeof EditorSettingSchema>;
|
||||||
|
|||||||
@@ -1236,6 +1236,8 @@
|
|||||||
"com.affine.settings.editorSettings.page.edgeless-default-theme.description": "Set edgeless default color scheme.",
|
"com.affine.settings.editorSettings.page.edgeless-default-theme.description": "Set edgeless default color scheme.",
|
||||||
"com.affine.settings.editorSettings.page.edgeless-default-theme.title": "Edgeless default theme",
|
"com.affine.settings.editorSettings.page.edgeless-default-theme.title": "Edgeless default theme",
|
||||||
"com.affine.settings.editorSettings.page.edgeless-default-theme.specified": "Specified by current color mode",
|
"com.affine.settings.editorSettings.page.edgeless-default-theme.specified": "Specified by current color mode",
|
||||||
|
"com.affine.settings.editorSettings.page.edgeless-scroll-wheel-zoom.title": "Scroll wheel zoom",
|
||||||
|
"com.affine.settings.editorSettings.page.edgeless-scroll-wheel-zoom.description": "Use the scroll wheel to zoom in and out.",
|
||||||
"com.affine.settings.editorSettings.preferences": "Preferences",
|
"com.affine.settings.editorSettings.preferences": "Preferences",
|
||||||
"com.affine.settings.editorSettings.preferences.export.description": "You can export the entire preferences data for backup, and the exported data can be re-imported.",
|
"com.affine.settings.editorSettings.preferences.export.description": "You can export the entire preferences data for backup, and the exported data can be re-imported.",
|
||||||
"com.affine.settings.editorSettings.preferences.export.title": "Export Settings",
|
"com.affine.settings.editorSettings.preferences.export.title": "Export Settings",
|
||||||
|
|||||||
@@ -1143,6 +1143,8 @@
|
|||||||
"com.affine.settings.editorSettings.page.edgeless-default-theme.description": "设置默认的无界配色方案。",
|
"com.affine.settings.editorSettings.page.edgeless-default-theme.description": "设置默认的无界配色方案。",
|
||||||
"com.affine.settings.editorSettings.page.edgeless-default-theme.title": "无界默认配色方案",
|
"com.affine.settings.editorSettings.page.edgeless-default-theme.title": "无界默认配色方案",
|
||||||
"com.affine.settings.editorSettings.page.edgeless-default-theme.specified": "由当前应用的配色方案指定",
|
"com.affine.settings.editorSettings.page.edgeless-default-theme.specified": "由当前应用的配色方案指定",
|
||||||
|
"com.affine.settings.editorSettings.page.edgeless-scroll-wheel-zoom.title": "滚轮缩放",
|
||||||
|
"com.affine.settings.editorSettings.page.edgeless-scroll-wheel-zoom.description": "使用鼠标滚轮来调整缩放比例",
|
||||||
"com.affine.settings.editorSettings.preferences": "首选项",
|
"com.affine.settings.editorSettings.preferences": "首选项",
|
||||||
"com.affine.settings.editorSettings.preferences.export.description": "您可以导出整个首选项数据进行备份,然后可以重新导入导出的数据。",
|
"com.affine.settings.editorSettings.preferences.export.description": "您可以导出整个首选项数据进行备份,然后可以重新导入导出的数据。",
|
||||||
"com.affine.settings.editorSettings.preferences.export.title": "导出设置",
|
"com.affine.settings.editorSettings.preferences.export.title": "导出设置",
|
||||||
|
|||||||
@@ -1140,6 +1140,8 @@
|
|||||||
"com.affine.settings.editorSettings.page.edgeless-default-theme.description": "設定無界的預設配色。",
|
"com.affine.settings.editorSettings.page.edgeless-default-theme.description": "設定無界的預設配色。",
|
||||||
"com.affine.settings.editorSettings.page.edgeless-default-theme.title": "無界預設主題",
|
"com.affine.settings.editorSettings.page.edgeless-default-theme.title": "無界預設主題",
|
||||||
"com.affine.settings.editorSettings.page.edgeless-default-theme.specified": "由目前顏色模式指定",
|
"com.affine.settings.editorSettings.page.edgeless-default-theme.specified": "由目前顏色模式指定",
|
||||||
|
"com.affine.settings.editorSettings.page.edgeless-scroll-wheel-zoom.title": "滾輪縮放",
|
||||||
|
"com.affine.settings.editorSettings.page.edgeless-scroll-wheel-zoom.description": "使用滑鼠滾輪來調整縮放比例",
|
||||||
"com.affine.settings.editorSettings.preferences": "首選項",
|
"com.affine.settings.editorSettings.preferences": "首選項",
|
||||||
"com.affine.settings.editorSettings.preferences.export.description": "您可以匯出整個首選項數據進行備份,導出的數據亦可重新匯入。",
|
"com.affine.settings.editorSettings.preferences.export.description": "您可以匯出整個首選項數據進行備份,導出的數據亦可重新匯入。",
|
||||||
"com.affine.settings.editorSettings.preferences.export.title": "匯出設定",
|
"com.affine.settings.editorSettings.preferences.export.title": "匯出設定",
|
||||||
|
|||||||
Reference in New Issue
Block a user