mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 21:27:20 +00:00
fix: theme not being persisted issue (#2283)
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { BrowserWindow, nativeTheme } from 'electron';
|
import { app, BrowserWindow, nativeTheme } from 'electron';
|
||||||
|
|
||||||
import { isMacOS } from '../../../../utils';
|
import { isMacOS } from '../../../../utils';
|
||||||
import type { NamespaceHandlers } from '../type';
|
import type { NamespaceHandlers } from '../type';
|
||||||
@@ -17,6 +17,25 @@ export const uiHandlers = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
handleMinimizeApp: async () => {
|
||||||
|
const windows = BrowserWindow.getAllWindows();
|
||||||
|
windows.forEach(w => {
|
||||||
|
w.minimize();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleMaximizeApp: async () => {
|
||||||
|
const windows = BrowserWindow.getAllWindows();
|
||||||
|
windows.forEach(w => {
|
||||||
|
if (w.isMaximized()) {
|
||||||
|
w.unmaximize();
|
||||||
|
} else {
|
||||||
|
w.maximize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleCloseApp: async () => {
|
||||||
|
app.quit();
|
||||||
|
},
|
||||||
getGoogleOauthCode: async () => {
|
getGoogleOauthCode: async () => {
|
||||||
return getGoogleOauthCode();
|
return getGoogleOauthCode();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { BrowserWindow, nativeTheme } from 'electron';
|
|||||||
import electronWindowState from 'electron-window-state';
|
import electronWindowState from 'electron-window-state';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
import { isMacOS } from '../../utils';
|
import { isMacOS, isWindows } from '../../utils';
|
||||||
import { logger } from './logger';
|
import { logger } from './logger';
|
||||||
|
|
||||||
const IS_DEV: boolean =
|
const IS_DEV: boolean =
|
||||||
@@ -18,7 +18,11 @@ async function createWindow() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const browserWindow = new BrowserWindow({
|
const browserWindow = new BrowserWindow({
|
||||||
titleBarStyle: isMacOS() ? 'hiddenInset' : 'default',
|
titleBarStyle: isMacOS()
|
||||||
|
? 'hiddenInset'
|
||||||
|
: isWindows()
|
||||||
|
? 'hidden'
|
||||||
|
: 'default',
|
||||||
trafficLightPosition: { x: 24, y: 18 },
|
trafficLightPosition: { x: 24, y: 18 },
|
||||||
x: mainWindowState.x,
|
x: mainWindowState.x,
|
||||||
y: mainWindowState.y,
|
y: mainWindowState.y,
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
export const isMacOS = () => {
|
export const isMacOS = () => {
|
||||||
return process.platform === 'darwin';
|
return process.platform === 'darwin';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isWindows = () => {
|
||||||
|
return process.platform === 'win32';
|
||||||
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const mainDistDir = path.resolve(__dirname, '../dist/layers/main');
|
|||||||
|
|
||||||
// be careful and avoid any side effects in
|
// be careful and avoid any side effects in
|
||||||
const { handlers, events } = await import(
|
const { handlers, events } = await import(
|
||||||
path.resolve(mainDistDir, 'exposed.js')
|
'file://' + path.resolve(mainDistDir, 'exposed.js')
|
||||||
);
|
);
|
||||||
|
|
||||||
const handlersMeta = Object.entries(handlers).map(
|
const handlersMeta = Object.entries(handlers).map(
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ test('new page', async ({ page, workspace }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('app theme', async ({ page, electronApp }) => {
|
test('app theme', async ({ page, electronApp }) => {
|
||||||
await page.waitForSelector('v-line');
|
|
||||||
const root = page.locator('html');
|
const root = page.locator('html');
|
||||||
{
|
{
|
||||||
const themeMode = await root.evaluate(element =>
|
const themeMode = await root.evaluate(element =>
|
||||||
@@ -20,30 +19,25 @@ test('app theme', async ({ page, electronApp }) => {
|
|||||||
);
|
);
|
||||||
expect(themeMode).toBe('light');
|
expect(themeMode).toBe('light');
|
||||||
|
|
||||||
// check if electron theme source is set to light
|
const theme = await electronApp.evaluate(({ nativeTheme }) => {
|
||||||
const themeSource = await electronApp.evaluate(({ nativeTheme }) => {
|
return nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
|
||||||
return nativeTheme.themeSource;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(themeSource).toBe('light');
|
expect(theme).toBe('light');
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
await page.getByTestId('editor-option-menu').click();
|
await page.getByTestId('editor-option-menu').click();
|
||||||
await page.getByTestId('change-theme-dark').click();
|
await page.getByTestId('change-theme-dark').click();
|
||||||
await page.waitForTimeout(50);
|
await page.waitForTimeout(50);
|
||||||
{
|
const themeMode = await root.evaluate(element =>
|
||||||
const themeMode = await root.evaluate(element =>
|
element.getAttribute('data-theme')
|
||||||
element.getAttribute('data-theme')
|
);
|
||||||
);
|
expect(themeMode).toBe('dark');
|
||||||
expect(themeMode).toBe('dark');
|
const theme = await electronApp.evaluate(({ nativeTheme }) => {
|
||||||
}
|
return nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
|
||||||
|
|
||||||
const themeSource = await electronApp.evaluate(({ nativeTheme }) => {
|
|
||||||
return nativeTheme.themeSource;
|
|
||||||
});
|
});
|
||||||
|
expect(theme).toBe('dark');
|
||||||
expect(themeSource).toBe('dark');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ export const test = base.extend<{
|
|||||||
const logFilePath = await page.evaluate(async () => {
|
const logFilePath = await page.evaluate(async () => {
|
||||||
return window.apis?.debug.logFilePath();
|
return window.apis?.debug.logFilePath();
|
||||||
});
|
});
|
||||||
|
// wat for blocksuite to be loaded
|
||||||
|
await page.waitForSelector('v-line');
|
||||||
await use(page);
|
await use(page);
|
||||||
await page.close();
|
await page.close();
|
||||||
if (logFilePath) {
|
if (logFilePath) {
|
||||||
@@ -57,11 +59,12 @@ export const test = base.extend<{
|
|||||||
executablePath: resolve(__dirname, '../node_modules/.bin/electron'),
|
executablePath: resolve(__dirname, '../node_modules/.bin/electron'),
|
||||||
colorScheme: 'light',
|
colorScheme: 'light',
|
||||||
});
|
});
|
||||||
const sessionDataPath = await electronApp.evaluate(async ({ app }) => {
|
|
||||||
return app.getPath('sessionData');
|
|
||||||
});
|
|
||||||
await use(electronApp);
|
await use(electronApp);
|
||||||
await fs.rm(sessionDataPath, { recursive: true, force: true });
|
// FIXME: the following does not work well on CI
|
||||||
|
// const sessionDataPath = await electronApp.evaluate(async ({ app }) => {
|
||||||
|
// return app.getPath('sessionData');
|
||||||
|
// });
|
||||||
|
// await fs.rm(sessionDataPath, { recursive: true, force: true });
|
||||||
},
|
},
|
||||||
appInfo: async ({ electronApp }, use) => {
|
appInfo: async ({ electronApp }, use) => {
|
||||||
const appInfo = await electronApp.evaluate(async ({ app }) => {
|
const appInfo = await electronApp.evaluate(async ({ app }) => {
|
||||||
|
|||||||
@@ -110,7 +110,8 @@ export const settingItemLabelHint = style({
|
|||||||
export const row = style({
|
export const row = style({
|
||||||
padding: '40px 0',
|
padding: '40px 0',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: '60px',
|
columnGap: '60px',
|
||||||
|
rowGap: '12px',
|
||||||
selectors: {
|
selectors: {
|
||||||
'&': {
|
'&': {
|
||||||
borderBottom: '1px solid var(--affine-border-color)',
|
borderBottom: '1px solid var(--affine-border-color)',
|
||||||
@@ -119,22 +120,22 @@ export const row = style({
|
|||||||
paddingTop: 0,
|
paddingTop: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
flexWrap: 'wrap',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const col = style({
|
export const col = style({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
flexShrink: 0,
|
|
||||||
selectors: {
|
selectors: {
|
||||||
[`${row} &:nth-child(1)`]: {
|
[`${row} &:nth-child(1)`]: {
|
||||||
flex: 3,
|
flex: '3 0 200px',
|
||||||
},
|
},
|
||||||
[`${row} &:nth-child(2)`]: {
|
[`${row} &:nth-child(2)`]: {
|
||||||
flex: 5,
|
flex: '5 0 240px',
|
||||||
},
|
},
|
||||||
[`${row} &:nth-child(3)`]: {
|
[`${row} &:nth-child(3)`]: {
|
||||||
flex: 2,
|
flex: '2 0 200px',
|
||||||
alignItems: 'flex-end',
|
alignItems: 'flex-end',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -88,7 +88,6 @@ export const GeneralPanel: React.FC<PanelProps> = ({
|
|||||||
|
|
||||||
<div className={style.col}>
|
<div className={style.col}>
|
||||||
<StyledInput
|
<StyledInput
|
||||||
width={284}
|
|
||||||
height={38}
|
height={38}
|
||||||
value={input}
|
value={input}
|
||||||
data-testid="workspace-name-input"
|
data-testid="workspace-name-input"
|
||||||
|
|||||||
@@ -1,13 +1,7 @@
|
|||||||
import { displayFlex, styled } from '@affine/component';
|
import { displayFlex, styled } from '@affine/component';
|
||||||
import { Input } from '@affine/component';
|
import { Input } from '@affine/component';
|
||||||
|
|
||||||
export const StyledInput = styled(Input)(() => {
|
export const StyledInput = Input;
|
||||||
return {
|
|
||||||
border: '1px solid var(--affine-border-color)',
|
|
||||||
borderRadius: '8px',
|
|
||||||
fontSize: 'var(--affine-font-sm)',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export const StyledWorkspaceInfo = styled('div')(() => {
|
export const StyledWorkspaceInfo = styled('div')(() => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -24,10 +24,7 @@ import { useCurrentPageId } from '../../../../hooks/current/use-current-page-id'
|
|||||||
import { useCurrentWorkspace } from '../../../../hooks/current/use-current-workspace';
|
import { useCurrentWorkspace } from '../../../../hooks/current/use-current-workspace';
|
||||||
import { toast } from '../../../../utils';
|
import { toast } from '../../../../utils';
|
||||||
import { MenuThemeModeSwitch } from '../header-right-items/theme-mode-switch';
|
import { MenuThemeModeSwitch } from '../header-right-items/theme-mode-switch';
|
||||||
import {
|
import * as styles from '../styles.css';
|
||||||
StyledHorizontalDivider,
|
|
||||||
StyledHorizontalDividerContainer,
|
|
||||||
} from '../styles';
|
|
||||||
import { LanguageMenu } from './language-menu';
|
import { LanguageMenu } from './language-menu';
|
||||||
const CommonMenu = () => {
|
const CommonMenu = () => {
|
||||||
const content = (
|
const content = (
|
||||||
@@ -120,9 +117,9 @@ const PageMenu = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<StyledHorizontalDividerContainer>
|
<div className={styles.horizontalDividerContainer}>
|
||||||
<StyledHorizontalDivider />
|
<div className={styles.horizontalDivider} />
|
||||||
</StyledHorizontalDividerContainer>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { BrowserWarning } from '@affine/component/affine-banner';
|
|||||||
import { appSidebarOpenAtom } from '@affine/component/app-sidebar';
|
import { appSidebarOpenAtom } from '@affine/component/app-sidebar';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { WorkspaceFlavour } from '@affine/workspace/type';
|
import { WorkspaceFlavour } from '@affine/workspace/type';
|
||||||
|
import { CloseIcon, MinusIcon, RoundedRectangleIcon } from '@blocksuite/icons';
|
||||||
import type { Page } from '@blocksuite/store';
|
import type { Page } from '@blocksuite/store';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import type { FC, HTMLAttributes, PropsWithChildren } from 'react';
|
import type { FC, HTMLAttributes, PropsWithChildren } from 'react';
|
||||||
@@ -24,11 +25,7 @@ import { HeaderShareMenu } from './header-right-items/share-menu';
|
|||||||
import SyncUser from './header-right-items/sync-user';
|
import SyncUser from './header-right-items/sync-user';
|
||||||
import TrashButtonGroup from './header-right-items/trash-button-group';
|
import TrashButtonGroup from './header-right-items/trash-button-group';
|
||||||
import UserAvatar from './header-right-items/user-avatar';
|
import UserAvatar from './header-right-items/user-avatar';
|
||||||
import {
|
import * as styles from './styles.css';
|
||||||
StyledHeader,
|
|
||||||
StyledHeaderContainer,
|
|
||||||
StyledHeaderRightSide,
|
|
||||||
} from './styles';
|
|
||||||
import { OSWarningMessage, shouldShowWarning } from './utils';
|
import { OSWarningMessage, shouldShowWarning } from './utils';
|
||||||
|
|
||||||
const SidebarSwitch = lazy(() =>
|
const SidebarSwitch = lazy(() =>
|
||||||
@@ -53,6 +50,9 @@ export const enum HeaderRightItemName {
|
|||||||
ShareMenu = 'shareMenu',
|
ShareMenu = 'shareMenu',
|
||||||
EditPage = 'editPage',
|
EditPage = 'editPage',
|
||||||
UserAvatar = 'userAvatar',
|
UserAvatar = 'userAvatar',
|
||||||
|
|
||||||
|
// some windows only items
|
||||||
|
WindowsAppControls = 'windowsAppControls',
|
||||||
}
|
}
|
||||||
|
|
||||||
type HeaderItem = {
|
type HeaderItem = {
|
||||||
@@ -67,6 +67,7 @@ type HeaderItem = {
|
|||||||
}
|
}
|
||||||
) => boolean;
|
) => boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const HeaderRightItems: Record<HeaderRightItemName, HeaderItem> = {
|
const HeaderRightItems: Record<HeaderRightItemName, HeaderItem> = {
|
||||||
[HeaderRightItemName.TrashButtonGroup]: {
|
[HeaderRightItemName.TrashButtonGroup]: {
|
||||||
Component: TrashButtonGroup,
|
Component: TrashButtonGroup,
|
||||||
@@ -104,6 +105,44 @@ const HeaderRightItems: Record<HeaderRightItemName, HeaderItem> = {
|
|||||||
return !isPublic && !isPreview;
|
return !isPublic && !isPreview;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
[HeaderRightItemName.WindowsAppControls]: {
|
||||||
|
Component: () => {
|
||||||
|
return (
|
||||||
|
<div className={styles.windowAppControlsWrapper}>
|
||||||
|
<button
|
||||||
|
data-type="minimize"
|
||||||
|
className={styles.windowAppControl}
|
||||||
|
onClick={() => {
|
||||||
|
window.apis?.ui.handleMinimizeApp();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MinusIcon />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
data-type="maximize"
|
||||||
|
className={styles.windowAppControl}
|
||||||
|
onClick={() => {
|
||||||
|
window.apis?.ui.handleMaximizeApp();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RoundedRectangleIcon />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
data-type="close"
|
||||||
|
className={styles.windowAppControl}
|
||||||
|
onClick={() => {
|
||||||
|
window.apis?.ui.handleCloseApp();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
availableWhen: () => {
|
||||||
|
return environment.isDesktop && environment.isWindows;
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HeaderProps = BaseHeaderProps;
|
export type HeaderProps = BaseHeaderProps;
|
||||||
@@ -127,9 +166,10 @@ export const Header = forwardRef<
|
|||||||
|
|
||||||
const mode = useCurrentMode();
|
const mode = useCurrentMode();
|
||||||
return (
|
return (
|
||||||
<StyledHeaderContainer
|
<div
|
||||||
|
className={styles.headerContainer}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
hasWarning={showWarning}
|
data-has-warning={showWarning}
|
||||||
data-open={open}
|
data-open={open}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
@@ -145,11 +185,12 @@ export const Header = forwardRef<
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<StyledHeader
|
<div
|
||||||
hasWarning={showWarning}
|
className={styles.header}
|
||||||
|
data-has-warning={showWarning}
|
||||||
data-testid="editor-header-items"
|
data-testid="editor-header-items"
|
||||||
data-tauri-drag-region
|
data-tauri-drag-region
|
||||||
isEdgeless={mode === 'edgeless'}
|
data-is-edgeless={mode === 'edgeless'}
|
||||||
>
|
>
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<SidebarSwitch
|
<SidebarSwitch
|
||||||
@@ -160,7 +201,7 @@ export const Header = forwardRef<
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
|
|
||||||
{props.children}
|
{props.children}
|
||||||
<StyledHeaderRightSide>
|
<div className={styles.headerRightSide}>
|
||||||
{useMemo(() => {
|
{useMemo(() => {
|
||||||
return Object.entries(HeaderRightItems).map(
|
return Object.entries(HeaderRightItems).map(
|
||||||
([name, { availableWhen, Component }]) => {
|
([name, { availableWhen, Component }]) => {
|
||||||
@@ -184,10 +225,9 @@ export const Header = forwardRef<
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, [props])}
|
}, [props])}
|
||||||
{/*<ShareMenu />*/}
|
</div>
|
||||||
</StyledHeaderRightSide>
|
</div>
|
||||||
</StyledHeader>
|
</div>
|
||||||
</StyledHeaderContainer>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -15,15 +15,7 @@ import { QuickSearchButton } from '../../pure/quick-search-button';
|
|||||||
import { EditorModeSwitch } from './editor-mode-switch';
|
import { EditorModeSwitch } from './editor-mode-switch';
|
||||||
import type { BaseHeaderProps } from './header';
|
import type { BaseHeaderProps } from './header';
|
||||||
import { Header } from './header';
|
import { Header } from './header';
|
||||||
import {
|
import * as styles from './styles.css';
|
||||||
StyledQuickSearchTipButton,
|
|
||||||
StyledQuickSearchTipContent,
|
|
||||||
StyledSearchArrowWrapper,
|
|
||||||
StyledSwitchWrapper,
|
|
||||||
StyledTitle,
|
|
||||||
StyledTitleContainer,
|
|
||||||
StyledTitleWrapper,
|
|
||||||
} from './styles';
|
|
||||||
|
|
||||||
export type WorkspaceHeaderProps = BaseHeaderProps;
|
export type WorkspaceHeaderProps = BaseHeaderProps;
|
||||||
|
|
||||||
@@ -61,7 +53,7 @@ export const WorkspaceHeader = forwardRef<
|
|||||||
);
|
);
|
||||||
|
|
||||||
const TipsContent = (
|
const TipsContent = (
|
||||||
<StyledQuickSearchTipContent>
|
<div className={styles.quickSearchTipContent}>
|
||||||
<div>
|
<div>
|
||||||
Click button
|
Click button
|
||||||
{
|
{
|
||||||
@@ -78,22 +70,23 @@ export const WorkspaceHeader = forwardRef<
|
|||||||
{isMac() ? ' ⌘ + K' : ' Ctrl + K'} to activate Quick Search. Then you
|
{isMac() ? ' ⌘ + K' : ' Ctrl + K'} to activate Quick Search. Then you
|
||||||
can search keywords or quickly open recently viewed pages.
|
can search keywords or quickly open recently viewed pages.
|
||||||
</div>
|
</div>
|
||||||
<StyledQuickSearchTipButton
|
<div
|
||||||
|
className={styles.quickSearchTipButton}
|
||||||
data-testid="quick-search-got-it"
|
data-testid="quick-search-got-it"
|
||||||
onClick={() => setShowQuickSearchTips(false)}
|
onClick={() => setShowQuickSearchTips(false)}
|
||||||
>
|
>
|
||||||
Got it
|
Got it
|
||||||
</StyledQuickSearchTipButton>
|
</div>
|
||||||
</StyledQuickSearchTipContent>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header ref={ref} {...props}>
|
<Header ref={ref} {...props}>
|
||||||
{children}
|
{children}
|
||||||
{!isPublic && currentPage && (
|
{!isPublic && currentPage && (
|
||||||
<StyledTitleContainer data-tauri-drag-region>
|
<div className={styles.titleContainer}>
|
||||||
<StyledTitleWrapper>
|
<div className={styles.titleWrapper}>
|
||||||
<StyledSwitchWrapper>
|
<div className={styles.switchWrapper}>
|
||||||
<EditorModeSwitch
|
<EditorModeSwitch
|
||||||
blockSuiteWorkspace={workspace.blockSuiteWorkspace}
|
blockSuiteWorkspace={workspace.blockSuiteWorkspace}
|
||||||
pageId={currentPage.id}
|
pageId={currentPage.id}
|
||||||
@@ -101,8 +94,8 @@ export const WorkspaceHeader = forwardRef<
|
|||||||
marginRight: '12px',
|
marginRight: '12px',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</StyledSwitchWrapper>
|
</div>
|
||||||
<StyledTitle>{title || 'Untitled'}</StyledTitle>
|
<div className={styles.title}>{title || 'Untitled'}</div>
|
||||||
<QuickSearchTips
|
<QuickSearchTips
|
||||||
data-testid="quick-search-tips"
|
data-testid="quick-search-tips"
|
||||||
content={TipsContent}
|
content={TipsContent}
|
||||||
@@ -111,16 +104,16 @@ export const WorkspaceHeader = forwardRef<
|
|||||||
open={showQuickSearchTips}
|
open={showQuickSearchTips}
|
||||||
offset={[0, -5]}
|
offset={[0, -5]}
|
||||||
>
|
>
|
||||||
<StyledSearchArrowWrapper>
|
<div className={styles.searchArrowWrapper}>
|
||||||
<QuickSearchButton
|
<QuickSearchButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setOpenQuickSearch(true);
|
setOpenQuickSearch(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</StyledSearchArrowWrapper>
|
</div>
|
||||||
</QuickSearchTips>
|
</QuickSearchTips>
|
||||||
</StyledTitleWrapper>
|
</div>
|
||||||
</StyledTitleContainer>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Header>
|
</Header>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,216 @@
|
|||||||
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
export const headerContainer = style({
|
||||||
|
height: '52px',
|
||||||
|
flexShrink: 0,
|
||||||
|
position: 'sticky',
|
||||||
|
top: 0,
|
||||||
|
background: 'var(--affine-background-primary-color)',
|
||||||
|
WebkitAppRegion: 'drag',
|
||||||
|
zIndex: 'var(--affine-z-index-popover)',
|
||||||
|
'@media': {
|
||||||
|
'(max-width: 768px)': {
|
||||||
|
selectors: {
|
||||||
|
'&[data-open="true"]': {
|
||||||
|
// @ts-ignore
|
||||||
|
WebkitAppRegion: 'no-drag',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
selectors: {
|
||||||
|
'&[data-has-warning="true"]': {
|
||||||
|
height: '96px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const header = style({
|
||||||
|
flexShrink: 0,
|
||||||
|
height: '52px',
|
||||||
|
width: '100%',
|
||||||
|
padding: '0 20px',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
background: 'var(--affine-background-primary-color)',
|
||||||
|
zIndex: 99,
|
||||||
|
position: 'relative',
|
||||||
|
selectors: {
|
||||||
|
'&[data-is-edgeless="true"]': {
|
||||||
|
borderBottom: `1px solid var(--affine-border-color)`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const titleContainer = style({
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
margin: 'auto',
|
||||||
|
position: 'absolute',
|
||||||
|
inset: 'auto auto auto 50%',
|
||||||
|
transform: 'translate(-50%, 0px)',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
alignContent: 'unset',
|
||||||
|
fontSize: 'var(--affine-font-base)',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const title = style({
|
||||||
|
maxWidth: '620px',
|
||||||
|
transition: 'max-width .15s',
|
||||||
|
userSelect: 'none',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
'@media': {
|
||||||
|
'(max-width: 768px)': {
|
||||||
|
selectors: {
|
||||||
|
'&[data-open="true"]': {
|
||||||
|
// @ts-ignore
|
||||||
|
WebkitAppRegion: 'no-drag',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const titleWrapper = style({
|
||||||
|
height: '100%',
|
||||||
|
position: 'relative',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const headerRightSide = style({
|
||||||
|
height: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '12px',
|
||||||
|
zIndex: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const browserWarning = style({
|
||||||
|
backgroundColor: 'var(--affine-background-warning-color)',
|
||||||
|
color: 'var(--affine-warning-color)',
|
||||||
|
height: '36px',
|
||||||
|
fontSize: 'var(--affine-font-sm)',
|
||||||
|
display: 'none',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
selectors: {
|
||||||
|
'&[data-show="true"]': {
|
||||||
|
display: 'flex',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const closeButton = style({
|
||||||
|
width: '36px',
|
||||||
|
height: '36px',
|
||||||
|
color: 'var(--affine-icon-color)',
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
position: 'absolute',
|
||||||
|
right: '15px',
|
||||||
|
top: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const switchWrapper = style({
|
||||||
|
position: 'absolute',
|
||||||
|
right: '100%',
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
margin: 'auto',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const searchArrowWrapper = style({
|
||||||
|
position: 'absolute',
|
||||||
|
left: 'calc(100% + 4px)',
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
margin: 'auto',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const pageListTitleWrapper = style({
|
||||||
|
fontSize: 'var(--affine-font-base)',
|
||||||
|
color: 'var(--affine-text-primary-color)',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const pageListTitleIcon = style({
|
||||||
|
fontSize: '20px',
|
||||||
|
height: '1em',
|
||||||
|
marginRight: '12px',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const quickSearchTipButton = style({
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginTop: '12px',
|
||||||
|
color: '#FFFFFF',
|
||||||
|
width: '48px',
|
||||||
|
height: ' 26px',
|
||||||
|
fontSize: 'var(--affine-font-sm)',
|
||||||
|
lineHeight: '22px',
|
||||||
|
background: 'var(--affine-primary-color)',
|
||||||
|
borderRadius: '8px',
|
||||||
|
textAlign: 'center',
|
||||||
|
cursor: 'pointer',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const quickSearchTipContent = style({
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'flex-end',
|
||||||
|
flexDirection: 'column',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const horizontalDivider = style({
|
||||||
|
width: '100%',
|
||||||
|
borderTop: `1px solid var(--affine-border-color)`,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const horizontalDividerContainer = style({
|
||||||
|
width: '100%',
|
||||||
|
padding: '14px',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const windowAppControlsWrapper = style({
|
||||||
|
display: 'flex',
|
||||||
|
gap: '2px',
|
||||||
|
transform: 'translateX(8px)',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const windowAppControl = style({
|
||||||
|
// @ts-ignore
|
||||||
|
WebkitAppRegion: 'no-drag',
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'inline-flex',
|
||||||
|
width: '32px',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
borderRadius: '2px',
|
||||||
|
selectors: {
|
||||||
|
'&[data-type="close"]:hover': {
|
||||||
|
background: 'var(--affine-error-color)',
|
||||||
|
color: '#FFFFFF',
|
||||||
|
},
|
||||||
|
'&:hover': {
|
||||||
|
background: 'var(--affine-background-tertiary-color)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,186 +0,0 @@
|
|||||||
import {
|
|
||||||
absoluteCenter,
|
|
||||||
displayFlex,
|
|
||||||
styled,
|
|
||||||
textEllipsis,
|
|
||||||
} from '@affine/component';
|
|
||||||
|
|
||||||
export const StyledHeaderContainer = styled('div')<{
|
|
||||||
hasWarning: boolean;
|
|
||||||
}>(({ hasWarning }) => {
|
|
||||||
return {
|
|
||||||
height: hasWarning ? '96px' : '52px',
|
|
||||||
flexShrink: 0,
|
|
||||||
position: 'sticky',
|
|
||||||
top: 0,
|
|
||||||
background: 'var(--affine-background-primary-color)',
|
|
||||||
WebkitAppRegion: 'drag',
|
|
||||||
zIndex: 'var(--affine-z-index-popover)',
|
|
||||||
'@media (max-width: 768px)': {
|
|
||||||
'&[data-open="true"]': {
|
|
||||||
WebkitAppRegion: 'no-drag',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
export const StyledHeader = styled('div')<{
|
|
||||||
hasWarning: boolean;
|
|
||||||
isEdgeless: boolean;
|
|
||||||
}>(({ isEdgeless }) => {
|
|
||||||
return {
|
|
||||||
flexShrink: 0,
|
|
||||||
height: '52px',
|
|
||||||
width: '100%',
|
|
||||||
padding: '0 20px',
|
|
||||||
...displayFlex('space-between', 'center'),
|
|
||||||
background: 'var(--affine-background-primary-color)',
|
|
||||||
zIndex: 99,
|
|
||||||
position: 'relative',
|
|
||||||
borderBottom: isEdgeless ? '1px solid var(--affine-border-color)' : 'none',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export const StyledTitleContainer = styled('div')(() => ({
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
|
|
||||||
margin: 'auto',
|
|
||||||
...absoluteCenter({ horizontal: true, position: { top: 0 } }),
|
|
||||||
...displayFlex('center', 'center'),
|
|
||||||
fontSize: 'var(--affine-font-base)',
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const StyledTitle = styled('div')(({ theme }) => {
|
|
||||||
return {
|
|
||||||
maxWidth: '620px',
|
|
||||||
[theme.breakpoints.down('lg')]: {
|
|
||||||
maxWidth: '480px',
|
|
||||||
},
|
|
||||||
[theme.breakpoints.down('md')]: {
|
|
||||||
maxWidth: '240px',
|
|
||||||
},
|
|
||||||
[theme.breakpoints.down('sm')]: {
|
|
||||||
maxWidth: '180px',
|
|
||||||
},
|
|
||||||
transition: 'max-width .15s',
|
|
||||||
userSelect: 'none',
|
|
||||||
...textEllipsis(1),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export const StyledTitleWrapper = styled('div')({
|
|
||||||
height: '100%',
|
|
||||||
position: 'relative',
|
|
||||||
...displayFlex('center', 'center'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const StyledHeaderRightSide = styled('div')({
|
|
||||||
height: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
'>*:not(:last-child)': {
|
|
||||||
marginRight: '12px',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const StyledBrowserWarning = styled('div')<{ show: boolean }>(
|
|
||||||
({ show }) => {
|
|
||||||
return {
|
|
||||||
backgroundColor: 'var(--affine-background-warning-color)',
|
|
||||||
color: 'var(--affine-background-warning-color)',
|
|
||||||
height: '36px',
|
|
||||||
fontSize: 'var(--affine-font-sm)',
|
|
||||||
display: show ? 'flex' : 'none',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const StyledCloseButton = styled('div')(() => {
|
|
||||||
return {
|
|
||||||
width: '36px',
|
|
||||||
height: '36px',
|
|
||||||
color: 'var(--affine-icon-color)',
|
|
||||||
cursor: 'pointer',
|
|
||||||
...displayFlex('center', 'center'),
|
|
||||||
position: 'absolute',
|
|
||||||
right: '15px',
|
|
||||||
top: '0',
|
|
||||||
|
|
||||||
svg: {
|
|
||||||
width: '15px',
|
|
||||||
height: '15px',
|
|
||||||
position: 'relative',
|
|
||||||
zIndex: 1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export const StyledSwitchWrapper = styled('div')(() => {
|
|
||||||
return {
|
|
||||||
position: 'absolute',
|
|
||||||
right: '100%',
|
|
||||||
top: 0,
|
|
||||||
bottom: 0,
|
|
||||||
margin: 'auto',
|
|
||||||
...displayFlex('center', 'center'),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export const StyledSearchArrowWrapper = styled('div')(() => {
|
|
||||||
return {
|
|
||||||
position: 'absolute',
|
|
||||||
left: 'calc(100% + 4px)',
|
|
||||||
top: 0,
|
|
||||||
bottom: 0,
|
|
||||||
margin: 'auto',
|
|
||||||
...displayFlex('center', 'center'),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export const StyledPageListTittleWrapper = styled(StyledTitle)(() => {
|
|
||||||
return {
|
|
||||||
fontSize: 'var(--affine-font-base)',
|
|
||||||
color: 'var(--affine-text-primary-color)',
|
|
||||||
...displayFlex('center', 'center'),
|
|
||||||
'>svg': {
|
|
||||||
fontSize: '20px',
|
|
||||||
marginRight: '12px',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
export const StyledQuickSearchTipButton = styled('div')(() => {
|
|
||||||
return {
|
|
||||||
...displayFlex('center', 'center'),
|
|
||||||
marginTop: '12px',
|
|
||||||
color: '#FFFFFF',
|
|
||||||
width: '48px',
|
|
||||||
height: ' 26px',
|
|
||||||
fontSize: 'var(--affine-font-sm)',
|
|
||||||
lineHeight: '22px',
|
|
||||||
background: 'var(--affine-primary-color)',
|
|
||||||
borderRadius: '8px',
|
|
||||||
textAlign: 'center',
|
|
||||||
cursor: 'pointer',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
export const StyledQuickSearchTipContent = styled('div')(() => {
|
|
||||||
return {
|
|
||||||
...displayFlex('center', 'flex-end'),
|
|
||||||
flexDirection: 'column',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export const StyledHorizontalDivider = styled('div')(() => {
|
|
||||||
return {
|
|
||||||
width: '100%',
|
|
||||||
borderTop: `1px solid var(--affine-border-color)`,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
export const StyledHorizontalDividerContainer = styled('div')(() => {
|
|
||||||
return {
|
|
||||||
width: '100%',
|
|
||||||
padding: '14px',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -5,7 +5,7 @@ import type React from 'react';
|
|||||||
import { openQuickSearchModalAtom } from '../../../atoms';
|
import { openQuickSearchModalAtom } from '../../../atoms';
|
||||||
import type { HeaderProps } from '../../blocksuite/workspace-header/header';
|
import type { HeaderProps } from '../../blocksuite/workspace-header/header';
|
||||||
import { Header } from '../../blocksuite/workspace-header/header';
|
import { Header } from '../../blocksuite/workspace-header/header';
|
||||||
import { StyledPageListTittleWrapper } from '../../blocksuite/workspace-header/styles';
|
import * as styles from '../../blocksuite/workspace-header/styles.css';
|
||||||
import { QuickSearchButton } from '../quick-search-button';
|
import { QuickSearchButton } from '../quick-search-button';
|
||||||
|
|
||||||
export type WorkspaceTitleProps = React.PropsWithChildren<
|
export type WorkspaceTitleProps = React.PropsWithChildren<
|
||||||
@@ -22,15 +22,15 @@ export const WorkspaceTitle: React.FC<WorkspaceTitleProps> = ({
|
|||||||
const setOpenQuickSearch = useSetAtom(openQuickSearchModalAtom);
|
const setOpenQuickSearch = useSetAtom(openQuickSearchModalAtom);
|
||||||
return (
|
return (
|
||||||
<Header {...props}>
|
<Header {...props}>
|
||||||
<StyledPageListTittleWrapper>
|
<div className={styles.pageListTitleWrapper}>
|
||||||
{icon}
|
<div className={styles.pageListTitleIcon}>{icon}</div>
|
||||||
{children}
|
{children}
|
||||||
<QuickSearchButton
|
<QuickSearchButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setOpenQuickSearch(true);
|
setOpenQuickSearch(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</StyledPageListTittleWrapper>
|
</div>
|
||||||
</Header>
|
</Header>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,19 +1,22 @@
|
|||||||
import type { ThemeProviderProps } from '@affine/component';
|
import type { ThemeProviderProps } from '@affine/component';
|
||||||
import { ThemeProvider as NextThemeProvider, useTheme } from 'next-themes';
|
import { ThemeProvider as NextThemeProvider, useTheme } from 'next-themes';
|
||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
import type React from 'react';
|
|
||||||
import { memo, useRef } from 'react';
|
import { memo, useRef } from 'react';
|
||||||
|
|
||||||
const themes = ['dark', 'light'];
|
const themes = ['dark', 'light'];
|
||||||
|
|
||||||
|
// a workaround to sync theme to electron
|
||||||
|
let firstRender = true;
|
||||||
|
|
||||||
const DesktopThemeSync = memo(function DesktopThemeSync() {
|
const DesktopThemeSync = memo(function DesktopThemeSync() {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const lastThemeRef = useRef(theme);
|
const lastThemeRef = useRef(theme);
|
||||||
if (lastThemeRef.current !== theme) {
|
if (lastThemeRef.current !== theme || firstRender) {
|
||||||
if (environment.isDesktop && theme) {
|
if (environment.isDesktop && theme) {
|
||||||
window.apis?.ui.handleThemeChange(theme as 'dark' | 'light' | 'system');
|
window.apis?.ui.handleThemeChange(theme as 'dark' | 'light' | 'system');
|
||||||
}
|
}
|
||||||
lastThemeRef.current = theme;
|
lastThemeRef.current = theme;
|
||||||
|
firstRender = false;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
"AFFiNE Cloud": "AFFiNE Cloud",
|
"AFFiNE Cloud": "AFFiNE Cloud",
|
||||||
"It takes up more space on your device": "Es verbraucht mehr Speicherplatz auf deinem Gerät.",
|
"It takes up more space on your device": "Es verbraucht mehr Speicherplatz auf deinem Gerät.",
|
||||||
"Sign out description": "Nach dem Abmelden gehen alle nicht synchronisierten Inhalte verloren.",
|
"Sign out description": "Nach dem Abmelden gehen alle nicht synchronisierten Inhalte verloren.",
|
||||||
"Export AFFiNE backup file": "AFFiNE-Backup als Datei exportieren (bald verfügbar)",
|
"Export AFFiNE backup file": "AFFiNE-Backup als Datei exportieren",
|
||||||
"Members": "Mitglieder",
|
"Members": "Mitglieder",
|
||||||
"Saved then enable AFFiNE Cloud": "Alle Änderungen werden lokal gespeichert. Klicke hier, um AFFiNE Cloud zu aktivieren.",
|
"Saved then enable AFFiNE Cloud": "Alle Änderungen werden lokal gespeichert. Klicke hier, um AFFiNE Cloud zu aktivieren.",
|
||||||
"Joined Workspace": "Workspace beigetreten",
|
"Joined Workspace": "Workspace beigetreten",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"Members": "Members",
|
"Members": "Members",
|
||||||
"Add to favorites": "Add to favorites",
|
"Add to favorites": "Add to favorites",
|
||||||
"It takes up more space on your device": "It takes up more space on your device.",
|
"It takes up more space on your device": "It takes up more space on your device.",
|
||||||
"Export AFFiNE backup file": "Export AFFiNE backup file (coming soon)",
|
"Export AFFiNE backup file": "Export AFFiNE backup file",
|
||||||
"Saved then enable AFFiNE Cloud": "All changes are saved locally, click to enable AFFiNE Cloud.",
|
"Saved then enable AFFiNE Cloud": "All changes are saved locally, click to enable AFFiNE Cloud.",
|
||||||
"Help and Feedback": "Help and Feedback",
|
"Help and Feedback": "Help and Feedback",
|
||||||
"Not now": "Not now",
|
"Not now": "Not now",
|
||||||
|
|||||||
@@ -188,7 +188,7 @@
|
|||||||
"will delete member": "supprimera le membre",
|
"will delete member": "supprimera le membre",
|
||||||
"is a Local Workspace": "est un espace de travail local",
|
"is a Local Workspace": "est un espace de travail local",
|
||||||
"Cloud Workspace Description": "Toutes les données vont être synchronisées et sauvegardées sur le compte AFFiNE <1>{{email}}</1>",
|
"Cloud Workspace Description": "Toutes les données vont être synchronisées et sauvegardées sur le compte AFFiNE <1>{{email}}</1>",
|
||||||
"Export AFFiNE backup file": "Exporter un fichier de sauvegarde AFFiNE (à venir...)",
|
"Export AFFiNE backup file": "Exporter un fichier de sauvegarde AFFiNE",
|
||||||
"Export Description": "Vous pouvez exporter l'intégralité des données de l'espace de travail à titre de sauvegarde ; les données ainsi exportées peuvent être réimportées.",
|
"Export Description": "Vous pouvez exporter l'intégralité des données de l'espace de travail à titre de sauvegarde ; les données ainsi exportées peuvent être réimportées.",
|
||||||
"Download data Description1": "Cela prend davantage d’espace sur votre appareil.",
|
"Download data Description1": "Cela prend davantage d’espace sur votre appareil.",
|
||||||
"It takes up little space on your device": "Cela prend peu d’espace sur votre appareil.",
|
"It takes up little space on your device": "Cela prend peu d’espace sur votre appareil.",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"Check Our Docs": "Проверьте нашу документацию",
|
"Check Our Docs": "Проверьте нашу документацию",
|
||||||
"is a Cloud Workspace": "это облачное рабочее пространство.",
|
"is a Cloud Workspace": "это облачное рабочее пространство.",
|
||||||
"Owner": "Владелец",
|
"Owner": "Владелец",
|
||||||
"Export AFFiNE backup file": "Экспорт файла резервной копии AFFiNE (скоро)",
|
"Export AFFiNE backup file": "Экспорт файла резервной копии AFFiNE",
|
||||||
"is a Local Workspace": "это локальное рабочее пространство",
|
"is a Local Workspace": "это локальное рабочее пространство",
|
||||||
"Wait for Sync": "Дождитесь синхронизации",
|
"Wait for Sync": "Дождитесь синхронизации",
|
||||||
"Joined Workspace": "Присоединенное рабочее пространство",
|
"Joined Workspace": "Присоединенное рабочее пространство",
|
||||||
|
|||||||
@@ -178,7 +178,7 @@
|
|||||||
"Owner": "所有者",
|
"Owner": "所有者",
|
||||||
"Published to Web": "公开到互联网",
|
"Published to Web": "公开到互联网",
|
||||||
"Data sync mode": "数据同步模式",
|
"Data sync mode": "数据同步模式",
|
||||||
"Export AFFiNE backup file": "导出 AFFiNE 备份文件(即将到来)",
|
"Export AFFiNE backup file": "导出 AFFiNE 备份文件",
|
||||||
"AFFiNE Cloud": "AFFiNE 云服务",
|
"AFFiNE Cloud": "AFFiNE 云服务",
|
||||||
"Export Description": "您可以导出整个工作区数据进行备份,导出的数据可以重新被导入。",
|
"Export Description": "您可以导出整个工作区数据进行备份,导出的数据可以重新被导入。",
|
||||||
"It takes up little space on your device": "此操作会在你的设备上占用少许空间。",
|
"It takes up little space on your device": "此操作会在你的设备上占用少许空间。",
|
||||||
|
|||||||
Reference in New Issue
Block a user