Compare commits

..

49 Commits

Author SHA1 Message Date
Alex Yang
81afecdb0e v0.8.0-canary.26 2023-08-17 21:33:59 -05:00
Alex Yang
f1cb2fc6d6 chore: bump version (#3816) 2023-08-17 21:33:34 -05:00
Alex Yang
96b64e1c78 chore: bump version (#3815) 2023-08-17 21:01:49 -05:00
JimmFly
4d58f2b4c7 fix: wrong cascading relationship (#3800) 2023-08-17 19:59:37 -05:00
fourdim
ab9452969b chore: update the top tip (#3797)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-08-17 19:59:08 -05:00
JimmFly
aea508573b chore: adjust preloading tags (#3803) 2023-08-17 19:58:50 -05:00
Peng Xiao
068c697be9 fix: app sidebar ui issues (#3783)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-08-17 18:36:17 +00:00
Qi
7a31089c4b feat: modify shortcut key style (#3807) 2023-08-17 18:27:24 +00:00
Alex Yang
d50fcaa94e build: fix turbosnap rootDir 2023-08-17 13:03:08 -05:00
danielchim
7f8dfc17a0 fix: workspace dropdown fix (#3808) 2023-08-17 17:47:18 +00:00
Hongtao Lye
fb47a04f55 fix: toc tooltip (#3812) 2023-08-17 17:10:37 +00:00
Alex Yang
da3dd1e324 fix(core): cleanup layout when switch page (#3794) 2023-08-16 23:34:56 -05:00
Alex Yang
c3e465d644 fix(core): editor height incorrect (#3799) 2023-08-16 23:20:27 -05:00
Mirone
d8d6620c5f chore: bump blocksuite version (#3798) 2023-08-16 22:57:21 -05:00
Alex Yang
9853d0f6ef fix: disable unstable snapshot (#3791) 2023-08-16 21:44:48 -05:00
Alex Yang
9d723fd487 v0.8.0-canary.25 2023-08-16 21:34:42 -05:00
Alex Yang
ef7ad4f111 build: fix file ignore 2023-08-16 20:08:59 -05:00
Alex Yang
c59d1e67ab v0.8.0-canary.24 2023-08-16 17:42:16 -05:00
danielchim
9ab9c0c70d feat: new workspace switch dropdown design (#3700)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-08-16 17:18:43 -05:00
Alex Yang
f369ca39f7 fix(core): correct the suspense behavior (#3789) 2023-08-16 16:42:35 -05:00
Rohit Yadav
804b8f38b8 fix(core): unused z-index (#3781)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-08-16 16:25:51 -05:00
Alex Yang
dd23917e3e docs: rename to upstreams section 2023-08-16 16:09:29 -05:00
Alex Yang
b604d9b47e docs: update README.md 2023-08-16 16:06:37 -05:00
Alex Yang
1e5a4a6849 feat(storybook): improve code (#3786) 2023-08-16 15:07:55 -05:00
LongYinan
64656c3c98 fix(native): static link msvc runtime on Windows (#3773)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-08-16 14:55:37 -05:00
Alex Yang
61ba85e1f3 chore: bump version (#3784) 2023-08-16 14:53:33 -05:00
Peng Xiao
61ffc4220c fix: ignore some files to be bundled (#3770) 2023-08-16 18:29:47 +00:00
danielchim
866408015e fix: tooltip arrow (#3769)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-08-16 17:48:13 +00:00
Alex Yang
651e815b42 chore: bump version (#3771)
Co-authored-by: Mirone <Saul-Mirone@outlook.com>
2023-08-16 12:02:07 -05:00
Alex Yang
645a300112 ci: add environment 2023-08-16 11:31:13 -05:00
Peng Xiao
e0a3c7f2bc fix: disable secondary db test (#3774) 2023-08-17 00:11:04 +08:00
Alex Yang
3dbefda6ed feat(storybook): import plugins (#3768) 2023-08-16 03:01:14 -05:00
Alex Yang
73eddc2386 v0.8.0-canary.23 2023-08-16 02:43:10 -05:00
Alex Yang
6f9dfcc3c1 feat: add outline plugin (#3624)
Co-authored-by: codert <codert.sn@gmail.com>
2023-08-16 02:34:26 -05:00
Alex Yang
93d352f3d8 ci: checkout pull request ref 2023-08-16 00:54:57 -05:00
Alex Yang
7546b080ea ci: add name 2023-08-16 00:38:45 -05:00
Alex Yang
6988b6f034 ci: publish storybook on push to master 2023-08-16 00:37:53 -05:00
Alex Yang
de2cb1a3bc ci: add publish-storybook.yml 2023-08-16 00:36:24 -05:00
KaranPant
08f01ea1b3 fix: add min height to footer (#3717) 2023-08-16 03:03:30 +00:00
Alex Yang
0df30e43c6 feat(storybook): add not found page (#3767) 2023-08-15 16:58:14 -05:00
Alex Yang
67b33d9b8f feat(storybook): preview app (#3765) 2023-08-15 15:34:02 -05:00
Alex Yang
42dfd0a4bb fix(core): default page mode (#3745) 2023-08-15 14:49:53 -05:00
Alex Yang
25052220a4 feat: add chromatic (#3764) 2023-08-15 14:32:24 -05:00
Qi
48e96cd399 fix: wrong style of cancel button in create workspace modal (#3761) 2023-08-15 12:44:03 -05:00
JimmFly
ca016f1dd1 chore: adjust preloading page (#3753) 2023-08-15 05:53:53 +00:00
Qi
a4fe7dd119 fix: ui issues (#3755) 2023-08-15 05:53:19 +00:00
JimmFly
8d2df468ee chore: update en.json (#3754) 2023-08-15 05:08:48 +00:00
Peng Xiao
2830cb19fe fix: show recursive items (#3750) 2023-08-15 04:01:46 +00:00
Alex Yang
8487b2c4af fix(electron): type on handlers (#3747) 2023-08-15 01:06:25 +00:00
144 changed files with 4206 additions and 2766 deletions

2
.cargo/config.toml Normal file
View File

@@ -0,0 +1,2 @@
[target.x86_64-pc-windows-msvc]
rustflags = ["-C", "target-feature=+crt-static"]

View File

@@ -235,28 +235,6 @@ jobs:
name: affine
fail_ci_if_error: false
storybook-test:
name: Storybook Test
runs-on: ubuntu-latest
environment: development
needs: [build-storybook]
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
playwright-install: true
electron-install: false
- name: Download storybook artifact
uses: actions/download-artifact@v3
with:
name: storybook
path: ./apps/storybook/storybook-static
- name: Run storybook tests
working-directory: ./apps/storybook
run: |
yarn exec concurrently -k -s first -n "SB,TEST" -c "magenta,blue" "yarn exec serve ./storybook-static -l 6006" "yarn exec wait-on tcp:6006 && yarn test"
e2e-plugin-test:
name: E2E Plugin Test
runs-on: ubuntu-latest

38
.github/workflows/publish-storybook.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
name: Publish Storybook
on:
push:
branches:
- master
pull_request_target:
branches:
- master
paths-ignore:
- README.md
- .github/**
- '!.github/workflows/publish-storybook.yml'
jobs:
publish-storybook:
name: Publish Storybook
runs-on: ubuntu-latest
environment: development
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
# This is required to fetch all commits for chromatic
fetch-depth: 0
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
- name: Build Plugins
run: yarn run build:plugins
- name: Publish to Chromatic
uses: chromaui/action@v1
with:
workingDir: apps/storybook
buildScriptName: build
onlyChanged: true
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}

View File

@@ -127,7 +127,7 @@ If you have questions, you are welcome to contact us. One of the best places to
| [@affine/copilot-plugin](plugins/copilot) | AI Copilot that help you document writing |
| [@affine/image-preview-plugin](plugins/image-preview) | Component for previewing an image |
## Thanks
## Upstreams
We would also like to give thanks to open-source projects that make AFFiNE possible:
@@ -178,10 +178,18 @@ See [BUILDING.md] for instructions on how to build AFFiNE from source code.
We welcome contributions from everyone.
See [docs/contributing/tutorial.md](./docs/contributing/tutorial.md) for details.
## Thanks
<a href="https://www.chromatic.com/"><img src="https://user-images.githubusercontent.com/321738/84662277-e3db4f80-af1b-11ea-88f5-91d67a5e59f6.png" width="153" height="30" alt="Chromatic" /></a>
Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testing platform that helps us review UI changes and catch visual regressions.
## License
See [LICENSE] for details.
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftoeverything%2FAFFiNE.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftoeverything%2FAFFiNE?ref=badge_large)
[all-contributors-badge]: https://img.shields.io/github/contributors/toeverything/AFFiNE
[license]: ./LICENSE
[building.md]: ./docs/BUILDING.md
@@ -196,5 +204,3 @@ See [LICENSE] for details.
[typescript-version-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/affine/dev/typescript
[react-version-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/AFFiNE/react?filename=apps%2Fcore%2Fpackage.json&color=rgb(97%2C228%2C251)
[blocksuite-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/AFFiNE/@blocksuite/store?color=6880ff&filename=apps%2Fcore%2Fpackage.json&label=blocksuite
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftoeverything%2FAFFiNE.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftoeverything%2FAFFiNE?ref=badge_large)

View File

@@ -6,14 +6,21 @@ const require = createRequire(import.meta.url);
const packageJson = require('../package.json');
const editorFlags: BlockSuiteFeatureFlags = {
enable_database: true,
enable_slash_menu: true,
enable_edgeless_toolbar: true,
enable_block_hub: true,
enable_drag_handle: true,
enable_block_hub: true,
enable_surface: true,
enable_edgeless_toolbar: true,
enable_slash_menu: true,
enable_database: true,
enable_database_filter: false,
enable_data_view: false,
enable_page_tags: false,
enable_toggle_block: false,
enable_linked_page: true,
enable_bookmark_operation: false,
enable_note_index: false,
enable_attachment_block: true,
};
export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {

View File

@@ -2,12 +2,18 @@
"name": "@affine/core",
"type": "module",
"private": true,
"version": "0.8.0-canary.22",
"version": "0.8.0-canary.26",
"scripts": {
"build": "yarn -T run build-core",
"dev": "yarn -T run dev-core",
"static-server": "ts-node-esm ./server.mts"
},
"exports": {
"./app": "./src/app.tsx",
"./router": "./src/router.ts",
"./bootstrap/setup": "./src/bootstrap/setup.ts",
"./bootstrap/register-plugins": "./src/bootstrap/register-plugins.ts"
},
"dependencies": {
"@affine-test/fixtures": "workspace:*",
"@affine/component": "workspace:*",
@@ -18,27 +24,27 @@
"@affine/jotai": "workspace:*",
"@affine/templates": "workspace:*",
"@affine/workspace": "workspace:*",
"@blocksuite/block-std": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/blocks": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/editor": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/global": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/block-std": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/blocks": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/editor": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/global": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/icons": "^2.1.31",
"@blocksuite/lit": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/store": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/lit": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/store": "0.0.0-20230818021533-7e342436-nightly",
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/sortable": "^7.0.2",
"@emotion/cache": "^11.11.0",
"@emotion/react": "^11.11.1",
"@emotion/server": "^11.11.0",
"@emotion/styled": "^11.11.0",
"@mui/material": "^5.14.4",
"@mui/material": "^5.14.5",
"@react-hookz/web": "^23.1.0",
"@toeverything/components": "^0.0.11",
"async-call-rpc": "^6.3.1",
"cmdk": "^0.2.0",
"css-spring": "^4.1.0",
"cssnano": "^6.0.1",
"graphql": "^16.7.1",
"graphql": "^16.8.0",
"intl-segmenter-polyfill-rs": "^0.1.5",
"jotai": "^2.3.1",
"jotai-devtools": "^0.6.1",
@@ -51,21 +57,21 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-is": "18.2.0",
"react-resizable-panels": "^0.0.54",
"react-resizable-panels": "^0.0.55",
"react-router-dom": "^6.15.0",
"rxjs": "^7.8.1",
"ses": "^0.18.7",
"swr": "2.2.1",
"y-protocols": "^1.0.5",
"yjs": "^13.6.7",
"zod": "^3.21.4"
"zod": "^3.22.1"
},
"devDependencies": {
"@perfsee/webpack": "^1.8.4",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.11",
"@sentry/webpack-plugin": "^2.6.2",
"@svgr/webpack": "^8.0.1",
"@swc/core": "^1.3.76",
"@svgr/webpack": "^8.1.0",
"@swc/core": "^1.3.77",
"@types/lodash.debounce": "^4.0.7",
"@types/webpack-env": "^1.18.1",
"copy-webpack-plugin": "^11.0.0",

View File

@@ -20,8 +20,10 @@ import { getOrCreateWorkspace } from '@affine/workspace/manager';
import { createIndexedDBDownloadProvider } from '@affine/workspace/providers';
import { nanoid } from '@blocksuite/store';
import { useStaticBlockSuiteWorkspace } from '@toeverything/infra/__internal__/react';
import { rootStore } from '@toeverything/infra/atom';
import { buildShowcaseWorkspace } from '@toeverything/infra/blocksuite';
import { setPageModeAtom } from '../../atoms';
import {
BlockSuitePageList,
NewWorkspaceSettingDetail,
@@ -43,7 +45,12 @@ export const LocalAdapter: WorkspaceAdapter<WorkspaceFlavour.LOCAL> = {
);
blockSuiteWorkspace.meta.setName(DEFAULT_WORKSPACE_NAME);
if (runtimeConfig.enablePreloading) {
buildShowcaseWorkspace(blockSuiteWorkspace).catch(err => {
buildShowcaseWorkspace(blockSuiteWorkspace, {
store: rootStore,
atoms: {
pageMode: setPageModeAtom,
},
}).catch(err => {
logger.error('init page with preloading failed', err);
});
} else {

View File

@@ -36,9 +36,13 @@ const importLogger = new DebugLogger('plugins:import');
const pushLayoutAtom = atom<
null,
// fixme: check plugin name here
[pluginName: string, create: (root: HTMLElement) => () => void],
[
pluginName: string,
create: (root: HTMLElement) => () => void,
options: { maxWidth: (number | undefined)[] } | undefined,
],
void
>(null, (_, set, pluginName, callback) => {
>(null, (_, set, pluginName, callback, options) => {
set(pluginWindowAtom, items => ({
...items,
[pluginName]: callback,
@@ -50,20 +54,20 @@ const pushLayoutAtom = atom<
first: 'editor',
second: pluginName,
splitPercentage: 70,
maxWidth: options?.maxWidth,
};
} else {
return {
...layout,
direction: 'horizontal',
first: 'editor',
splitPercentage: 70,
second: {
direction: 'horizontal',
// fixme: incorrect type here
first: layout.second,
second: pluginName,
splitPercentage: 70,
first: pluginName,
second: layout.second,
splitPercentage: 50,
},
} as ExpectedLayout;
} satisfies ExpectedLayout;
}
});
addCleanup(pluginName, () => {
@@ -77,40 +81,36 @@ const deleteLayoutAtom = atom<null, [string], void>(null, (_, set, id) => {
delete newItems[id];
return newItems;
});
const removeLayout = (layout: LayoutNode): LayoutNode => {
if (layout === 'editor') {
return 'editor';
const removeLayout = (layout: LayoutNode): LayoutNode | string => {
if (typeof layout === 'string') {
return layout;
}
if (layout.first === id) {
return layout.second;
} else if (layout.second === id) {
return layout.first;
} else {
if (typeof layout === 'string') {
return layout as ExpectedLayout;
}
if (layout.first === id) {
return layout.second;
} else if (layout.second === id) {
return layout.first;
} else {
return removeLayout(layout.second);
}
return {
...layout,
second: removeLayout(layout.second),
};
}
};
set(contentLayoutAtom, layout => {
if (layout === 'editor') {
return 'editor';
} else {
if (typeof layout === 'string') {
return layout as ExpectedLayout;
}
if (layout.first === id) {
return layout.second as ExpectedLayout;
} else if (layout.second === id) {
return layout.first as ExpectedLayout;
} else {
return removeLayout(layout.second) as ExpectedLayout;
}
return removeLayout(layout) as ExpectedLayout;
}
});
});
// clean up plugin windows when switching to other pages
rootStore.sub(currentPageAtom, () => {
rootStore.set(contentLayoutAtom, 'editor');
});
// module -> importName -> updater[]
export const _rootImportsMap = new Map<string, Map<string, any>>();
const rootImportsMapSetupPromise = setupImportsMap(_rootImportsMap, {
@@ -123,6 +123,7 @@ const rootImportsMapSetupPromise = setupImportsMap(_rootImportsMap, {
swr: import('swr'),
'@affine/component': import('@affine/component'),
'@blocksuite/icons': import('@blocksuite/icons'),
'@blocksuite/blocks': import('@blocksuite/blocks'),
'@affine/sdk/entry': {
rootStore: rootStore,
currentWorkspaceAtom: currentWorkspaceAtom,

View File

@@ -169,7 +169,14 @@ function createFirstAppData() {
rootStore.set(rootWorkspacesMetadataAtom, result);
}
let isSetup = false;
export async function setup() {
if (isSetup) {
console.warn('already setup');
return;
}
isSetup = true;
rootStore.set(
workspaceAdaptersAtom,
WorkspaceAdapters as Record<

View File

@@ -81,11 +81,7 @@ const NameWorkspaceContent = ({
onChange={setWorkspaceName}
/>
<div className={style.buttonGroup}>
<Button
data-testid="create-workspace-close-button"
type="primary"
onClick={onClose}
>
<Button data-testid="create-workspace-close-button" onClick={onClose}>
{t.Cancel()}
</Button>
<Button

View File

@@ -12,7 +12,6 @@ import type { ReactElement } from 'react';
import { useCallback } from 'react';
export const StyledListItem = styled(MenuItem)(() => ({
width: '132px',
height: '38px',
textTransform: 'capitalize',
}));

View File

@@ -38,9 +38,8 @@ export const ExportPanel = ({ workspace }: ExportPanelProps) => {
const t = useAFFiNEI18N();
const onExport = useCallback(async () => {
await syncBlobsToSqliteDb(workspace);
const result: SaveDBFileResult = await window.apis?.dialog.saveDBFileAs(
workspaceId
);
const result: SaveDBFileResult =
await window.apis?.dialog.saveDBFileAs(workspaceId);
if (result?.error) {
toast(t[result.error]());
} else if (!result?.canceled) {

View File

@@ -3,20 +3,48 @@ import { SettingWrapper } from '@affine/component/setting-components';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import {
type ShortcutsInfo,
useEdgelessShortcuts,
useGeneralShortcuts,
useMarkdownShortcuts,
usePageShortcuts,
} from '../../../../../hooks/affine/use-shortcuts';
import { shortcutRow } from './style.css';
import { shortcutKey, shortcutKeyContainer, shortcutRow } from './style.css';
const ShortcutsPanel = ({
shortcutsInfo,
}: {
shortcutsInfo: ShortcutsInfo;
}) => {
return (
<SettingWrapper title={shortcutsInfo.title}>
{Object.entries(shortcutsInfo.shortcuts).map(([title, shortcuts]) => {
return (
<div key={title} className={shortcutRow}>
<span>{title}</span>
<div className={shortcutKeyContainer}>
{shortcuts.map(key => {
return (
<span className={shortcutKey} key={key}>
{key}
</span>
);
})}
</div>
</div>
);
})}
</SettingWrapper>
);
};
export const Shortcuts = () => {
const t = useAFFiNEI18N();
const markdownShortcuts = useMarkdownShortcuts();
const pageShortcuts = usePageShortcuts();
const edgelessShortcuts = useEdgelessShortcuts();
const generalShortcuts = useGeneralShortcuts();
const markdownShortcutsInfo = useMarkdownShortcuts();
const pageShortcutsInfo = usePageShortcuts();
const edgelessShortcutsInfo = useEdgelessShortcuts();
const generalShortcutsInfo = useGeneralShortcuts();
return (
<>
@@ -25,46 +53,10 @@ export const Shortcuts = () => {
subtitle={t['Check Keyboard Shortcuts quickly']()}
data-testid="keyboard-shortcuts-title"
/>
<SettingWrapper title={t['General']()}>
{Object.entries(generalShortcuts).map(([title, shortcuts]) => {
return (
<div key={title} className={shortcutRow}>
<span>{title}</span>
<span className="shortcut">{shortcuts}</span>
</div>
);
})}
</SettingWrapper>
<SettingWrapper title={t['Page']()}>
{Object.entries(pageShortcuts).map(([title, shortcuts]) => {
return (
<div key={title} className={shortcutRow}>
<span>{title}</span>
<span className="shortcut">{shortcuts}</span>
</div>
);
})}
</SettingWrapper>
<SettingWrapper title={t['Edgeless']()}>
{Object.entries(edgelessShortcuts).map(([title, shortcuts]) => {
return (
<div key={title} className={shortcutRow}>
<span>{title}</span>
<span className="shortcut">{shortcuts}</span>
</div>
);
})}
</SettingWrapper>
<SettingWrapper title={t['Markdown Syntax']()}>
{Object.entries(markdownShortcuts).map(([title, shortcuts]) => {
return (
<div key={title} className={shortcutRow}>
<span>{title}</span>
<span className="shortcut">{shortcuts}</span>
</div>
);
})}
</SettingWrapper>
<ShortcutsPanel shortcutsInfo={generalShortcutsInfo} />
<ShortcutsPanel shortcutsInfo={pageShortcutsInfo} />
<ShortcutsPanel shortcutsInfo={edgelessShortcutsInfo} />
<ShortcutsPanel shortcutsInfo={markdownShortcutsInfo} />
</>
);
};

View File

@@ -1,4 +1,4 @@
import { globalStyle, style } from '@vanilla-extract/css';
import { style } from '@vanilla-extract/css';
export const shortcutRow = style({
height: '32px',
@@ -14,8 +14,25 @@ export const shortcutRow = style({
},
});
globalStyle(`${shortcutRow} .shortcut`, {
border: '1px solid var(--affine-border-color)',
borderRadius: '8px',
padding: '4px 18px',
export const shortcutKeyContainer = style({
display: 'flex',
});
export const shortcutKey = style({
minWidth: '24px',
height: '20px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '0 6px',
border: '1px solid var(--affine-border-color)',
borderRadius: '4px',
background: 'var(--affine-background-tertiary-color)',
boxShadow:
'0px 6px 4px 0px rgba(255, 255, 255, 0.24) inset, 0px 0px 0px 0.5px rgba(0, 0, 0, 0.10) inset',
fontSize: 'var(--affine-font-xs)',
selectors: {
'&:not(:last-of-type)': {
marginRight: '2px',
},
},
});

View File

@@ -1,10 +1,11 @@
import {
SettingModal as SettingModalBase,
type SettingModalProps as SettingModalBaseProps,
WorkspaceDetailSkeleton,
} from '@affine/component/setting-components';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ContactWithUsIcon } from '@blocksuite/icons';
import { useCallback } from 'react';
import { Suspense, useCallback } from 'react';
import { AccountSetting } from './account-setting';
import {
@@ -77,7 +78,9 @@ export const SettingModal = ({
<div className="wrapper">
<div className="content">
{activeTab === 'workspace' && workspaceId ? (
<WorkspaceSetting key={workspaceId} workspaceId={workspaceId} />
<Suspense fallback={<WorkspaceDetailSkeleton />}>
<WorkspaceSetting key={workspaceId} workspaceId={workspaceId} />
</Suspense>
) : null}
{generalSettingList.find(v => v.key === activeTab) ? (
<GeneralSetting generalKey={activeTab as GeneralSettingKeys} />

View File

@@ -28,6 +28,7 @@ globalStyle(`${settingContent} .footer`, {
marginTop: '-80px',
fontSize: 'var(--affine-font-sm)',
display: 'flex',
minHeight: '100px',
});
globalStyle(`${settingContent} .footer a`, {

View File

@@ -1,7 +1,6 @@
import { WorkspaceDetailSkeleton } from '@affine/component/setting-components';
import { usePassiveWorkspaceEffect } from '@toeverything/infra/__internal__/react';
import { useSetAtom } from 'jotai';
import { Suspense, useCallback } from 'react';
import { useCallback } from 'react';
import { getUIAdapter } from '../../../../adapters/workspace';
import { openSettingModalAtom } from '../../../../atoms';
@@ -33,12 +32,10 @@ export const WorkspaceSetting = ({ workspaceId }: { workspaceId: string }) => {
const onTransformWorkspace = useOnTransformWorkspace();
return (
<Suspense fallback={<WorkspaceDetailSkeleton />}>
<NewSettingsDetail
onTransformWorkspace={onTransformWorkspace}
onDeleteWorkspace={onDeleteWorkspace}
currentWorkspaceId={workspaceId}
/>
</Suspense>
<NewSettingsDetail
onTransformWorkspace={onTransformWorkspace}
onDeleteWorkspace={onDeleteWorkspace}
currentWorkspaceId={workspaceId}
/>
);
};

View File

@@ -1,4 +1,4 @@
import { style } from '@vanilla-extract/css';
import { type ComplexStyleRule, style } from '@vanilla-extract/css';
export const headerTitleContainer = style({
display: 'flex',
@@ -10,11 +10,11 @@ export const headerTitleContainer = style({
});
export const titleEditButton = style({
flexGrow: 1,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
});
WebkitAppRegion: 'no-drag',
} as ComplexStyleRule);
export const titleInput = style({
position: 'absolute',

View File

@@ -3,6 +3,8 @@ import type { HTMLAttributes } from 'react';
import type React from 'react';
import { cloneElement, useState } from 'react';
import edgelessHover from './animation-data/edgeless-hover.json';
import pageHover from './animation-data/page-hover.json';
import { StyledSwitchItem } from './style';
type HoverAnimateControllerProps = {
@@ -52,7 +54,7 @@ export const PageSwitchItem = (
options={{
loop: false,
autoplay: false,
animationData: require('./animation-data/page-hover.json'),
animationData: pageHover,
rendererSettings: {
preserveAspectRatio: 'xMidYMid slice',
},
@@ -71,7 +73,7 @@ export const EdgelessSwitchItem = (
options={{
loop: false,
autoplay: false,
animationData: require('./animation-data/edgeless-hover.json'),
animationData: edgelessHover,
rendererSettings: {
preserveAspectRatio: 'xMidYMid slice',
},

View File

@@ -17,7 +17,15 @@ import { contentLayoutAtom, rootStore } from '@toeverything/infra/atom';
import clsx from 'clsx';
import { useAtomValue, useSetAtom } from 'jotai';
import type { CSSProperties, ReactElement } from 'react';
import { memo, Suspense, useCallback, useMemo } from 'react';
import {
memo,
startTransition,
Suspense,
useCallback,
useEffect,
useMemo,
useRef,
} from 'react';
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
import { pageSettingFamily } from '../atoms';
@@ -134,25 +142,42 @@ interface PluginContentAdapterProps {
const PluginContentAdapter = memo<PluginContentAdapterProps>(
function PluginContentAdapter({ windowItem, pluginName }) {
return (
<div
className={pluginContainer}
ref={useCallback(
(ref: HTMLDivElement | null) => {
if (ref) {
const div = document.createElement('div');
const cleanup = windowItem(div);
ref.appendChild(div);
addCleanup(pluginName, () => {
cleanup();
ref.removeChild(div);
const rootRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
const abortController = new AbortController();
const root = rootRef.current;
if (root) {
startTransition(() => {
if (abortController.signal.aborted) {
return;
}
const div = document.createElement('div');
const cleanup = windowItem(div);
root.appendChild(div);
if (abortController.signal.aborted) {
cleanup();
root.removeChild(div);
} else {
const cl = () => {
cleanup();
root.removeChild(div);
};
const dispose = addCleanup(pluginName, cl);
abortController.signal.addEventListener('abort', () => {
setTimeout(() => {
dispose();
cl();
});
}
},
[pluginName, windowItem]
)}
/>
);
});
}
});
return () => {
abortController.abort();
};
}
return;
}, [pluginName, windowItem]);
return <div className={pluginContainer} ref={rootRef} />;
}
);
@@ -175,13 +200,13 @@ const LayoutPanel = memo(function LayoutPanel(
}
} else {
return (
<PanelGroup
style={{
height: 'calc(100% - 52px)',
}}
direction={node.direction}
>
<Panel defaultSize={node.splitPercentage}>
<PanelGroup direction={node.direction}>
<Panel
defaultSize={node.splitPercentage}
style={{
maxWidth: node.maxWidth?.[0],
}}
>
<Suspense>
<LayoutPanel node={node.first} editorProps={props.editorProps} />
</Suspense>
@@ -191,6 +216,7 @@ const LayoutPanel = memo(function LayoutPanel(
defaultSize={100 - node.splitPercentage}
style={{
overflow: 'scroll',
maxWidth: node.maxWidth?.[1],
}}
>
<Suspense>
@@ -211,6 +237,18 @@ export const PageDetailEditor = (props: PageDetailEditorProps) => {
const layout = useAtomValue(contentLayoutAtom);
if (layout === 'editor') {
return (
<Suspense>
<PanelGroup direction="horizontal">
<Panel>
<EditorWrapper {...props} />
</Panel>
</PanelGroup>
</Suspense>
);
}
return (
<>
<Suspense>

View File

@@ -8,6 +8,7 @@ export const header = style({
padding: '0 16px',
minHeight: '52px',
borderBottom: '1px solid var(--affine-border-color)',
zIndex: 2,
selectors: {
'&[data-sidebar-floating="false"]': {
WebkitAppRegion: 'drag',

View File

@@ -44,7 +44,7 @@ const OSWarningMessage = () => {
return (
<span>
<Trans i18nKey="recommendBrowser">
We recommend the <strong>Chrome</strong> browser for optimal
We recommend the <strong>Chrome</strong> browser for an optimal
experience.
</Trans>
</span>

View File

@@ -60,7 +60,11 @@ export const HelpIsland = ({
style={{ height: spread ? `${showList.length * 40 + 4}px` : 0 }}
>
{showList.includes('whatNew') && (
<Tooltip content={t["Discover what's new!"]()} placement="left-end">
<Tooltip
content={t["Discover what's new!"]()}
placement="left-end"
showArrow={true}
>
<StyledIconWrapper
data-testid="right-bottom-change-log-icon"
onClick={() => {
@@ -72,7 +76,11 @@ export const HelpIsland = ({
</Tooltip>
)}
{showList.includes('contact') && (
<Tooltip content={t['Contact Us']()} placement="left-end">
<Tooltip
content={t['Contact Us']()}
placement="left-end"
showArrow={true}
>
<StyledIconWrapper
data-testid="right-bottom-contact-us-icon"
onClick={openAbout}
@@ -82,7 +90,11 @@ export const HelpIsland = ({
</Tooltip>
)}
{showList.includes('shortcuts') && (
<Tooltip content={t['Keyboard Shortcuts']()} placement="left-end">
<Tooltip
content={t['Keyboard Shortcuts']()}
placement="left-end"
showArrow={true}
>
<StyledIconWrapper
data-testid="shortcuts-icon"
onClick={() => {
@@ -98,6 +110,7 @@ export const HelpIsland = ({
<Tooltip
content={t['com.affine.helpIsland.gettingStarted']()}
placement="left-end"
showArrow={true}
>
<StyledIconWrapper
data-testid="easy-guide"
@@ -112,7 +125,11 @@ export const HelpIsland = ({
)}
</StyledAnimateWrapper>
<Tooltip content={t['Help and Feedback']()} placement="left-end">
<Tooltip
content={t['Help and Feedback']()}
placement={'left-end'}
showArrow={true}
>
<MuiFade in={!spread} data-testid="faq-icon">
<StyledTriggerWrapper>
<HelpIcon />

View File

@@ -6,46 +6,80 @@ import {
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import {
type ShortcutsInfo,
useEdgelessShortcuts,
useGeneralShortcuts,
useMarkdownShortcuts,
usePageShortcuts,
} from '../../../hooks/affine/use-shortcuts';
import { KeyboardIcon } from './icons';
import {
StyledListItem,
StyledModalHeader,
StyledShortcutsModal,
StyledSubTitle,
StyledTitle,
} from './style';
import * as styles from './style.css';
// import {
// StyledListItem,
// StyledModalHeader,
// StyledShortcutsModal,
// StyledSubTitle,
// StyledTitle,
// } from './style';
type ModalProps = {
open: boolean;
onClose: () => void;
};
const ShortcutsPanel = ({
shortcutsInfo,
}: {
shortcutsInfo: ShortcutsInfo;
}) => {
return (
<>
<div className={styles.subtitle}>{shortcutsInfo.title}</div>
{Object.entries(shortcutsInfo.shortcuts).map(([title, shortcuts]) => {
return (
<div className={styles.listItem} key={title}>
<span>{title}</span>
<div className={styles.keyContainer}>
{shortcuts.map(key => {
return (
<span className={styles.key} key={key}>
{key}
</span>
);
})}
</div>
</div>
);
})}
</>
);
};
export const ShortcutsModal = ({ open, onClose }: ModalProps) => {
const t = useAFFiNEI18N();
const markdownShortcuts = useMarkdownShortcuts();
const pageShortcuts = usePageShortcuts();
const edgelessShortcuts = useEdgelessShortcuts();
const generalShortcuts = useGeneralShortcuts();
const markdownShortcutsInfo = useMarkdownShortcuts();
const pageShortcutsInfo = usePageShortcuts();
const edgelessShortcutsInfo = useEdgelessShortcuts();
const generalShortcutsInfo = useGeneralShortcuts();
return (
<MuiSlide direction="left" in={open} mountOnEnter unmountOnExit>
<StyledShortcutsModal data-testid="shortcuts-modal">
<div className={styles.shortcutsModal} data-testid="shortcuts-modal">
<MuiClickAwayListener
onClickAway={() => {
onClose();
}}
>
<div>
<StyledModalHeader>
<StyledTitle>
<div
className={styles.modalHeader}
style={{ marginBottom: '-28px' }}
>
<div className={styles.title}>
<KeyboardIcon />
{t['Shortcuts']()}
</StyledTitle>
</div>
<ModalCloseButton
top={6}
@@ -54,48 +88,14 @@ export const ShortcutsModal = ({ open, onClose }: ModalProps) => {
onClose();
}}
/>
</StyledModalHeader>
<StyledSubTitle style={{ marginTop: 0 }}>
{t['General']()}
</StyledSubTitle>
{Object.entries(generalShortcuts).map(([title, shortcuts]) => {
return (
<StyledListItem key={title}>
<span>{title}</span>
<span>{shortcuts}</span>
</StyledListItem>
);
})}
<StyledSubTitle>{t['Page']()}</StyledSubTitle>
{Object.entries(pageShortcuts).map(([title, shortcuts]) => {
return (
<StyledListItem key={title}>
<span>{title}</span>
<span>{shortcuts}</span>
</StyledListItem>
);
})}
<StyledSubTitle>{t['Edgeless']()}</StyledSubTitle>
{Object.entries(edgelessShortcuts).map(([title, shortcuts]) => {
return (
<StyledListItem key={title}>
<span>{title}</span>
<span>{shortcuts}</span>
</StyledListItem>
);
})}
<StyledSubTitle>{t['Markdown Syntax']()}</StyledSubTitle>
{Object.entries(markdownShortcuts).map(([title, shortcuts]) => {
return (
<StyledListItem key={title}>
<span>{title}</span>
<span>{shortcuts}</span>
</StyledListItem>
);
})}
</div>
<ShortcutsPanel shortcutsInfo={generalShortcutsInfo} />
<ShortcutsPanel shortcutsInfo={pageShortcutsInfo} />
<ShortcutsPanel shortcutsInfo={edgelessShortcutsInfo} />
<ShortcutsPanel shortcutsInfo={markdownShortcutsInfo} />
</div>
</MuiClickAwayListener>
</StyledShortcutsModal>
</div>
</MuiSlide>
);
};

View File

@@ -0,0 +1,89 @@
import { globalStyle, style } from '@vanilla-extract/css';
export const shortcutsModal = style({
width: '288px',
height: '74vh',
paddingBottom: '28px',
backgroundColor: 'var(--affine-white)',
boxShadow: 'var(--affine-popover-shadow)',
borderRadius: `var(--affine-popover-radius)`,
overflow: 'auto',
position: 'fixed',
right: '12px',
top: '0',
bottom: '0',
margin: 'auto',
zIndex: 'var(--affine-z-index-modal)',
});
// export const shortcutsModal = style({
// color: 'var(--affine-text-primary-color)',
// fontWeight: '500',
// fontSize: 'var(--affine-font-sm)',
// height: '44px',
// display: 'flex',
// justifyContent: 'center',
// alignItems: 'center',
// svg: {
// width: '20px',
// marginRight: '14px',
// color: 'var(--affine-primary-color)',
// },
// });
export const title = style({
color: 'var(--affine-text-primary-color)',
fontWeight: '500',
fontSize: 'var(--affine-font-sm)',
height: '44px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
});
globalStyle(`${title} svg`, {
width: '20px',
marginRight: '14px',
color: 'var(--affine-primary-color)',
});
export const subtitle = style({
fontWeight: '500',
fontSize: 'var(--affine-font-sm)',
height: '34px',
lineHeight: '36px',
marginTop: '28px',
padding: '0 16px',
});
export const modalHeader = style({
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
paddingTop: '8px 4px 0 4px',
width: '100%',
padding: '8px 16px 0 16px',
position: 'sticky',
left: '0',
top: '0',
background: 'var(--affine-white)',
transition: 'background-color 0.5s',
});
export const listItem = style({
height: '34px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
fontSize: 'var(--affine-font-sm)',
padding: '0 16px',
});
export const keyContainer = style({
display: 'flex',
});
export const key = style({
selectors: {
'&:not(:last-child)::after': {
content: '+',
margin: '0 4px',
},
},
});

View File

@@ -1,56 +0,0 @@
import { displayFlex, styled } from '@affine/component';
export const StyledShortcutsModal = styled('div')(() => ({
width: '288px',
height: '74vh',
paddingBottom: '28px',
backgroundColor: 'var(--affine-white)',
boxShadow: 'var(--affine-popover-shadow)',
borderRadius: `var(--affine-popover-radius)`,
overflow: 'auto',
boxRadius: '10px',
position: 'fixed',
right: '12px',
top: '0',
bottom: '0',
margin: 'auto',
zIndex: 'var(--affine-z-index-modal)',
}));
export const StyledTitle = styled('div')(() => ({
color: 'var(--affine-text-primary-color)',
fontWeight: '500',
fontSize: 'var(--affine-font-sm)',
height: '44px',
...displayFlex('center', 'center'),
svg: {
width: '20px',
marginRight: '14px',
color: 'var(--affine-primary-color)',
},
}));
export const StyledSubTitle = styled('div')(() => ({
fontWeight: '500',
fontSize: 'var(--affine-font-sm)',
height: '34px',
lineHeight: '36px',
marginTop: '28px',
padding: '0 16px',
}));
export const StyledModalHeader = styled('div')(() => ({
...displayFlex('space-between', 'center'),
paddingTop: '8px 4px 0 4px',
width: '100%',
padding: '8px 16px 0 16px',
position: 'sticky',
left: '0',
top: '0',
background: 'var(--affine-white)',
transition: 'background-color 0.5s',
}));
export const StyledListItem = styled('div')(() => ({
height: '34px',
...displayFlex('space-between', 'center'),
fontSize: 'var(--affine-font-sm)',
padding: '0 16px',
}));

View File

@@ -8,6 +8,7 @@ export const group = style({
display: 'flex',
gap: '24px',
justifyContent: 'center',
zIndex: 2,
});
export const buttonContainer = style({
boxShadow: 'var(--affine-float-button-shadow-2)',

View File

@@ -1,12 +1,4 @@
import {
Menu,
MenuItem,
Modal,
ModalCloseButton,
ModalWrapper,
Tooltip,
} from '@affine/component';
import { ScrollableContainer } from '@affine/component';
import { Menu, MenuItem, Tooltip } from '@affine/component';
import { WorkspaceList } from '@affine/component/workspace-list';
import type {
AffineCloudWorkspace,
@@ -15,28 +7,40 @@ import type {
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { RootWorkspaceMetadata } from '@affine/workspace/atom';
import { HelpIcon, ImportIcon, PlusIcon } from '@blocksuite/icons';
import type { DragEndEvent } from '@dnd-kit/core';
import { useCallback, useRef } from 'react';
import type { AllWorkspace } from '../../../shared';
import { Footer } from '../footer';
import {
StyledCreateWorkspaceCard,
CloudWorkspaceIcon,
HelpIcon,
ImportIcon,
MoreHorizontalIcon,
PlusIcon,
} from '@blocksuite/icons';
import type { DragEndEvent } from '@dnd-kit/core';
import { Popover } from '@mui/material';
import { IconButton } from '@toeverything/components/button';
import { Divider } from '@toeverything/components/divider';
import { useSetAtom } from 'jotai';
import { useCallback } from 'react';
import { openDisableCloudAlertModalAtom } from '../../../atoms';
import type { AllWorkspace } from '../../../shared';
import {
StyledCreateWorkspaceCardPill,
StyledCreateWorkspaceCardPillContainer,
StyledCreateWorkspaceCardPillContent,
StyledCreateWorkspaceCardPillIcon,
StyledCreateWorkspaceCardPillTextSecondary,
StyledHelperContainer,
StyledImportWorkspaceCardPill,
StyledModalBody,
StyledModalContent,
StyledModalFooterContent,
StyledModalHeader,
StyledModalHeaderContent,
StyledModalHeaderLeft,
StyledModalTitle,
StyledOperationWrapper,
StyleWorkspaceAdd,
StyleWorkspaceInfo,
StyleWorkspaceTitle,
StyledSignInCardPill,
StyledSignInCardPillTextCotainer,
StyledSignInCardPillTextPrimary,
StyledSignInCardPillTextSecondary,
} from './styles';
interface WorkspaceModalProps {
@@ -52,105 +56,94 @@ interface WorkspaceModalProps {
onMoveWorkspace: (activeId: string, overId: string) => void;
}
const CreateWorkspaceCard = ({
onNewWorkspace,
onAddWorkspace,
}: {
onNewWorkspace: () => void;
onAddWorkspace: () => void;
}) => {
const AccountMenu = () => {
const t = useAFFiNEI18N();
const anchorEL = useRef<HTMLDivElement>(null);
return (
<div>
<div>Unlimted</div>
<Divider></Divider>
<MenuItem icon={<ImportIcon />} data-testid="editor-option-menu-import">
{t['com.affine.workspace.cloud.join']()}
</MenuItem>
<MenuItem icon={<ImportIcon />} data-testid="editor-option-menu-import">
{t['com.affine.workspace.cloud.account.settings']()}
</MenuItem>
<Divider></Divider>
<MenuItem icon={<ImportIcon />} data-testid="editor-option-menu-import">
{t['com.affine.workspace.cloud.account.logout']()}
</MenuItem>
</div>
);
};
if (runtimeConfig.enableSQLiteProvider && environment.isDesktop) {
return (
<Menu
placement="auto"
trigger={['click']}
zIndex={1000}
content={
<StyledCreateWorkspaceCardPillContainer>
<StyledCreateWorkspaceCardPill>
<MenuItem
style={{
height: 'auto',
padding: '8px 12px',
}}
onClick={onNewWorkspace}
data-testid="new-workspace"
>
<StyledCreateWorkspaceCardPillContent>
<div>
<p>{t['New Workspace']()}</p>
<StyledCreateWorkspaceCardPillTextSecondary>
<p>{t['Create your own workspace']()}</p>
</StyledCreateWorkspaceCardPillTextSecondary>
</div>
<StyledCreateWorkspaceCardPillIcon>
<PlusIcon />
</StyledCreateWorkspaceCardPillIcon>
</StyledCreateWorkspaceCardPillContent>
</MenuItem>
</StyledCreateWorkspaceCardPill>
<StyledCreateWorkspaceCardPill>
<MenuItem
onClick={onAddWorkspace}
data-testid="add-workspace"
style={{
height: 'auto',
padding: '8px 12px',
}}
>
<StyledCreateWorkspaceCardPillContent>
<div>
<p>{t['Add Workspace']()}</p>
<StyledCreateWorkspaceCardPillTextSecondary>
<p>{t['Add Workspace Hint']()}</p>
</StyledCreateWorkspaceCardPillTextSecondary>
</div>
<StyledCreateWorkspaceCardPillIcon>
<ImportIcon />
</StyledCreateWorkspaceCardPillIcon>
</StyledCreateWorkspaceCardPillContent>
</MenuItem>
</StyledCreateWorkspaceCardPill>
</StyledCreateWorkspaceCardPillContainer>
}
>
<StyledCreateWorkspaceCard
ref={anchorEL}
data-testid="add-or-new-workspace"
>
<StyleWorkspaceAdd className="add-icon">
<PlusIcon />
</StyleWorkspaceAdd>
const CloudWorkSpaceList = ({
disabled,
workspaces,
onClickWorkspace,
onClickWorkspaceSetting,
currentWorkspaceId,
onMoveWorkspace,
}: WorkspaceModalProps) => {
const t = useAFFiNEI18N();
<StyleWorkspaceInfo>
<StyleWorkspaceTitle>{t['New Workspace']()}</StyleWorkspaceTitle>
<p>{t['Create Or Import']()}</p>
</StyleWorkspaceInfo>
</StyledCreateWorkspaceCard>
</Menu>
);
} else {
return (
<div>
<StyledCreateWorkspaceCard
onClick={onNewWorkspace}
data-testid="new-workspace"
>
<StyleWorkspaceAdd className="add-icon">
<PlusIcon />
</StyleWorkspaceAdd>
return (
<>
<StyledModalHeader>
<StyledModalHeaderLeft>
<StyledModalTitle>
{t['com.affine.workspace.cloud.sync']()}
</StyledModalTitle>
<Tooltip
content={t['Workspace description']()}
placement="top-start"
disablePortal={true}
>
<StyledHelperContainer>
<HelpIcon />
</StyledHelperContainer>
</Tooltip>
</StyledModalHeaderLeft>
<StyleWorkspaceInfo>
<StyleWorkspaceTitle>{t['New Workspace']()}</StyleWorkspaceTitle>
<p>{t['Create Or Import']()}</p>
</StyleWorkspaceInfo>
</StyledCreateWorkspaceCard>
</div>
);
}
<StyledOperationWrapper>
<Menu
placement="bottom-end"
trigger={['click']}
content={<AccountMenu />}
zIndex={1000}
>
<IconButton
data-testid="previous-image-button"
icon={<MoreHorizontalIcon />}
type="plain"
/>
</Menu>
</StyledOperationWrapper>
</StyledModalHeader>
<StyledModalContent>
<WorkspaceList
disabled={disabled}
items={
workspaces.filter(
({ flavour }) => flavour !== WorkspaceFlavour.PUBLIC
) as (AffineCloudWorkspace | LocalWorkspace)[]
}
currentWorkspaceId={currentWorkspaceId}
onClick={onClickWorkspace}
onSettingClick={onClickWorkspaceSetting}
onDragEnd={useCallback(
(event: DragEndEvent) => {
const { active, over } = event;
if (active.id !== over?.id) {
onMoveWorkspace(active.id as string, over?.id as string);
}
},
[onMoveWorkspace]
)}
/>
<Divider />
</StyledModalContent>
</>
);
};
export const WorkspaceListModal = ({
@@ -166,70 +159,152 @@ export const WorkspaceListModal = ({
onMoveWorkspace,
}: WorkspaceModalProps) => {
const t = useAFFiNEI18N();
const setOpen = useSetAtom(openDisableCloudAlertModalAtom);
// TODO: AFFiNE Cloud support
const isLoggedIn = false;
const anchorEl = document.getElementById('current-workspace');
return (
<Modal open={open} onClose={onClose}>
<ModalWrapper
width={720}
height={690}
style={{
<Popover
sx={{
color: 'success.main',
zIndex: 999,
'& .MuiPopover-paper': {
borderRadius: '8px',
display: 'flex',
flexDirection: 'column',
}}
>
<StyledModalHeader>
<StyledModalHeaderLeft>
<StyledModalTitle>{t['My Workspaces']()}</StyledModalTitle>
<Tooltip
content={t['Workspace description']()}
placement="top-start"
disablePortal={true}
>
<StyledHelperContainer>
<HelpIcon />
</StyledHelperContainer>
</Tooltip>
</StyledModalHeaderLeft>
<StyledOperationWrapper>
<ModalCloseButton
data-testid="close-workspace-modal"
onClick={() => {
onClose();
}}
absolute={false}
/>
</StyledOperationWrapper>
</StyledModalHeader>
<ScrollableContainer>
<StyledModalContent>
<WorkspaceList
disabled={disabled}
items={
workspaces.filter(
({ flavour }) => flavour !== WorkspaceFlavour.PUBLIC
) as (AffineCloudWorkspace | LocalWorkspace)[]
boxShadow: 'var(--affine-shadow-2)',
backgroundColor: 'var(--affine-background-overlay-panel-color)',
},
maxHeight: '90vh',
}}
open={open}
anchorEl={anchorEl}
onClose={onClose}
>
<StyledModalHeaderContent>
<StyledSignInCardPill>
<MenuItem
style={{
height: 'auto',
padding: '8px 12px',
}}
onClick={async () => {
if (!runtimeConfig.enableCloud) {
setOpen(true);
}
currentWorkspaceId={currentWorkspaceId}
onClick={onClickWorkspace}
onSettingClick={onClickWorkspaceSetting}
onDragEnd={useCallback(
(event: DragEndEvent) => {
const { active, over } = event;
if (active.id !== over?.id) {
onMoveWorkspace(active.id as string, over?.id as string);
}
},
[onMoveWorkspace]
)}
/>
<CreateWorkspaceCard
onNewWorkspace={onNewWorkspace}
onAddWorkspace={onAddWorkspace}
/>
</StyledModalContent>
</ScrollableContainer>
<Footer />
</ModalWrapper>
</Modal>
}}
data-testid="cloud-signin-button"
>
<StyledCreateWorkspaceCardPillContent>
<StyledCreateWorkspaceCardPillIcon>
<CloudWorkspaceIcon />
</StyledCreateWorkspaceCardPillIcon>
<StyledSignInCardPillTextCotainer>
<StyledSignInCardPillTextPrimary>
{t['com.affine.workspace.cloud.auth']()}
</StyledSignInCardPillTextPrimary>
<StyledSignInCardPillTextSecondary>
Sync with AFFiNE Cloud
</StyledSignInCardPillTextSecondary>
</StyledSignInCardPillTextCotainer>
</StyledCreateWorkspaceCardPillContent>
</MenuItem>
</StyledSignInCardPill>
<Divider />
</StyledModalHeaderContent>
<StyledModalBody>
{isLoggedIn ? (
<CloudWorkSpaceList
disabled={disabled}
open={open}
onClose={onClose}
workspaces={workspaces}
onClickWorkspace={onClickWorkspace}
onClickWorkspaceSetting={onClickWorkspaceSetting}
onNewWorkspace={onNewWorkspace}
onAddWorkspace={onAddWorkspace}
currentWorkspaceId={currentWorkspaceId}
onMoveWorkspace={onMoveWorkspace}
/>
) : null}
<StyledModalHeader>
<StyledModalTitle>{t['Local Workspace']()}</StyledModalTitle>
<Tooltip
content={t['Workspace description']()}
placement="top-start"
disablePortal={true}
>
<StyledHelperContainer>
<HelpIcon />
</StyledHelperContainer>
</Tooltip>
</StyledModalHeader>
<StyledModalContent>
<WorkspaceList
disabled={disabled}
items={
workspaces.filter(
({ flavour }) => flavour !== WorkspaceFlavour.PUBLIC
) as (AffineCloudWorkspace | LocalWorkspace)[]
}
currentWorkspaceId={currentWorkspaceId}
onClick={onClickWorkspace}
onSettingClick={onClickWorkspaceSetting}
onDragEnd={useCallback(
(event: DragEndEvent) => {
const { active, over } = event;
if (active.id !== over?.id) {
onMoveWorkspace(active.id as string, over?.id as string);
}
},
[onMoveWorkspace]
)}
/>
</StyledModalContent>
{runtimeConfig.enableSQLiteProvider && environment.isDesktop ? (
<StyledImportWorkspaceCardPill>
<MenuItem
onClick={onAddWorkspace}
data-testid="add-workspace"
style={{
height: 'auto',
padding: '8px 12px',
}}
>
<StyledCreateWorkspaceCardPillContent>
<StyledCreateWorkspaceCardPillIcon>
<ImportIcon />
</StyledCreateWorkspaceCardPillIcon>
<div>
<p>{t['com.affine.workspace.local.import']()}</p>
</div>
</StyledCreateWorkspaceCardPillContent>
</MenuItem>
</StyledImportWorkspaceCardPill>
) : null}
</StyledModalBody>
<StyledModalFooterContent>
<StyledCreateWorkspaceCardPill>
<MenuItem
style={{
height: 'auto',
padding: '8px 12px',
}}
onClick={onNewWorkspace}
data-testid="new-workspace"
>
<StyledCreateWorkspaceCardPillContent>
<StyledCreateWorkspaceCardPillIcon>
<PlusIcon />
</StyledCreateWorkspaceCardPillIcon>
<div>
<p>{t['New Workspace']()}</p>
</div>
</StyledCreateWorkspaceCardPillContent>
</MenuItem>
</StyledCreateWorkspaceCardPill>
</StyledModalFooterContent>
</Popover>
);
};

View File

@@ -81,11 +81,29 @@ export const StyledCreateWorkspaceCardPillContainer = styled('div')(() => {
});
export const StyledCreateWorkspaceCardPill = styled('div')(() => {
return {
borderRadius: '8px',
display: 'flex',
width: '100%',
height: '58px',
border: `1px solid var(--affine-border-color)`,
};
});
export const StyledSignInCardPill = styled('div')(() => {
return {
borderRadius: '8px',
display: 'flex',
width: '100%',
height: '58px',
};
});
export const StyledImportWorkspaceCardPill = styled('div')(() => {
return {
borderRadius: '5px',
display: 'flex',
boxShadow: '0px 0px 6px 0px rgba(0, 0, 0, 0.1)',
background: 'var(--affine-background-primary-color)',
width: '100%',
};
});
@@ -94,7 +112,6 @@ export const StyledCreateWorkspaceCardPillContent = styled('div')(() => {
display: 'flex',
gap: '12px',
alignItems: 'center',
justifyContent: 'space-between',
};
});
@@ -106,13 +123,30 @@ export const StyledCreateWorkspaceCardPillIcon = styled('div')(() => {
};
});
export const StyledCreateWorkspaceCardPillTextSecondary = styled('div')(() => {
export const StyledSignInCardPillTextCotainer = styled('div')(() => {
return {
display: 'flex',
flexDirection: 'column',
};
});
export const StyledSignInCardPillTextSecondary = styled('div')(() => {
return {
fontSize: '12px',
color: 'var(--affine-text-secondary-color)',
};
});
export const StyledSignInCardPillTextPrimary = styled('div')(() => {
return {
fontSize: 'var(--affine-font-base)',
fontWeight: 600,
lineHeight: '24px',
maxWidth: '200px',
...textEllipsis(1),
};
});
export const StyledModalHeaderLeft = styled('div')(() => {
return { ...displayFlex('flex-start', 'center') };
});
@@ -120,6 +154,7 @@ export const StyledModalTitle = styled('div')(() => {
return {
fontWeight: 600,
fontSize: 'var(--affine-font-h6)',
color: 'var(--affine-text-primary-color)',
};
});
@@ -134,11 +169,30 @@ export const StyledHelperContainer = styled('div')(() => {
});
export const StyledModalContent = styled('div')({
height: '540px',
padding: '8px 40px',
...displayFlex('space-between', 'flex-start', 'flex-start'),
flexWrap: 'wrap',
flexDirection: 'column',
width: '100%',
});
export const StyledModalFooterContent = styled('div')({
...displayFlex('space-between', 'flex-start', 'flex-start'),
flexWrap: 'wrap',
flexDirection: 'column',
width: '100%',
padding: '12px',
backgroundColor: 'var(--affine-background-overlay-panel-color)',
});
export const StyledModalHeaderContent = styled('div')({
...displayFlex('space-between', 'flex-start', 'flex-start'),
flexWrap: 'wrap',
flexDirection: 'column',
width: '100%',
padding: '12px 12px 0px 12px',
backgroundColor: 'var(--affine-background-overlay-panel-color)',
});
export const StyledOperationWrapper = styled('div')(() => {
return {
...displayFlex('flex-end', 'center'),
@@ -150,23 +204,34 @@ export const StyleWorkspaceAdd = styled('div')(() => {
width: '58px',
height: '58px',
borderRadius: '100%',
background: 'var(--affine-white-80)',
background: 'var(--affine-background-overlay-panel-color)',
border: '1.5px dashed #f4f5fa',
transition: 'background .2s',
fontSize: '24px',
...displayFlex('center', 'center'),
borderColor: 'var(--affine-white)',
color: 'var(--affine-primary-color)',
color: 'var(--affine-background-overlay-panel-color)',
};
});
export const StyledModalHeader = styled('div')(() => {
return {
width: '100%',
marginTop: '10px',
left: 0,
top: 0,
borderRadius: '24px 24px 0 0',
padding: '10px 40px',
padding: '12px 14px',
...displayFlex('space-between', 'center'),
};
});
export const StyledModalBody = styled('div')(() => {
return {
padding: '0px 12px',
display: 'inline-flex',
flexDirection: 'column',
alignItems: 'flex-start',
gap: '12px',
flex: 1,
overflowY: 'auto',
};
});

View File

@@ -6,7 +6,6 @@ export const StyledSelectorContainer = styled('div')(() => {
display: 'flex',
alignItems: 'center',
padding: '0 6px',
margin: '0 -6px',
borderRadius: '8px',
color: 'var(--affine-text-primary-color)',
':hover': {

View File

@@ -32,6 +32,7 @@ export const WorkspaceSelector = ({
// Open dialog when `Enter` or `Space` pressed
// TODO-Doma Refactor with `@radix-ui/react-dialog` or other libraries that handle these out of the box and be accessible by default
// TODO: Delete this?
const handleKeyDown = useCallback(
(e: React.KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') {
@@ -50,6 +51,7 @@ export const WorkspaceSelector = ({
onClick={onClick}
onKeyDown={handleKeyDown}
data-testid="current-workspace"
id="current-workspace"
>
<WorkspaceAvatar
data-testid="workspace-avatar"

View File

@@ -211,7 +211,7 @@ const CollectionRenderer = ({
}
>
<div data-testid="collection-options" className={styles.more}>
<MoreHorizontalIcon></MoreHorizontalIcon>
<MoreHorizontalIcon />
</div>
</Menu>
}
@@ -228,8 +228,8 @@ const CollectionRenderer = ({
<div>{collection.name}</div>
</div>
</MenuItem>
<Collapsible.Content>
<div style={{ marginLeft: 8 }}>
<Collapsible.Content className={styles.collapsibleContent}>
<div style={{ marginLeft: 20, marginTop: -4 }}>
{pagesToRender.map(page => {
return (
<Page

View File

@@ -182,20 +182,18 @@ export const Page = ({
>
{page.title || t['Untitled']()}
</MenuItem>
<Collapsible.Content>
<div style={{ marginLeft: 8 }}>
{referencesToRender.map(id => {
return (
<ReferencePage
key={id}
workspace={workspace}
pageId={id}
metaMapping={allPageMeta}
parentIds={new Set([pageId])}
/>
);
})}
</div>
<Collapsible.Content className={styles.collapsibleContent}>
{referencesToRender.map(id => {
return (
<ReferencePage
key={id}
workspace={workspace}
pageId={id}
metaMapping={allPageMeta}
parentIds={new Set([pageId])}
/>
);
})}
</Collapsible.Content>
</Collapsible.Root>
);

View File

@@ -1,4 +1,4 @@
import { globalStyle, style } from '@vanilla-extract/css';
import { globalStyle, keyframes, style } from '@vanilla-extract/css';
export const wrapper = style({
userSelect: 'none',
@@ -29,8 +29,9 @@ export const more = style({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 4,
padding: 4,
borderRadius: 2,
fontSize: 16,
color: 'var(--affine-icon-color)',
':hover': {
backgroundColor: 'var(--affine-hover-color)',
},
@@ -52,3 +53,34 @@ export const menuDividerStyle = style({
height: '1px',
background: 'var(--affine-border-color)',
});
const slideDown = keyframes({
'0%': {
height: '0px',
},
'100%': {
height: 'var(--radix-collapsible-content-height)',
},
});
const slideUp = keyframes({
'0%': {
height: 'var(--radix-collapsible-content-height)',
},
'100%': {
height: '0px',
},
});
export const collapsibleContent = style({
overflow: 'hidden',
marginTop: '4px',
selectors: {
'&[data-state="open"]': {
animation: `${slideDown} 0.2s ease-out`,
},
'&[data-state="closed"]': {
animation: `${slideUp} 0.2s ease-out`,
},
},
});

View File

@@ -29,14 +29,8 @@ export const ReferencePage = ({
const icon = setting?.mode === 'edgeless' ? <EdgelessIcon /> : <PageIcon />;
const references = useBlockSuitePageReferences(workspace, pageId);
const referencesToShow = useMemo(() => {
return [
...new Set(
references.filter(
ref => !parentIds.has(ref) && !metaMapping[ref]?.trash
)
),
];
}, [references, parentIds, metaMapping]);
return [...new Set(references.filter(ref => !metaMapping[ref]?.trash))];
}, [references, metaMapping]);
const [collapsed, setCollapsed] = useState(true);
const collapsible = referencesToShow.length > 0;
const nestedItem = parentIds.size > 0;

View File

@@ -11,11 +11,13 @@ export const label = style({
export const favItemWrapper = style({
display: 'flex',
flexDirection: 'column',
gap: '4px',
selectors: {
'&[data-nested="true"]': {
marginLeft: '12px',
width: 'calc(100% - 12px)',
marginLeft: '20px',
width: 'calc(100% - 20px)',
},
'&:not(:first-of-type)': {
marginTop: '4px',
},
},
});
@@ -40,6 +42,7 @@ const slideUp = keyframes({
export const collapsibleContent = style({
overflow: 'hidden',
marginTop: '4px',
selectors: {
'&[data-state="open"]': {
animation: `${slideDown} 0.2s ease-out`,
@@ -53,5 +56,4 @@ export const collapsibleContent = style({
export const collapsibleContentInner = style({
display: 'flex',
flexDirection: 'column',
gap: '4px',
});

View File

@@ -51,14 +51,14 @@ export type RootAppSidebarProps = {
};
const RouteMenuLinkItem = React.forwardRef<
HTMLDivElement,
HTMLButtonElement,
{
currentPath: string; // todo: pass through useRouter?
path: string;
icon: ReactElement;
children?: ReactElement;
isDraggedOver?: boolean;
} & React.HTMLAttributes<HTMLDivElement>
} & React.HTMLAttributes<HTMLButtonElement>
>(({ currentPath, path, icon, children, isDraggedOver, ...props }, ref) => {
// Force active style when a page is dragged over
const active = isDraggedOver || currentPath === path;
@@ -196,6 +196,8 @@ export const RootAppSidebar = ({
</CategoryDivider>
<CollectionsList workspace={blockSuiteWorkspace} />
<CategoryDivider label={t['others']()} />
{/* fixme: remove the following spacer */}
<div style={{ height: '4px' }} />
<RouteMenuLinkItem
ref={trashDroppable.setNodeRef}
isDraggedOver={trashDroppable.isOver}
@@ -211,7 +213,7 @@ export const RootAppSidebar = ({
</SidebarScrollableContainer>
<SidebarContainer>
{isDesktop && <AppUpdaterButton />}
<div />
<div style={{ height: '4px' }} />
<AddPageButton onClick={onClickNewPage} />
</SidebarContainer>
</AppSidebar>

View File

@@ -1,20 +1,24 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useMemo } from 'react';
interface ShortcutTip {
[x: string]: string;
interface ShortcutMap {
[x: string]: string[];
}
export interface ShortcutsInfo {
title: string;
shortcuts: ShortcutMap;
}
export const useWinGeneralKeyboardShortcuts = (): ShortcutTip => {
export const useWinGeneralKeyboardShortcuts = (): ShortcutMap => {
const t = useAFFiNEI18N();
return useMemo(
() => ({
[t['Cancel']()]: 'ESC',
[t['Quick Search']()]: 'Ctrl + K',
[t['New Page']()]: 'Ctrl + N',
[t['Cancel']()]: ['ESC'],
[t['Quick Search']()]: ['Ctrl', 'K'],
[t['New Page']()]: ['Ctrl', 'N'],
// not implement yet
// [t['Append to Daily Note']()]: 'Ctrl + Alt + A',
[t['Expand/Collapse Sidebar']()]: 'Ctrl + /',
[t['Expand/Collapse Sidebar']()]: ['Ctrl', '/'],
// not implement yet
// [t['Go Back']()]: 'Ctrl + [',
// [t['Go Forward']()]: 'Ctrl + ]',
@@ -22,16 +26,16 @@ export const useWinGeneralKeyboardShortcuts = (): ShortcutTip => {
[t]
);
};
export const useMacGeneralKeyboardShortcuts = (): ShortcutTip => {
export const useMacGeneralKeyboardShortcuts = (): ShortcutMap => {
const t = useAFFiNEI18N();
return useMemo(
() => ({
[t['Cancel']()]: 'ESC',
[t['Quick Search']()]: '⌘ + K',
[t['New Page']()]: '⌘ + N',
[t['Cancel']()]: ['ESC'],
[t['Quick Search']()]: ['⌘', 'K'],
[t['New Page']()]: ['⌘', 'N'],
// not implement yet
// [t['Append to Daily Note']()]: '⌘ + ⌥ + A',
[t['Expand/Collapse Sidebar']()]: '⌘ + /',
[t['Expand/Collapse Sidebar']()]: ['⌘', '/'],
// not implement yet
// [t['Go Back']()]: '⌘ + [',
// [t['Go Forward']()]: '⌘ + ]',
@@ -40,28 +44,28 @@ export const useMacGeneralKeyboardShortcuts = (): ShortcutTip => {
);
};
export const useMacEdgelessKeyboardShortcuts = (): ShortcutTip => {
export const useMacEdgelessKeyboardShortcuts = (): ShortcutMap => {
const t = useAFFiNEI18N();
return useMemo(
() => ({
[t['Select All']()]: '⌘ + A',
[t['Undo']()]: '⌘ + Z',
[t['Redo']()]: '⌘ + ⇧ + Z',
[t['Zoom in']()]: '⌘ + +',
[t['Zoom out']()]: '⌘ + -',
[t['Zoom to 100%']()]: '⌘ + 0',
[t['Zoom to fit']()]: '⌘ + 1',
[t['Select']()]: 'V',
[t['Text']()]: 'T',
[t['Shape']()]: 'S',
[t['Image']()]: 'I',
[t['Straight Connector']()]: 'L',
[t['Elbowed Connector']()]: 'X',
[t['Select All']()]: ['⌘', 'A'],
[t['Undo']()]: ['⌘', 'Z'],
[t['Redo']()]: ['⌘', '⇧', 'Z'],
[t['Zoom in']()]: ['⌘', '+'],
[t['Zoom out']()]: ['⌘', '-'],
[t['Zoom to 100%']()]: ['⌘', '0'],
[t['Zoom to fit']()]: ['⌘', '1'],
[t['Select']()]: ['V'],
[t['Text']()]: ['T'],
[t['Shape']()]: ['S'],
[t['Image']()]: ['I'],
[t['Straight Connector']()]: ['L'],
[t['Elbowed Connector']()]: ['X'],
// not implement yet
// [t['Curve Connector']()]: 'C',
[t['Pen']()]: 'P',
[t['Hand']()]: 'H',
[t['Note']()]: 'N',
[t['Pen']()]: ['P'],
[t['Hand']()]: ['H'],
[t['Note']()]: ['N'],
// not implement yet
// [t['Group']()]: '⌘ + G',
// [t['Ungroup']()]: '⌘ + ⇧ + G',
@@ -69,29 +73,29 @@ export const useMacEdgelessKeyboardShortcuts = (): ShortcutTip => {
[t]
);
};
export const useWinEdgelessKeyboardShortcuts = (): ShortcutTip => {
export const useWinEdgelessKeyboardShortcuts = (): ShortcutMap => {
const t = useAFFiNEI18N();
return useMemo(
() => ({
[t['Select All']()]: 'Ctrl + A',
[t['Undo']()]: 'Ctrl + Z',
[t['Redo']()]: 'Ctrl + Y/Ctrl + Shift + Z',
[t['Zoom in']()]: 'Ctrl + +',
[t['Zoom out']()]: 'Ctrl + -',
[t['Zoom to 100%']()]: 'Ctrl + 0',
[t['Zoom to fit']()]: 'Ctrl + 1',
[t['Select']()]: 'V',
[t['Text']()]: 'T',
[t['Shape']()]: 'S',
[t['Image']()]: 'I',
[t['Straight Connector']()]: 'L',
[t['Elbowed Connector']()]: 'X',
[t['Select All']()]: ['Ctrl', 'A'],
[t['Undo']()]: ['Ctrl', 'Z'],
[t['Redo']()]: ['Ctrl', 'Y/Ctrl', 'Shift', 'Z'],
[t['Zoom in']()]: ['Ctrl', '+'],
[t['Zoom out']()]: ['Ctrl', '-'],
[t['Zoom to 100%']()]: ['Ctrl', '0'],
[t['Zoom to fit']()]: ['Ctrl', '1'],
[t['Select']()]: ['V'],
[t['Text']()]: ['T'],
[t['Shape']()]: ['S'],
[t['Image']()]: ['I'],
[t['Straight Connector']()]: ['L'],
[t['Elbowed Connector']()]: ['X'],
// not implement yet
// [t['Curve Connector']()]: 'C',
[t['Pen']()]: 'P',
[t['Hand']()]: 'H',
[t['Note']()]: 'N',
[t['Switch']()]: 'Alt + S',
[t['Pen']()]: ['P'],
[t['Hand']()]: ['H'],
[t['Note']()]: ['N'],
[t['Switch']()]: ['Alt ', ''],
// not implement yet
// [t['Group']()]: 'Ctrl + G',
// [t['Ungroup']()]: 'Ctrl + Shift + G',
@@ -99,31 +103,31 @@ export const useWinEdgelessKeyboardShortcuts = (): ShortcutTip => {
[t]
);
};
export const useMacPageKeyboardShortcuts = (): ShortcutTip => {
export const useMacPageKeyboardShortcuts = (): ShortcutMap => {
const t = useAFFiNEI18N();
return useMemo(
() => ({
[t['Undo']()]: '⌘+Z',
[t['Redo']()]: '⌘+⇧+Z',
[t['Bold']()]: '⌘+B',
[t['Italic']()]: '⌘+I',
[t['Underline']()]: '⌘+U',
[t['Strikethrough']()]: '⌘+⇧+S',
[t['Inline code']()]: ' ⌘+E',
[t['Code block']()]: '⌘+⌥+C',
[t['Link']()]: '⌘+K',
[t['Quick search']()]: '⌘+K',
[t['Body text']()]: '⌘+⌥+0',
[t['Heading']({ number: '1' })]: '⌘+⌥+1',
[t['Heading']({ number: '2' })]: '⌘+⌥+2',
[t['Heading']({ number: '3' })]: '⌘+⌥+3',
[t['Heading']({ number: '4' })]: '⌘+⌥+4',
[t['Heading']({ number: '5' })]: '⌘+⌥+5',
[t['Heading']({ number: '6' })]: '⌘+⌥+6',
[t['Increase indent']()]: 'Tab',
[t['Reduce indent']()]: '⇧+Tab',
[t['Group as Database']()]: '⌘ + G',
[t['Switch']()]: '⌥ + S',
[t['Undo']()]: ['⌘', 'Z'],
[t['Redo']()]: ['⌘', '⇧', 'Z'],
[t['Bold']()]: ['⌘', 'B'],
[t['Italic']()]: ['⌘', 'I'],
[t['Underline']()]: ['⌘', 'U'],
[t['Strikethrough']()]: ['⌘', '⇧', 'S'],
[t['Inline code']()]: ['⌘', 'E'],
[t['Code block']()]: ['⌘', '⌥', 'C'],
[t['Link']()]: ['⌘', 'K'],
[t['Quick search']()]: ['⌘', 'K'],
[t['Body text']()]: ['⌘', '⌥', '0'],
[t['Heading']({ number: '1' })]: ['⌘', '⌥', '1'],
[t['Heading']({ number: '2' })]: ['⌘', '⌥', '2'],
[t['Heading']({ number: '3' })]: ['⌘', '⌥', '3'],
[t['Heading']({ number: '4' })]: ['⌘', '⌥', '4'],
[t['Heading']({ number: '5' })]: ['⌘', '⌥', '5'],
[t['Heading']({ number: '6' })]: ['⌘', '⌥', '6'],
[t['Increase indent']()]: ['Tab'],
[t['Reduce indent']()]: ['⇧', 'Tab'],
[t['Group as Database']()]: ['⌘', 'G'],
[t['Switch']()]: ['⌥', 'S'],
// not implement yet
// [t['Move Up']()]: '⌘ + ⌥ + ↑',
// [t['Move Down']()]: '⌘ + ⌥ + ↓',
@@ -132,53 +136,53 @@ export const useMacPageKeyboardShortcuts = (): ShortcutTip => {
);
};
export const useMacMarkdownShortcuts = (): ShortcutTip => {
export const useMacMarkdownShortcuts = (): ShortcutMap => {
const t = useAFFiNEI18N();
return useMemo(
() => ({
[t['Bold']()]: '**Text** ',
[t['Italic']()]: '*Text* ',
[t['Underline']()]: '~Text~ ',
[t['Strikethrough']()]: '~~Text~~ ',
[t['Divider']()]: '***',
[t['Inline code']()]: '`Text` ',
[t['Code block']()]: '``` Space',
[t['Heading']({ number: '1' })]: '# Text',
[t['Heading']({ number: '2' })]: '## Text',
[t['Heading']({ number: '3' })]: '### Text',
[t['Heading']({ number: '4' })]: '#### Text',
[t['Heading']({ number: '5' })]: '##### Text',
[t['Heading']({ number: '6' })]: '###### Text',
[t['Bold']()]: ['**Text**'],
[t['Italic']()]: ['*Text*'],
[t['Underline']()]: ['~Text~'],
[t['Strikethrough']()]: ['~~Text~~'],
[t['Divider']()]: ['***'],
[t['Inline code']()]: ['`Text` '],
[t['Code block']()]: ['``` Space'],
[t['Heading']({ number: '1' })]: ['# Text'],
[t['Heading']({ number: '2' })]: ['## Text'],
[t['Heading']({ number: '3' })]: ['### Text'],
[t['Heading']({ number: '4' })]: ['#### Text'],
[t['Heading']({ number: '5' })]: ['##### Text'],
[t['Heading']({ number: '6' })]: ['###### Text'],
}),
[t]
);
};
export const useWinPageKeyboardShortcuts = (): ShortcutTip => {
export const useWinPageKeyboardShortcuts = (): ShortcutMap => {
const t = useAFFiNEI18N();
return useMemo(
() => ({
[t['Undo']()]: 'Ctrl+Z',
[t['Redo']()]: 'Ctrl+Y',
[t['Bold']()]: 'Ctrl+B',
[t['Italic']()]: 'Ctrl+I',
[t['Underline']()]: 'Ctrl+U',
[t['Strikethrough']()]: 'Ctrl+Shift+S',
[t['Inline code']()]: ' Ctrl+E',
[t['Code block']()]: 'Ctrl+Alt+C',
[t['Link']()]: 'Ctrl+K',
[t['Quick search']()]: 'Ctrl+K',
[t['Body text']()]: 'Ctrl+Shift+0',
[t['Heading']({ number: '1' })]: 'Ctrl+Shift+1',
[t['Heading']({ number: '2' })]: 'Ctrl+Shift+2',
[t['Heading']({ number: '3' })]: 'Ctrl+Shift+3',
[t['Heading']({ number: '4' })]: 'Ctrl+Shift+4',
[t['Heading']({ number: '5' })]: 'Ctrl+Shift+5',
[t['Heading']({ number: '6' })]: 'Ctrl+Shift+6',
[t['Increase indent']()]: 'Tab',
[t['Reduce indent']()]: 'Shift+Tab',
[t['Group as Database']()]: 'Ctrl + G',
['Switch']: 'Alt + S',
[t['Undo']()]: ['Ctrl', 'Z'],
[t['Redo']()]: ['Ctrl', 'Y'],
[t['Bold']()]: ['Ctrl', 'B'],
[t['Italic']()]: ['Ctrl', 'I'],
[t['Underline']()]: ['Ctrl', 'U'],
[t['Strikethrough']()]: ['Ctrl', 'Shift', 'S'],
[t['Inline code']()]: [' Ctrl', 'E'],
[t['Code block']()]: ['Ctrl', 'Alt', 'C'],
[t['Link']()]: ['Ctr', 'K'],
[t['Quick search']()]: ['Ctrl', 'K'],
[t['Body text']()]: ['Ctrl', 'Shift', '0'],
[t['Heading']({ number: '1' })]: ['Ctrl', 'Shift', '1'],
[t['Heading']({ number: '2' })]: ['Ctrl', 'Shift', '2'],
[t['Heading']({ number: '3' })]: ['Ctrl', 'Shift', '3'],
[t['Heading']({ number: '4' })]: ['Ctrl', 'Shift', '4'],
[t['Heading']({ number: '5' })]: ['Ctrl', 'Shift', '5'],
[t['Heading']({ number: '6' })]: ['Ctrl', 'Shift', '6'],
[t['Increase indent']()]: ['Tab'],
[t['Reduce indent']()]: ['Shift+Tab'],
[t['Group as Database']()]: ['Ctrl + G'],
['Switch']: ['Alt + S'],
// not implement yet
// [t['Move Up']()]: 'Ctrl + Alt + ↑',
// [t['Move Down']()]: 'Ctrl + Alt + ↓',
@@ -186,54 +190,73 @@ export const useWinPageKeyboardShortcuts = (): ShortcutTip => {
[t]
);
};
export const useWinMarkdownShortcuts = (): ShortcutTip => {
export const useWinMarkdownShortcuts = (): ShortcutMap => {
const t = useAFFiNEI18N();
return useMemo(
() => ({
[t['Bold']()]: '**Text** ',
[t['Italic']()]: '*Text* ',
[t['Underline']()]: '~Text~ ',
[t['Strikethrough']()]: '~~Text~~ ',
[t['Divider']()]: '***',
[t['Inline code']()]: '`Text` ',
[t['Code block']()]: '``` Text',
[t['Heading']({ number: '1' })]: '# Text',
[t['Heading']({ number: '2' })]: '## Text',
[t['Heading']({ number: '3' })]: '### Text',
[t['Heading']({ number: '4' })]: '#### Text',
[t['Heading']({ number: '5' })]: '##### Text',
[t['Heading']({ number: '6' })]: '###### Text',
[t['Bold']()]: ['**Text** '],
[t['Italic']()]: ['*Text* '],
[t['Underline']()]: ['~Text~ '],
[t['Strikethrough']()]: ['~~Text~~ '],
[t['Divider']()]: ['***'],
[t['Inline code']()]: ['`Text` '],
[t['Code block']()]: ['``` Text'],
[t['Heading']({ number: '1' })]: ['# Text'],
[t['Heading']({ number: '2' })]: ['## Text'],
[t['Heading']({ number: '3' })]: ['### Text'],
[t['Heading']({ number: '4' })]: ['#### Text'],
[t['Heading']({ number: '5' })]: ['##### Text'],
[t['Heading']({ number: '6' })]: ['###### Text'],
}),
[t]
);
};
export const useMarkdownShortcuts = (): ShortcutTip => {
export const useMarkdownShortcuts = (): ShortcutsInfo => {
const t = useAFFiNEI18N();
const macMarkdownShortcuts = useMacMarkdownShortcuts();
const winMarkdownShortcuts = useWinMarkdownShortcuts();
const isMac = environment.isBrowser && environment.isMacOs;
return isMac ? macMarkdownShortcuts : winMarkdownShortcuts;
return {
title: t['Markdown Syntax'](),
shortcuts: isMac ? macMarkdownShortcuts : winMarkdownShortcuts,
};
};
export const usePageShortcuts = (): ShortcutTip => {
export const usePageShortcuts = (): ShortcutsInfo => {
const t = useAFFiNEI18N();
const macPageShortcuts = useMacPageKeyboardShortcuts();
const winPageShortcuts = useWinPageKeyboardShortcuts();
const isMac = environment.isBrowser && environment.isMacOs;
return isMac ? macPageShortcuts : winPageShortcuts;
return {
title: t['Page'](),
shortcuts: isMac ? macPageShortcuts : winPageShortcuts,
};
};
export const useEdgelessShortcuts = (): ShortcutTip => {
export const useEdgelessShortcuts = (): ShortcutsInfo => {
const t = useAFFiNEI18N();
const macEdgelessShortcuts = useMacEdgelessKeyboardShortcuts();
const winEdgelessShortcuts = useWinEdgelessKeyboardShortcuts();
const isMac = environment.isBrowser && environment.isMacOs;
return isMac ? macEdgelessShortcuts : winEdgelessShortcuts;
return {
title: t['Edgeless'](),
shortcuts: isMac ? macEdgelessShortcuts : winEdgelessShortcuts,
};
};
export const useGeneralShortcuts = (): ShortcutTip => {
export const useGeneralShortcuts = (): ShortcutsInfo => {
const t = useAFFiNEI18N();
const macGeneralShortcuts = useMacGeneralKeyboardShortcuts();
const winGeneralShortcuts = useWinGeneralKeyboardShortcuts();
const isMac = environment.isBrowser && environment.isMacOs;
return isMac ? macGeneralShortcuts : winGeneralShortcuts;
return {
title: t['General'](),
shortcuts: isMac ? macGeneralShortcuts : winGeneralShortcuts,
};
};

View File

@@ -5,12 +5,14 @@ import { saveWorkspaceToLocalStorage } from '@affine/workspace/local/crud';
import { getOrCreateWorkspace } from '@affine/workspace/manager';
import { nanoid } from '@blocksuite/store';
import { getWorkspace } from '@toeverything/infra/__internal__/workspace';
import { rootStore } from '@toeverything/infra/atom';
import { buildShowcaseWorkspace } from '@toeverything/infra/blocksuite';
import { useAtomValue, useSetAtom } from 'jotai';
import { useCallback } from 'react';
import { LocalAdapter } from '../adapters/local';
import { WorkspaceAdapters } from '../adapters/workspace';
import { setPageModeAtom } from '../atoms';
const logger = new DebugLogger('use-workspaces');
@@ -52,7 +54,12 @@ export function useAppHelper() {
id,
WorkspaceFlavour.LOCAL
);
await buildShowcaseWorkspace(blockSuiteWorkspace);
await buildShowcaseWorkspace(blockSuiteWorkspace, {
store: rootStore,
atoms: {
pageMode: setPageModeAtom,
},
});
}
set(workspaces => [
...workspaces,

View File

@@ -126,6 +126,7 @@ export const AllWorkspaceModals = (): ReactElement => {
);
const setCurrentPageId = useSetAtom(currentPageIdAtom);
const [isPending, startTransition] = useTransition();
const [, startCloseTransition] = useTransition();
const [, setOpenSettingModalAtom] = useAtom(openSettingModalAtom);
const handleOpenSettingModal = useCallback(
@@ -153,7 +154,9 @@ export const AllWorkspaceModals = (): ReactElement => {
isOpenCreateWorkspaceModal === false
}
onClose={useCallback(() => {
setOpenWorkspacesModal(false);
startCloseTransition(() => {
setOpenWorkspacesModal(false);
});
}, [setOpenWorkspacesModal])}
onMoveWorkspace={useCallback(
(activeId, overId) => {
@@ -169,10 +172,12 @@ export const AllWorkspaceModals = (): ReactElement => {
)}
onClickWorkspace={useCallback(
workspaceId => {
setOpenWorkspacesModal(false);
setCurrentWorkspaceId(workspaceId);
setCurrentPageId(null);
jumpToSubPath(workspaceId, WorkspaceSubPath.ALL);
startCloseTransition(() => {
setOpenWorkspacesModal(false);
setCurrentWorkspaceId(workspaceId);
setCurrentPageId(null);
jumpToSubPath(workspaceId, WorkspaceSubPath.ALL);
});
},
[
jumpToSubPath,

View File

@@ -1,41 +1,41 @@
import type { RouteObject } from 'react-router-dom';
import { createBrowserRouter } from 'react-router-dom';
export const router = createBrowserRouter(
[
{
path: '/',
lazy: () => import('./pages/index'),
},
{
path: '/workspace/:workspaceId',
lazy: () => import('./pages/workspace/index'),
children: [
{
path: 'all',
lazy: () => import('./pages/workspace/all-page'),
},
{
path: 'trash',
lazy: () => import('./pages/workspace/trash-page'),
},
{
path: ':pageId',
lazy: () => import('./pages/workspace/detail-page'),
},
],
},
{
path: '/404',
lazy: () => import('./pages/404'),
},
{
path: '*',
lazy: () => import('./pages/404'),
},
],
export const routes = [
{
future: {
v7_normalizeFormMethod: true,
},
}
);
path: '/',
lazy: () => import('./pages/index'),
},
{
path: '/workspace/:workspaceId',
lazy: () => import('./pages/workspace/index'),
children: [
{
path: 'all',
lazy: () => import('./pages/workspace/all-page'),
},
{
path: 'trash',
lazy: () => import('./pages/workspace/trash-page'),
},
{
path: ':pageId',
lazy: () => import('./pages/workspace/detail-page'),
},
],
},
{
path: '/404',
lazy: () => import('./pages/404'),
},
{
path: '*',
lazy: () => import('./pages/404'),
},
] satisfies [RouteObject, ...RouteObject[]];
export const router = createBrowserRouter(routes, {
future: {
v7_normalizeFormMethod: true,
},
});

View File

@@ -5,7 +5,7 @@
"typeRoots": ["../../node_modules", "../../node_modules/@types"],
"types": ["webpack-env", "ses", "affine__env"]
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.json"],
"exclude": ["node_modules"],
"references": [
{

View File

@@ -1,6 +1,6 @@
{
"name": "@affine/docs",
"version": "0.8.0-canary.22",
"version": "0.8.0-canary.26",
"type": "module",
"private": true,
"scripts": {
@@ -10,12 +10,12 @@
},
"dependencies": {
"@affine/component": "workspace:*",
"@blocksuite/block-std": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/blocks": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/editor": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/global": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/lit": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/store": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/block-std": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/blocks": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/editor": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/global": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/lit": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/store": "0.0.0-20230818021533-7e342436-nightly",
"express": "^4.18.2",
"jotai": "^2.3.1",
"react": "18.3.0-canary-7118f5dd7-20230705",
@@ -28,7 +28,7 @@
"@types/react-dom": "^18.2.7",
"@vanilla-extract/css": "^1.12.0",
"@vanilla-extract/vite-plugin": "^3.8.2",
"autoprefixer": "^10.4.14",
"autoprefixer": "^10.4.15",
"tailwindcss": "^3.3.3",
"typescript": "^5.1.6"
}

View File

@@ -117,7 +117,7 @@ test('affine cloud disabled', async ({ page }) => {
});
await page.waitForSelector('v-line');
await page.getByTestId('current-workspace').click();
await page.getByTestId('sign-in-button').click();
await page.getByTestId('cloud-signin-button').click();
await page.getByTestId('disable-affine-cloud-modal').waitFor({
state: 'visible',
});
@@ -153,13 +153,23 @@ test('windows only check', async ({ page }) => {
test('delete workspace', async ({ page }) => {
await page.getByTestId('current-workspace').click();
await page.getByTestId('add-or-new-workspace').click();
await page.getByTestId('new-workspace').click();
await page.getByTestId('create-workspace-default-location-button').click();
await page.getByTestId('create-workspace-input').type('Delete Me');
await page.getByTestId('create-workspace-create-button').click();
await page.getByTestId('create-workspace-continue-button').click();
await page.getByTestId('slider-bar-workspace-setting-button').click();
await page.getByTestId('create-workspace-default-location-button').click({
delay: 100,
});
await page.getByTestId('create-workspace-input').type('Delete Me', {
delay: 100,
});
await page.getByTestId('create-workspace-create-button').click({
delay: 100,
});
await page.getByTestId('create-workspace-continue-button').click({
delay: 100,
});
await page.waitForTimeout(1000);
await page.getByTestId('current-workspace').click();
await page.getByTestId('workspace-card').nth(1).hover();
await page.getByTestId('workspace-card-setting-button').nth(1).click();
await page.getByTestId('current-workspace-label').click();
expect(await page.getByTestId('workspace-name-input').inputValue()).toBe(
'Delete Me'

View File

@@ -130,6 +130,7 @@ module.exports = {
: undefined,
// We need the following line for updater
extraResource: ['./resources/app-update.yml'],
ignore: ['e2e', 'tests'],
},
makers,
hooks: {

View File

@@ -1,7 +1,7 @@
{
"name": "@affine/electron",
"private": true,
"version": "0.8.0-canary.22",
"version": "0.8.0-canary.26",
"author": "affine",
"repository": {
"url": "https://github.com/toeverything/AFFiNE",
@@ -28,10 +28,10 @@
"@affine/env": "workspace:*",
"@affine/native": "workspace:*",
"@affine/sdk": "workspace:*",
"@blocksuite/blocks": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/editor": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/lit": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/store": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/blocks": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/editor": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/lit": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/store": "0.0.0-20230818021533-7e342436-nightly",
"@electron-forge/cli": "^6.3.0",
"@electron-forge/core": "^6.3.0",
"@electron-forge/core-utils": "^6.3.0",
@@ -44,11 +44,11 @@
"@types/fs-extra": "^11.0.1",
"@types/uuid": "^9.0.2",
"cross-env": "7.0.3",
"electron": "^25.5.0",
"electron": "^26.0.0",
"electron-log": "^5.0.0-beta.25",
"electron-squirrel-startup": "1.0.0",
"electron-window-state": "^5.0.3",
"esbuild": "^0.18.20",
"esbuild": "^0.19.2",
"fs-extra": "^11.1.1",
"jotai": "^2.3.1",
"ts-node": "^10.9.1",
@@ -62,7 +62,7 @@
"@toeverything/infra": "workspace:*",
"async-call-rpc": "^6.3.1",
"electron-updater": "^6.1.4",
"link-preview-js": "^3.0.4",
"link-preview-js": "^3.0.5",
"lodash-es": "^4.17.21",
"nanoid": "^4.0.2",
"rxjs": "^7.8.1",

View File

@@ -1,12 +1,10 @@
/* eslint-disable no-async-promise-executor */
import { spawn } from 'node:child_process';
import { readFileSync } from 'node:fs';
import path from 'node:path';
import electronPath from 'electron';
import * as esbuild from 'esbuild';
import { config, electronDir } from './common.mjs';
import { config } from './common.mjs';
// this means we don't spawn electron windows, mainly for testing
const watchMode = process.argv.includes('--watch');
@@ -19,20 +17,6 @@ const stderrFilterPatterns = [
/ExtensionLoadWarning/,
];
// these are set before calling `config`, so we have a chance to override them
try {
const devJson = readFileSync(
path.resolve(electronDir, './dev.json'),
'utf-8'
);
const devEnv = JSON.parse(devJson);
Object.assign(process.env, devEnv);
} catch (err) {
console.warn(
`Could not read dev.json. Some functions may not work as expected.`
);
}
/** @type {ChildProcessWithoutNullStreams | null} */
let spawnProcess = null;

View File

@@ -91,7 +91,8 @@ test('db should be removed in db$Map after destroyed', async () => {
expect(db$Map.has(workspaceId)).toBe(false);
});
test('if db has a secondary db path, we should also poll that', async () => {
// we have removed secondary db feature
test.skip('if db has a secondary db path, we should also poll that', async () => {
const { ensureSQLiteDB } = await import('../ensure-db');
const { storeWorkspaceMeta } = await import('../../workspace');
const workspaceId = v4();

View File

@@ -44,10 +44,10 @@ export abstract class BaseSQLiteAdapter {
try {
if (!this.db) {
logger.warn(`${this.path} is not connected`);
return;
return null;
}
const blob = await this.db.getBlob(key);
return blob?.data;
return blob?.data ?? null;
} catch (error) {
logger.error('getBlob', error);
return null;

View File

@@ -128,7 +128,7 @@ export class WorkspaceSQLiteDB extends BaseSQLiteAdapter {
if (doc) {
return encodeStateAsUpdate(doc);
}
return null;
return false;
};
// non-blocking and use yDoc to validate the update

View File

@@ -1,12 +1,24 @@
import type {
DBHandlers,
DialogHandlers,
WorkspaceHandlers,
} from '@toeverything/infra/type';
import { dbEvents, dbHandlers } from './db';
import { dialogHandlers } from './dialog';
import { workspaceEvents, workspaceHandlers } from './workspace';
type AllHandlers = {
db: DBHandlers;
workspace: WorkspaceHandlers;
dialog: DialogHandlers;
};
export const handlers = {
db: dbHandlers,
workspace: workspaceHandlers,
dialog: dialogHandlers,
};
} satisfies AllHandlers;
export const events = {
db: dbEvents,

View File

@@ -34,7 +34,7 @@ async function createWindow() {
: isWindows()
? 'hidden'
: 'default',
trafficLightPosition: { x: 24, y: 18 },
trafficLightPosition: { x: 20, y: 18 },
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,

View File

@@ -1,7 +1,7 @@
{
"name": "@affine/prototype",
"private": true,
"version": "0.8.0-canary.22",
"version": "0.8.0-canary.26",
"type": "module",
"scripts": {
"dev": "vite --host --port 3003",
@@ -18,13 +18,13 @@
"@affine/jotai": "workspace:*",
"@affine/templates": "workspace:*",
"@affine/workspace": "workspace:*",
"@blocksuite/block-std": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/blocks": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/editor": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/global": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/block-std": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/blocks": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/editor": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/global": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/icons": "^2.1.31",
"@blocksuite/lit": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/store": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/lit": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/store": "0.0.0-20230818021533-7e342436-nightly",
"@toeverything/hooks": "workspace:*",
"@toeverything/y-indexeddb": "workspace:*",
"react": "^18.2.0",

View File

@@ -1,7 +1,7 @@
{
"name": "@affine/server",
"private": true,
"version": "0.8.0-canary.22",
"version": "0.8.0-canary.26",
"description": "Affine Node.js server",
"type": "module",
"bin": {
@@ -18,7 +18,7 @@
"dependencies": {
"@apollo/server": "^4.9.1",
"@auth/prisma-adapter": "^1.0.1",
"@aws-sdk/client-s3": "^3.388.0",
"@aws-sdk/client-s3": "^3.391.0",
"@nestjs/apollo": "^12.0.7",
"@nestjs/common": "^10.1.3",
"@nestjs/core": "^10.1.3",
@@ -31,11 +31,11 @@
"cookie-parser": "^1.4.6",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"graphql": "^16.7.1",
"graphql": "^16.8.0",
"graphql-type-json": "^0.3.2",
"graphql-upload": "^16.0.2",
"lodash-es": "^4.17.21",
"next-auth": "4.22.1",
"next-auth": "4.22.5",
"nodemailer": "^6.9.4",
"parse-duration": "^1.1.0",
"prisma": "^5.1.1",
@@ -50,7 +50,7 @@
"@types/cookie-parser": "^1.4.3",
"@types/express": "^4.17.17",
"@types/lodash-es": "^4.17.8",
"@types/node": "^18.17.4",
"@types/node": "^18.17.5",
"@types/nodemailer": "^6.4.9",
"@types/supertest": "^2.0.12",
"c8": "^8.0.1",

View File

@@ -5,6 +5,7 @@ import { mergeConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
import { getRuntimeConfig } from '../../core/.webpack/runtime-config';
import turbosnap from 'vite-plugin-turbosnap';
runCli(
{
@@ -28,11 +29,12 @@ export default {
'@storybook/addon-interactions',
'@storybook/addon-storysource',
'storybook-dark-mode',
'storybook-addon-react-router-v6',
],
framework: {
name: '@storybook/react-vite',
},
async viteFinal(config, _) {
async viteFinal(config, { configType }) {
return mergeConfig(config, {
assetsInclude: ['**/*.md'],
plugins: [
@@ -40,6 +42,11 @@ export default {
tsconfigPaths({
root: fileURLToPath(new URL('../../../', import.meta.url)),
}),
configType === 'PRODUCTION'
? turbosnap({
rootDir: fileURLToPath(new URL('../../../', import.meta.url)),
})
: null,
],
define: {
'process.env': {},

View File

@@ -1,13 +1,17 @@
import 'ses';
import '@affine/component/theme/global.css';
import '@affine/component/theme/theme.css';
import { LOCALES, createI18n } from '@affine/i18n';
import '@toeverything/components/style.css';
import { createI18n } from '@affine/i18n';
import { ThemeProvider, useTheme } from 'next-themes';
import { setupGlobal } from '@affine/env/global';
import type { ComponentType } from 'react';
import { useEffect } from 'react';
import { useDarkMode } from 'storybook-dark-mode';
import { setup } from '@affine/core/bootstrap/setup';
import { AffineContext } from '@affine/component/context';
import { use } from 'foxact/use';
import useSWR from 'swr';
import type { Decorator } from '@storybook/react';
setupGlobal();
const setupPromise = setup();
export const parameters = {
backgrounds: { disable: true },
@@ -20,51 +24,42 @@ export const parameters = {
},
};
export const globalTypes = {
locale: {
name: 'Locale',
description: 'Internationalization locale',
defaultValue: 'en',
toolbar: {
icon: 'globe',
items: LOCALES.map(locale => ({
title: locale.originalName,
value: locale.tag,
right: locale.flagEmoji,
})),
const i18n = createI18n();
const withI18n: Decorator = (Story, context) => {
const locale = context.globals.locale;
useSWR(
locale,
async () => {
await i18n.changeLanguage(locale);
},
},
{
suspense: true,
}
);
return <Story {...context} />;
};
const createI18nDecorator = () => {
const i18n = createI18n();
const withI18n = (Story: any, context: any) => {
const locale = context.globals.locale;
useEffect(() => {
i18n.changeLanguage(locale);
}, [locale]);
return <Story {...context} />;
};
return withI18n;
};
const Component = () => {
const ThemeChange = () => {
const isDark = useDarkMode();
const theme = useTheme();
useEffect(() => {
theme.setTheme(isDark ? 'dark' : 'light');
}, [isDark]);
if (theme.resolvedTheme === 'dark' && !isDark) {
theme.setTheme('light');
} else if (theme.resolvedTheme === 'light' && isDark) {
theme.setTheme('dark');
}
return null;
};
export const decorators = [
(Story: ComponentType) => {
return (
<ThemeProvider>
<Component />
<Story />
</ThemeProvider>
);
},
createI18nDecorator(),
];
const withContextDecorator: Decorator = (Story, context) => {
use(setupPromise);
return (
<ThemeProvider>
<AffineContext>
<ThemeChange />
<Story {...context} />
</AffineContext>
</ThemeProvider>
);
};
export const decorators = [withContextDecorator, withI18n];

1
apps/storybook/README.md Normal file
View File

@@ -0,0 +1 @@
# Storybook

View File

@@ -3,42 +3,46 @@
"private": true,
"scripts": {
"dev": "storybook dev -p 6006",
"build": "NODE_OPTIONS=--max_old_space_size=4096 storybook build",
"build": "storybook build",
"test": "test-storybook"
},
"dependencies": {
"@affine/component": "workspace:*",
"@affine/i18n": "workspace:*",
"@storybook/addon-actions": "^7.2.3",
"@storybook/addon-essentials": "^7.2.3",
"@storybook/addon-interactions": "^7.2.3",
"@storybook/addon-links": "^7.2.3",
"@storybook/addon-storysource": "^7.2.3",
"@storybook/blocks": "^7.2.3",
"@storybook/builder-vite": "^7.2.3",
"@storybook/addon-actions": "^7.3.1",
"@storybook/addon-essentials": "^7.3.1",
"@storybook/addon-interactions": "^7.3.1",
"@storybook/addon-links": "^7.3.1",
"@storybook/addon-storysource": "^7.3.1",
"@storybook/blocks": "^7.3.1",
"@storybook/builder-vite": "^7.3.1",
"@storybook/jest": "^0.1.0",
"@storybook/react": "^7.2.3",
"@storybook/react-vite": "^7.2.3",
"@storybook/react": "^7.3.1",
"@storybook/react-vite": "^7.3.1",
"@storybook/test-runner": "^0.13.0",
"@storybook/testing-library": "^0.2.0",
"@vitejs/plugin-react": "^4.0.4",
"concurrently": "^8.2.0",
"jest-mock": "^29.6.2",
"serve": "^14.2.0",
"storybook": "^7.2.3",
"ses": "^0.18.7",
"storybook": "^7.3.1",
"storybook-dark-mode": "^3.0.1",
"wait-on": "^7.0.1"
},
"devDependencies": {
"@blocksuite/block-std": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/blocks": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/editor": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/global": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/block-std": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/blocks": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/editor": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/global": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/icons": "^2.1.31",
"@blocksuite/lit": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/store": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/lit": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/store": "0.0.0-20230818021533-7e342436-nightly",
"chromatic": "^6.22.0",
"react": "18.2.0",
"react-dom": "18.2.0"
"react-dom": "18.2.0",
"storybook-addon-react-router-v6": "^2.0.4",
"vite-plugin-turbosnap": "^1.0.2"
},
"peerDependencies": {
"@blocksuite/blocks": "*",
@@ -48,5 +52,5 @@
"@blocksuite/lit": "*",
"@blocksuite/store": "*"
},
"version": "0.8.0-canary.22"
"version": "0.8.0-canary.26"
}

View File

@@ -1,92 +0,0 @@
/* deepscan-disable USELESS_ARROW_FUNC_BIND */
import { BlockHubWrapper } from '@affine/component/block-hub';
import type { EditorProps } from '@affine/component/block-suite-editor';
import { BlockSuiteEditor } from '@affine/component/block-suite-editor';
import { rootBlockHubAtom } from '@affine/workspace/atom';
import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
import type { EditorContainer } from '@blocksuite/editor';
import type { Page } from '@blocksuite/store';
import { createMemoryStorage, Schema, Workspace } from '@blocksuite/store';
import { expect } from '@storybook/jest';
import type { Meta, StoryFn } from '@storybook/react';
import { use } from 'foxact/use';
const schema = new Schema();
schema.register(AffineSchemas).register(__unstableSchemas);
const blockSuiteWorkspace = new Workspace({
id: 'test',
blobStorages: [createMemoryStorage],
schema,
});
async function initPage(page: Page) {
await page.waitForLoaded();
// Add page block and surface block at root level
const pageBlockId = page.addBlock('affine:page', {
title: new page.Text('Hello, world!'),
});
page.addBlock('affine:surface', {}, pageBlockId);
const frameId = page.addBlock('affine:note', {}, pageBlockId);
page.addBlock(
'affine:paragraph',
{
text: new page.Text('This is a paragraph.'),
},
frameId
);
page.resetHistory();
}
const page = blockSuiteWorkspace.createPage('page0');
type BlockSuiteMeta = Meta<typeof BlockSuiteEditor>;
export default {
title: 'BlockSuite/Editor',
component: BlockSuiteEditor,
} satisfies BlockSuiteMeta;
const Template: StoryFn<EditorProps> = (props: Partial<EditorProps>) => {
if (!page.loaded) {
use(initPage(page));
}
return (
<div
style={{
height: '100vh',
width: '100vw',
overflow: 'auto',
}}
>
<BlockSuiteEditor onInit={initPage} page={page} mode="page" {...props} />
<BlockHubWrapper
style={{
position: 'absolute',
right: 12,
bottom: 12,
}}
blockHubAtom={rootBlockHubAtom}
/>
</div>
);
};
export const Empty = Template.bind({});
Empty.play = async ({ canvasElement }) => {
await new Promise<void>(resolve => {
setTimeout(() => resolve(), 500);
});
const editorContainer = canvasElement.querySelector(
'[data-testid="editor-page0"]'
) as HTMLDivElement;
expect(editorContainer).not.toBeNull();
const editor = editorContainer.querySelector(
'editor-container'
) as EditorContainer;
expect(editor).not.toBeNull();
};
Empty.args = {
mode: 'page',
};

View File

@@ -7,6 +7,9 @@ import { within } from '@storybook/testing-library';
export default {
title: 'AFFiNE/Breadcrumbs',
component: Breadcrumbs,
parameters: {
chromatic: { disableSnapshot: true },
},
} as Meta<typeof Breadcrumbs>;
const Template: StoryFn = args => <Breadcrumbs {...args} />;

View File

@@ -9,11 +9,15 @@ import {
HelpIcon,
PageIcon,
} from '@blocksuite/icons';
import type { Meta } from '@storybook/react';
export default {
title: 'AFFiNE/Card',
component: WorkspaceCard,
};
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta;
const blockSuiteWorkspace = getOrCreateWorkspace(
'blocksuite-local',

View File

@@ -0,0 +1,110 @@
import { pluginRegisterPromise } from '@affine/core/bootstrap/register-plugins';
import { routes } from '@affine/core/router';
import { assertExists } from '@blocksuite/global/utils';
import type { Decorator, StoryFn } from '@storybook/react';
import { userEvent, waitFor } from '@storybook/testing-library';
import { use } from 'foxact/use';
import { Outlet, useLocation } from 'react-router-dom';
import {
reactRouterOutlets,
reactRouterParameters,
withRouter,
} from 'storybook-addon-react-router-v6';
const withCleanLocalStorage: Decorator = (Story, context) => {
localStorage.clear();
return <Story {...context} />;
};
const FakeApp = () => {
const location = useLocation();
// fixme: `key` is a hack to force the storybook to re-render the outlet
return <Outlet key={location.pathname} />;
};
export default {
title: 'Preview/Core',
parameters: {
chromatic: { disableSnapshot: false },
},
};
export const Index: StoryFn = () => {
use(pluginRegisterPromise);
return <FakeApp />;
};
Index.decorators = [withRouter, withCleanLocalStorage];
Index.parameters = {
reactRouter: reactRouterParameters({
routing: reactRouterOutlets(routes),
}),
};
export const SettingPage: StoryFn = () => {
return <FakeApp />;
};
SettingPage.play = async ({ canvasElement }) => {
await waitFor(
() => {
assertExists(
canvasElement.querySelector('[data-testid="settings-modal-trigger"]')
);
},
{
timeout: 5000,
}
);
const settingModalBtn = canvasElement.querySelector(
'[data-testid="settings-modal-trigger"]'
) as Element;
await userEvent.click(settingModalBtn);
};
SettingPage.decorators = [withRouter, withCleanLocalStorage];
SettingPage.parameters = {
reactRouter: reactRouterParameters({
routing: reactRouterOutlets(routes),
}),
};
export const NotFoundPage: StoryFn = () => {
return <FakeApp />;
};
NotFoundPage.decorators = [withRouter, withCleanLocalStorage];
NotFoundPage.parameters = {
reactRouter: reactRouterParameters({
routing: reactRouterOutlets(routes),
location: {
path: '/404',
},
}),
};
export const WorkspaceList: StoryFn = () => {
return <FakeApp />;
};
WorkspaceList.play = async ({ canvasElement }) => {
// click current-workspace
await waitFor(
() => {
assertExists(
canvasElement.querySelector('[data-testid="current-workspace"]')
);
},
{
timeout: 5000,
}
);
const currentWorkspace = canvasElement.querySelector(
'[data-testid="current-workspace"]'
) as Element;
await userEvent.click(currentWorkspace);
};
WorkspaceList.decorators = [withRouter, withCleanLocalStorage];
WorkspaceList.parameters = {
reactRouter: reactRouterParameters({
routing: reactRouterOutlets(routes),
location: {
path: '/',
},
}),
};

View File

@@ -1,11 +1,14 @@
import { AFFiNEDatePicker } from '@affine/component/date-picker';
import type { StoryFn } from '@storybook/react';
import type { Meta, StoryFn } from '@storybook/react';
import { useState } from 'react';
export default {
title: 'AFFiNE/AFFiNEDatePicker',
component: AFFiNEDatePicker,
};
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta;
export const Default: StoryFn = () => {
const [value, setValue] = useState<string>(new Date().toString());

View File

@@ -2,11 +2,15 @@
import { toast } from '@affine/component';
import { ImportPage } from '@affine/component/import-page';
import type { StoryFn } from '@storybook/react';
import type { Meta } from '@storybook/react';
export default {
title: 'AFFiNE/ImportPage',
component: ImportPage,
};
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta;
const Template: StoryFn<typeof ImportPage> = args => <ImportPage {...args} />;

View File

@@ -9,6 +9,9 @@ import { useAtomValue, useSetAtom } from 'jotai';
export default {
title: 'AFFiNE/NotificationCenter',
component: NotificationCenter,
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta<typeof NotificationCenter>;
let id = 0;

View File

@@ -1,11 +1,14 @@
/* deepscan-disable USELESS_ARROW_FUNC_BIND */
import { TourModal } from '@affine/component/tour-modal';
import type { StoryFn } from '@storybook/react';
import type { Meta, StoryFn } from '@storybook/react';
export default {
title: 'AFFiNE/TourModal',
component: TourModal,
};
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta;
export const Basic: StoryFn = () => {
return <TourModal open={true} onClose={() => {}} />;

View File

@@ -4,6 +4,9 @@ import type { Meta } from '@storybook/react';
export default {
title: 'AFFiNE/PageDetailSkeleton',
component: PageDetailSkeleton,
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta<typeof PageDetailSkeleton>;
export const Basic = () => {

View File

@@ -7,13 +7,16 @@ import { NewPageButton } from '@affine/component/page-list';
import { OperationCell } from '@affine/component/page-list';
import { PageIcon } from '@blocksuite/icons';
import { expect } from '@storybook/jest';
import type { StoryFn } from '@storybook/react';
import type { Meta, StoryFn } from '@storybook/react';
import { userEvent } from '@storybook/testing-library';
export default {
title: 'AFFiNE/PageList',
component: PageList,
};
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta;
export const AffineOperationCell: StoryFn<OperationCellProps> = ({
...props
@@ -34,7 +37,7 @@ AffineOperationCell.play = async ({ canvasElement }) => {
'[data-testid="page-list-operation-button"]'
) as HTMLButtonElement;
expect(button).not.toBeNull();
userEvent.click(button);
await userEvent.click(button);
}
};
@@ -51,7 +54,7 @@ AffineNewPageButton.play = async ({ canvasElement }) => {
expect(button).not.toBeNull();
const dropdown = button.querySelector('svg') as SVGSVGElement;
expect(dropdown).not.toBeNull();
userEvent.click(dropdown);
await userEvent.click(dropdown);
};
export const AffineAllPageList: StoryFn<typeof PageList> = ({ ...props }) => (
@@ -69,11 +72,11 @@ AffineAllPageList.args = {
favorite: false,
icon: <PageIcon />,
isPublicPage: true,
title: 'Today Page',
title: 'Last Page',
tags: [],
preview: 'this is page preview',
createDate: new Date(),
updatedDate: new Date(),
createDate: new Date('2021-01-01'),
updatedDate: new Date('2023-08-15'),
bookmarkPage: () => toast('Bookmark page'),
onClickPage: () => toast('Click page'),
onDisablePublicSharing: () => toast('Disable public sharing'),

View File

@@ -12,14 +12,17 @@ import { WorkspaceFlavour } from '@affine/env/workspace';
import { getOrCreateWorkspace } from '@affine/workspace/manager';
import type { Page } from '@blocksuite/store';
import { expect } from '@storybook/jest';
import type { StoryFn } from '@storybook/react';
import type { Meta, StoryFn } from '@storybook/react';
import { use } from 'foxact/use';
import { useState } from 'react';
export default {
title: 'AFFiNE/ShareMenu',
component: ShareMenu,
};
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta;
async function initPage(page: Page) {
await page.waitForLoaded();

View File

@@ -1,11 +1,14 @@
/* deepscan-disable USELESS_ARROW_FUNC_BIND */
import { Switch } from '@affine/component';
import type { StoryFn } from '@storybook/react';
import type { Meta, StoryFn } from '@storybook/react';
export default {
title: 'AFFiNE/Switch',
component: Switch,
};
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta;
export const Basic: StoryFn = () => {
return <Switch>Switch</Switch>;

View File

@@ -16,6 +16,9 @@ export default {
},
},
},
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta<WorkspaceAvatarProps>;
const schema = new Schema();

View File

@@ -9,6 +9,9 @@ import { useState } from 'react';
export default {
title: 'AFFiNE/WorkspaceList',
component: WorkspaceList,
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta<WorkspaceListProps>;
export const Default = () => {

View File

@@ -9,6 +9,9 @@
"outDir": "lib"
},
"references": [
{
"path": "../../apps/core"
},
{
"path": "../../packages/component"
},

View File

@@ -4,7 +4,7 @@
"composite": true,
"module": "ESNext",
"jsx": "react-jsx",
"moduleResolution": "Node",
"moduleResolution": "bundler",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"noEmit": false,
@@ -13,6 +13,7 @@
"include": [".storybook/**/*"],
"exclude": ["lib"],
"references": [
{ "path": "../../apps/core" },
{ "path": "../../packages/i18n" },
{
"path": "../../packages/env"

View File

@@ -1,6 +1,6 @@
{
"name": "@affine/monorepo",
"version": "0.8.0-canary.22",
"version": "0.8.0-canary.26",
"private": true,
"author": "toeverything",
"license": "MPL-2.0",
@@ -66,7 +66,7 @@
"@faker-js/faker": "^8.0.2",
"@istanbuljs/schema": "^0.1.3",
"@magic-works/i18n-codegen": "^0.5.0",
"@nx/vite": "16.6.0",
"@nx/vite": "16.7.0",
"@perfsee/sdk": "^1.9.0",
"@playwright/test": "^1.37.0",
"@taplo/cli": "^0.5.2",
@@ -74,9 +74,9 @@
"@toeverything/infra": "workspace:*",
"@types/affine__env": "workspace:*",
"@types/eslint": "^8.44.2",
"@types/node": "^18.17.4",
"@typescript-eslint/eslint-plugin": "^6.3.0",
"@typescript-eslint/parser": "^6.3.0",
"@types/node": "^18.17.5",
"@typescript-eslint/eslint-plugin": "^6.4.0",
"@typescript-eslint/parser": "^6.4.0",
"@vanilla-extract/vite-plugin": "^3.8.2",
"@vanilla-extract/webpack-plugin": "^2.2.0",
"@vitejs/plugin-react-swc": "^3.3.2",
@@ -86,7 +86,7 @@
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-i": "^2.28.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-react": "^7.33.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-sonarjs": "^0.20.0",
@@ -94,16 +94,16 @@
"eslint-plugin-unused-imports": "^3.0.0",
"eslint-plugin-vue": "^9.17.0",
"fake-indexeddb": "4.0.2",
"happy-dom": "^10.9.0",
"happy-dom": "^10.10.0",
"husky": "^8.0.3",
"lint-staged": "^13.2.3",
"lint-staged": "^14.0.0",
"madge": "^6.1.0",
"msw": "^1.2.3",
"nanoid": "^4.0.2",
"nx": "16.6.0",
"nx": "16.7.0",
"nx-cloud": "latest",
"nyc": "^15.1.0",
"prettier": "^3.0.1",
"prettier": "^3.0.2",
"semver": "^7.5.4",
"serve": "^14.2.0",
"ts-node": "^10.9.1",

View File

@@ -7,5 +7,5 @@
"@affine/env": "workspace:*",
"@toeverything/infra": "workspace:*"
},
"version": "0.8.0-canary.22"
"version": "0.8.0-canary.26"
}

View File

@@ -20,5 +20,5 @@
"peerDependencies": {
"ts-node": "*"
},
"version": "0.8.0-canary.22"
"version": "0.8.0-canary.26"
}

View File

@@ -26,9 +26,9 @@
"@emotion/react": "^11.11.1",
"@emotion/server": "^11.11.0",
"@emotion/styled": "^11.11.0",
"@mui/base": "5.0.0-beta.10",
"@mui/base": "5.0.0-beta.11",
"@mui/icons-material": "^5.14.3",
"@mui/material": "^5.14.4",
"@mui/material": "^5.14.5",
"@popperjs/core": "^2.11.8",
"@radix-ui/react-avatar": "^1.0.3",
"@radix-ui/react-collapsible": "^1.0.3",
@@ -51,12 +51,12 @@
"rxjs": "^7.8.1"
},
"devDependencies": {
"@blocksuite/blocks": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/editor": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/global": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/blocks": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/editor": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/global": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/icons": "^2.1.31",
"@blocksuite/lit": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/store": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/lit": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/store": "0.0.0-20230818021533-7e342436-nightly",
"@types/react": "^18.2.20",
"@types/react-datepicker": "^4.15.0",
"@types/react-dnd": "^3.0.2",
@@ -66,5 +66,5 @@
"vite": "^4.4.9",
"yjs": "^13.6.7"
},
"version": "0.8.0-canary.22"
"version": "0.8.0-canary.26"
}

View File

@@ -3,13 +3,16 @@ import { style } from '@vanilla-extract/css';
export const root = style({
fontSize: 'var(--affine-font-xs)',
minHeight: '16px',
width: 'calc(100% + 6px)',
userSelect: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: '4px',
padding: '0 8px',
selectors: {
'&:not(:first-of-type)': {
marginTop: '10px',
marginTop: '16px',
},
},
});

View File

@@ -52,7 +52,6 @@ export const navStyle = style({
display: 'flex',
flexDirection: 'column',
zIndex: parseInt(baseTheme.zIndexModal),
borderRight: '1px solid transparent',
});
export const navHeaderStyle = style({
@@ -76,6 +75,7 @@ export const navBodyStyle = style({
height: 'calc(100% - 52px)',
display: 'flex',
flexDirection: 'column',
rowGap: '4px',
});
export const sidebarFloatMaskStyle = style({

View File

@@ -1,15 +1,23 @@
import { style } from '@vanilla-extract/css';
export const linkItemRoot = style({
color: 'inherit',
display: 'contents',
});
export const root = style({
display: 'inline-flex',
alignItems: 'center',
borderRadius: '4px',
textAlign: 'left',
color: 'inherit',
width: '100%',
minHeight: '30px',
userSelect: 'none',
cursor: 'pointer',
padding: '0 12px',
fontSize: 'var(--affine-font-sm)',
margin: '2px 0',
marginTop: '4px',
selectors: {
'&:hover': {
background: 'var(--affine-hover-color)',
@@ -29,10 +37,8 @@ export const root = style({
// 'linear-gradient(0deg, rgba(0, 0, 0, 0.04), rgba(0, 0, 0, 0.04)), rgba(0, 0, 0, 0.04)',
// },
'&[data-collapsible="true"]': {
width: 'calc(100% + 8px)',
transform: 'translateX(-8px)',
paddingLeft: '4px',
paddingRight: '12px',
paddingRight: '4px',
},
'&[data-type="collection-list-item"][data-collapsible="false"][data-active="true"],&[data-type="favorite-list-item"][data-collapsible="false"][data-active="true"], &[data-type="favorite-list-item"][data-collapsible="false"]:hover, &[data-type="collection-list-item"][data-collapsible="false"]:hover':
{
@@ -41,6 +47,9 @@ export const root = style({
paddingLeft: '20px',
paddingRight: '12px',
},
[`${linkItemRoot}:first-of-type &`]: {
marginTop: '0px',
},
},
});
@@ -53,6 +62,12 @@ export const content = style({
export const postfix = style({
justifySelf: 'flex-end',
display: 'none',
selectors: {
[`${root}:hover &`]: {
display: 'flex',
},
},
});
export const icon = style({
@@ -68,10 +83,15 @@ export const collapsedIconContainer = style({
justifyContent: 'center',
borderRadius: '2px',
transition: 'transform 0.2s',
color: 'inherit',
selectors: {
'&[data-collapsed="true"]': {
transform: 'rotate(-90deg)',
},
'&[data-disabled="true"]': {
opacity: 0.3,
pointerEvents: 'none',
},
'&:hover': {
background: 'var(--affine-hover-color)',
},
@@ -103,8 +123,3 @@ export const collapsedIcon = style({
export const spacer = style({
flex: 1,
});
export const linkItemRoot = style({
color: 'inherit',
display: 'contents',
});

View File

@@ -6,11 +6,13 @@ import { Link } from 'react-router-dom';
import * as styles from './index.css';
export interface MenuItemProps extends React.HTMLAttributes<HTMLDivElement> {
export interface MenuItemProps extends React.HTMLAttributes<HTMLButtonElement> {
icon?: React.ReactElement;
active?: boolean;
disabled?: boolean;
collapsed?: boolean; // true, false, undefined. undefined means no collapse
// true, false, undefined. undefined means no collapse
collapsed?: boolean;
// if onCollapsedChange is given, but collapsed is undefined, then we will render the collapse button as disabled
onCollapsedChange?: (collapsed: boolean) => void;
postfix?: React.ReactElement;
}
@@ -23,7 +25,7 @@ const stopPropagation: React.MouseEventHandler = e => {
e.stopPropagation();
};
export const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(
export const MenuItem = React.forwardRef<HTMLButtonElement, MenuItemProps>(
(
{
onClick,
@@ -38,14 +40,9 @@ export const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(
},
ref
) => {
const collapsible = collapsed !== undefined;
if (collapsible && !onCollapsedChange) {
throw new Error(
'onCollapsedChange is required when collapsed is defined'
);
}
const collapsible = onCollapsedChange !== undefined;
return (
<div
<button
ref={ref}
{...props}
onClick={onClick}
@@ -58,6 +55,7 @@ export const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(
<div className={styles.iconsContainer} data-collapsible={collapsible}>
{collapsible && (
<div
data-disabled={collapsed === undefined ? true : undefined}
onClick={e => {
e.stopPropagation();
e.preventDefault(); // for links
@@ -68,7 +66,7 @@ export const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(
>
<ArrowDownSmallIcon
className={styles.collapsedIcon}
data-collapsed={collapsed}
data-collapsed={collapsed !== false}
/>
</div>
)}
@@ -84,21 +82,22 @@ export const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(
{postfix}
</div>
) : null}
</div>
</button>
);
}
);
MenuItem.displayName = 'MenuItem';
export const MenuLinkItem = React.forwardRef<HTMLDivElement, MenuLinkItemProps>(
({ to, ...props }, ref) => {
return (
<Link to={to} className={styles.linkItemRoot}>
{/* The <a> element rendered by Link does not generate display box due to `display: contents` style */}
{/* Thus ref is passed to MenuItem instead of Link */}
<MenuItem ref={ref} {...props}></MenuItem>
</Link>
);
}
);
export const MenuLinkItem = React.forwardRef<
HTMLButtonElement,
MenuLinkItemProps
>(({ to, ...props }, ref) => {
return (
<Link to={to} className={styles.linkItemRoot}>
{/* The <a> element rendered by Link does not generate display box due to `display: contents` style */}
{/* Thus ref is passed to MenuItem instead of Link */}
<MenuItem ref={ref} {...props}></MenuItem>
</Link>
);
});
MenuLinkItem.displayName = 'MenuLinkItem';

View File

@@ -12,7 +12,7 @@ export const root = style({
userSelect: 'none',
cursor: 'pointer',
padding: '0 12px',
margin: '12px 0',
margin: '20px 0',
position: 'relative',
});

View File

@@ -1,6 +1,7 @@
import { assertExists } from '@blocksuite/global/utils';
import { useAtom, useSetAtom } from 'jotai';
import type { ReactElement } from 'react';
import { useCallback, useLayoutEffect, useState } from 'react';
import { useCallback } from 'react';
import {
appSidebarOpenAtom,
@@ -18,16 +19,10 @@ export const ResizeIndicator = (props: ResizeIndicatorProps): ReactElement => {
const [sidebarOpen, setSidebarOpen] = useAtom(appSidebarOpenAtom);
const [isResizing, setIsResizing] = useAtom(appSidebarResizingAtom);
const [anchorLeft, setAnchorLeft] = useState(0);
useLayoutEffect(() => {
if (!props.targetElement) return;
const { left } = props.targetElement.getBoundingClientRect();
setAnchorLeft(left);
}, [props.targetElement]);
const onResizeStart = useCallback(() => {
let resized = false;
assertExists(props.targetElement);
const { left: anchorLeft } = props.targetElement.getBoundingClientRect();
function onMouseMove(e: MouseEvent) {
e.preventDefault();
@@ -51,13 +46,7 @@ export const ResizeIndicator = (props: ResizeIndicatorProps): ReactElement => {
},
{ once: true }
);
}, [
anchorLeft,
props.targetElement,
setIsResizing,
setSidebarOpen,
setWidth,
]);
}, [props.targetElement, setIsResizing, setSidebarOpen, setWidth]);
return (
<div

View File

@@ -4,7 +4,6 @@ export const baseContainer = style({
padding: '4px 16px',
display: 'flex',
flexFlow: 'column nowrap',
rowGap: '4px',
});
export const scrollableContainerRoot = style({
@@ -45,6 +44,7 @@ export const scrollableContainer = style([
baseContainer,
{
height: '100%',
padding: '4px 8px',
},
]);
@@ -69,6 +69,7 @@ export const scrollbarThumb = style({
position: 'relative',
background: 'var(--affine-black-30)',
borderRadius: '4px',
overflow: 'hidden',
selectors: {
'&::before': {
content: '""',

View File

@@ -2,12 +2,6 @@ import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { RootWorkspaceMetadata } from '@affine/workspace/atom';
import { SettingsIcon } from '@blocksuite/icons';
import {
CloudWorkspaceIcon as DefaultCloudWorkspaceIcon,
CollaborationIcon as DefaultJoinedWorkspaceIcon,
LocalDataIcon as DefaultLocalDataIcon,
LocalWorkspaceIcon as DefaultLocalWorkspaceIcon,
} from '@blocksuite/icons';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import { useStaticBlockSuiteWorkspace } from '@toeverything/infra/__internal__/react';
import { useCallback } from 'react';
@@ -16,25 +10,10 @@ import { WorkspaceAvatar } from '../../workspace-avatar';
import {
StyledCard,
StyledSettingLink,
StyleWorkspaceInfo,
StyleWorkspaceTitle,
StyledWorkspaceInfo,
StyledWorkspaceTitle,
} from './styles';
const JoinedWorkspaceIcon = () => {
return <DefaultJoinedWorkspaceIcon style={{ color: '#FF646B' }} />;
};
const LocalWorkspaceIcon = () => {
return <DefaultLocalWorkspaceIcon style={{ color: '#FDBD32' }} />;
};
const CloudWorkspaceIcon = () => {
return <DefaultCloudWorkspaceIcon style={{ color: '#60A5FA' }} />;
};
const LocalDataIcon = () => {
return <DefaultLocalDataIcon style={{ color: '#62CD80' }} />;
};
export interface WorkspaceTypeProps {
flavour: WorkspaceFlavour;
}
@@ -46,21 +25,18 @@ const WorkspaceType = ({ flavour }: WorkspaceTypeProps) => {
if (flavour === WorkspaceFlavour.LOCAL) {
return (
<p title={t['Local Workspace']()}>
<LocalWorkspaceIcon />
<p style={{ fontSize: '10px' }} title={t['Local Workspace']()}>
<span>{t['Local Workspace']()}</span>
</p>
);
}
return isOwner ? (
<p title={t['Cloud Workspace']()}>
<CloudWorkspaceIcon />
<p style={{ fontSize: '10px' }} title={t['Cloud Workspace']()}>
<span>{t['Cloud Workspace']()}</span>
</p>
) : (
<p title={t['Joined Workspace']()}>
<JoinedWorkspaceIcon />
<p style={{ fontSize: '10px' }} title={t['Joined Workspace']()}>
<span>{t['Joined Workspace']()}</span>
</p>
);
@@ -79,7 +55,7 @@ export const WorkspaceCard = ({
currentWorkspaceId,
meta,
}: WorkspaceCardProps) => {
const t = useAFFiNEI18N();
// const t = useAFFiNEI18N();
const workspace = useStaticBlockSuiteWorkspace(meta.id);
const [name] = useBlockSuiteWorkspaceName(workspace);
@@ -91,20 +67,21 @@ export const WorkspaceCard = ({
}, [onClick, meta.id])}
active={workspace.id === currentWorkspaceId}
>
<WorkspaceAvatar size={58} workspace={workspace} />
<WorkspaceAvatar size={28} workspace={workspace} />
<StyleWorkspaceInfo>
<StyleWorkspaceTitle>{name}</StyleWorkspaceTitle>
<StyledWorkspaceInfo>
<StyledWorkspaceTitle>{name}</StyledWorkspaceTitle>
<WorkspaceType flavour={meta.flavour} />
{meta.flavour === WorkspaceFlavour.LOCAL && (
{/* {meta.flavour === WorkspaceFlavour.LOCAL && (
<p title={t['Available Offline']()}>
<LocalDataIcon />
<span>{t['Available Offline']()}</span>
</p>
)}
</StyleWorkspaceInfo>
)} */}
</StyledWorkspaceInfo>
<StyledSettingLink
className="setting-entry"
data-testid="workspace-card-setting-button"
onClick={e => {
e.stopPropagation();
onSettingClick(meta.id);

View File

@@ -2,7 +2,7 @@ import { IconButton } from '@toeverything/components/button';
import { displayFlex, styled, textEllipsis } from '../../../styles';
export const StyleWorkspaceInfo = styled('div')(() => {
export const StyledWorkspaceInfo = styled('div')(() => {
return {
marginLeft: '15px',
width: '202px',
@@ -23,13 +23,13 @@ export const StyleWorkspaceInfo = styled('div')(() => {
};
});
export const StyleWorkspaceTitle = styled('div')(() => {
export const StyledWorkspaceTitle = styled('div')(() => {
return {
fontSize: 'var(--affine-font-base)',
fontWeight: 600,
lineHeight: '24px',
marginBottom: '10px',
maxWidth: '200px',
color: 'var(--affine-text-primary-color)',
...textEllipsis(1),
};
});
@@ -38,19 +38,21 @@ export const StyledCard = styled('div')<{
active?: boolean;
}>(({ active }) => {
const borderColor = active ? 'var(--affine-primary-color)' : 'transparent';
const backgroundColor = active ? 'var(--affine-white)' : 'transparent';
return {
width: '310px',
height: '124px',
width: '280px',
height: '58px',
cursor: 'pointer',
padding: '16px',
boxShadow: 'var(--affine-shadow-1)',
borderRadius: '12px',
border: `1px solid ${borderColor}`,
...displayFlex('flex-start', 'flex-start'),
marginBottom: '24px',
marginBottom: '12px',
transition: 'background .2s',
background: 'var(--affine-white-80)',
alignItems: 'center',
position: 'relative',
color: 'var(--affine-text-secondary-color)',
background: backgroundColor,
':hover': {
background: 'var(--affine-hover-color)',
'.add-icon': {
@@ -96,3 +98,9 @@ export const StyledSettingLink = styled(IconButton)(() => {
},
};
});
export const StyledWorkspaceType = styled('p')(() => {
return {
fontSize: 10,
};
});

View File

@@ -14,6 +14,7 @@ export const avatarImageStyle = style({
height: '100%',
objectFit: 'cover',
objectPosition: 'center',
display: 'block',
});
const bottomAnimation = keyframes({

View File

@@ -67,7 +67,6 @@ export const mainContainerStyle = style({
width: 0,
flex: 1,
maxWidth: '100%',
zIndex: 2,
backgroundColor: 'var(--affine-background-primary-color)',
selectors: {
'&[data-show-padding="true"]': {
@@ -103,6 +102,9 @@ export const toolStyle = style({
right: '30px',
bottom: '30px',
zIndex: 'var(--affine-z-index-popover)',
display: 'flex',
flexDirection: 'column',
gap: '12px',
'@media': {
[breakpoints.down('md', true)]: {
right: 'calc((100vw - 640px) * 3 / 19 + 14px)',

View File

@@ -252,3 +252,11 @@ affine-block-hub {
padding: 0;
}
}
button,
input,
select,
textarea,
[role='button'] {
-webkit-app-region: no-drag;
}

View File

@@ -13,7 +13,6 @@ import {
import { styled } from '../../styles';
import type { PopperProps, VirtualElement } from './interface';
import { PopperArrow } from './popover-arrow';
export const Popper = ({
children,
content,
@@ -41,7 +40,8 @@ export const Popper = ({
}: PopperProps) => {
const [anchorEl, setAnchorEl] = useState<VirtualElement>();
const [visible, setVisible] = useState(defaultVisible);
const [arrowRef, setArrowRef] = useState<HTMLElement>();
//const [arrowRef, setArrowRef] = useState<HTMLElement>();
const arrowRef = null;
const pointerLeaveTimer = useRef<number>();
const pointerEnterTimer = useRef<number>();
@@ -170,12 +170,111 @@ export const Popper = ({
}
}}
>
{showArrow && (
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
<PopperArrow placement={placement} ref={setArrowRef} />
{showArrow ? (
<>
{placement.indexOf('bottom') === 0 ? (
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="11"
height="6"
viewBox="0 0 11 6"
fill="none"
>
<path
d="M6.38889 0.45C5.94444 -0.15 5.05555 -0.150001 4.61111 0.449999L0.499999 6L10.5 6L6.38889 0.45Z"
style={{ fill: 'var(--affine-tooltip)' }}
/>
</svg>
{content}
</div>
) : placement.indexOf('top') === 0 ? (
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
{content}
<svg
xmlns="http://www.w3.org/2000/svg"
width="11"
height="6"
viewBox="0 0 11 6"
fill="none"
>
<path
d="M4.61111 5.55C5.05556 6.15 5.94445 6.15 6.38889 5.55L10.5 -4.76837e-07H0.5L4.61111 5.55Z"
style={{ fill: 'var(--affine-tooltip)' }}
/>
</svg>
</div>
) : placement.indexOf('left') === 0 ? (
<>
{content}
<svg
xmlns="http://www.w3.org/2000/svg"
width="6"
height="10"
viewBox="0 0 6 10"
fill="none"
>
<path
d="M5.55 5.88889C6.15 5.44444 6.15 4.55555 5.55 4.11111L-4.76837e-07 0L-4.76837e-07 10L5.55 5.88889Z"
style={{ fill: 'var(--affine-tooltip)' }}
/>
</svg>
</>
) : placement.indexOf('right') === 0 ? (
<>
<svg
xmlns="http://www.w3.org/2000/svg"
width="6"
height="10"
viewBox="0 0 6 10"
style={{ fill: 'var(--affine-tooltip)' }}
>
<path
d="M0.45 4.11111C-0.15 4.55556 -0.15 5.44445 0.45 5.88889L6 10V0L0.45 4.11111Z"
style={{ fill: 'var(--affine-tooltip)' }}
/>
</svg>
{content}
</>
) : (
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
{content}
<svg
xmlns="http://www.w3.org/2000/svg"
width="11"
height="6"
viewBox="0 0 11 6"
fill="none"
>
<path
d="M4.61111 5.55C5.05556 6.15 5.94445 6.15 6.38889 5.55L10.5 -4.76837e-07H0.5L4.61111 5.55Z"
style={{ fill: 'var(--affine-tooltip)' }}
/>
</svg>
</div>
)}
</>
) : (
<>{content}</>
)}
{content}
</div>
</Grow>
)}

View File

@@ -70,6 +70,7 @@ export const scrollbarThumb = style({
position: 'relative',
background: 'var(--affine-divider-color)',
width: '50%',
overflow: 'hidden',
borderRadius: '4px',
':hover': {
background: 'var(--affine-icon-color)',

View File

@@ -7,15 +7,16 @@ import StyledPopperContainer from '../shared/container';
const StyledTooltip = styled(StyledPopperContainer)(() => {
return {
maxWidth: '320px',
boxShadow: 'var(--affine-float-button-shadow)',
padding: '4px 12px',
display: 'inline-flex',
minHeight: '38px',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
flexShrink: 0,
backgroundColor: 'var(--affine-tooltip)',
borderRadius: '4px',
color: 'var(--affine-white)',
fontSize: 'var(--affine-font-sm)',
borderRadius: '8px',
marginBottom: '12px',
overflowWrap: 'break-word',
padding: '5px 12px',
};
});

View File

@@ -8,5 +8,5 @@
"devDependencies": {
"@types/debug": "^4.1.8"
},
"version": "0.8.0-canary.22"
"version": "0.8.0-canary.26"
}

View File

@@ -5,10 +5,10 @@
"main": "./src/index.ts",
"module": "./src/index.ts",
"devDependencies": {
"@blocksuite/global": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/global": "0.0.0-20230818021533-7e342436-nightly",
"react": "18.2.0",
"react-dom": "18.2.0",
"zod": "^3.21.4"
"zod": "^3.22.1"
},
"exports": {
"./automation": "./src/automation.ts",
@@ -27,5 +27,5 @@
"dependencies": {
"lit": "^2.8.0"
},
"version": "0.8.0-canary.22"
"version": "0.8.0-canary.26"
}

View File

@@ -5,15 +5,22 @@ import { z } from 'zod';
import { isBrowser, isDesktop, isServer } from './constant.js';
import { isValidIPAddress } from './is-valid-ip-address.js';
import { UaHelper } from './ua-helper.js';
export const blockSuiteFeatureFlags = z.object({
enable_database: z.boolean(),
enable_database_filter: z.boolean(),
enable_data_view: z.boolean(),
enable_page_tags: z.boolean(),
enable_drag_handle: z.boolean(),
enable_surface: z.boolean(),
enable_block_hub: z.boolean(),
enable_slash_menu: z.boolean(),
enable_toggle_block: z.boolean(),
enable_edgeless_toolbar: z.boolean(),
enable_linked_page: z.boolean(),
enable_bookmark_operation: z.boolean(),
enable_note_index: z.boolean(),
enable_attachment_block: z.boolean(),
});
export const runtimeFlagsSchema = z.object({

View File

@@ -1,6 +1,6 @@
{
"name": "@affine/graphql",
"version": "0.8.0-canary.22",
"version": "0.8.0-canary.26",
"description": "Autogenerated GraphQL client for affine.pro",
"license": "MPL-2.0",
"type": "module",
@@ -16,12 +16,12 @@
"@graphql-codegen/typescript-operations": "^4.0.1",
"@types/lodash-es": "^4.17.8",
"lodash-es": "^4.17.21",
"prettier": "^3.0.1"
"prettier": "^3.0.2"
},
"scripts": {
"postinstall": "gql-gen"
},
"dependencies": {
"graphql": "^16.7.1"
"graphql": "^16.8.0"
}
}

View File

@@ -6,17 +6,17 @@
},
"private": true,
"dependencies": {
"foxact": "^0.2.17"
"foxact": "^0.2.20"
},
"devDependencies": {
"@affine/env": "workspace:*",
"@affine/y-provider": "workspace:*",
"@blocksuite/block-std": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/blocks": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/editor": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/global": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/lit": "0.0.0-20230814155455-ceb5d5d8-nightly",
"@blocksuite/store": "0.0.0-20230814155455-ceb5d5d8-nightly"
"@blocksuite/block-std": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/blocks": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/editor": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/global": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/lit": "0.0.0-20230818021533-7e342436-nightly",
"@blocksuite/store": "0.0.0-20230818021533-7e342436-nightly"
},
"peerDependencies": {
"@affine/y-provider": "workspace:*",
@@ -53,5 +53,5 @@
"optional": true
}
},
"version": "0.8.0-canary.22"
"version": "0.8.0-canary.26"
}

View File

@@ -31,11 +31,11 @@
"react-i18next": "^13.1.2"
},
"devDependencies": {
"@types/node": "^18.17.4",
"@types/node": "^18.17.5",
"@types/prettier": "^3.0.0",
"prettier": "^3.0.1",
"prettier": "^3.0.2",
"ts-node": "^10.9.1",
"typescript": "^5.1.6"
},
"version": "0.8.0-canary.22"
"version": "0.8.0-canary.26"
}

View File

@@ -399,6 +399,83 @@
"com.affine.workspace.cannot-delete": "You cannot delete the last workspace",
"com.affine.write_with_a_blank_page": "Write with a blank page",
"com.affine.yesterday": "Yesterday",
"com.affine.settings.sign": "Sign in / Sign up",
"com.affine.setting.sign.message": "Sync with AFFiNE Cloud",
"com.affine.setting.sign.out.message": "Securely sign out of your account.",
"com.affine.setting.account": "Account Settings",
"com.affine.setting.account.message": "Your personal information",
"com.affine.setting.account.delete": "Delete Account",
"com.affine.setting.account.delete.message": "Permanently delete this account and the Workspace data backup in AFFiNE Cloud. This action can not be undone.",
"com.affine.settings.email": "Email",
"com.affine.settings.email.action": "Change Email",
"com.affine.settings.password": "Password",
"com.affine.settings.password.message": "Set a password to sign in to your account",
"com.affine.settings.password.action.change": "Change password",
"com.affine.settings.password.action.set": "Set password",
"com.affine.settings.profile": "My Profile",
"com.affine.settings.profile.message": "Your account profile will be displayed to everyone.",
"com.affine.settings.profile.name": "Display Name",
"com.affine.settings.profile.placeholder": "Input account name",
"com.affine.auth.sign.in": "Sign in",
"com.affine.auth.sign.up": "Sign up",
"com.affine.auth.sign.up.sent.email.subtitle": "Create your account",
"com.affine.auth.sign.sent.email.message.start": "An email with a magic link has been sent to ",
"com.affine.auth.sign.sent.email.message.end": " You can click the link to create an account automatically.",
"com.affine.auth.sign.up.success.title": "Your account has been created and youre now signed in!",
"com.affine.auth.sign.up.success.subtitle": "The app will automatically open or redirect to the web version. if you encounter any issues, you can also click the button below to manually open the AFFiNE app.",
"com.affine.auth.page.sent.email.title": "Welcome to AFFiNE Cloud, you are almost there!",
"com.affine.auth.page.sent.email.subtitle": "Please set a password of 8-20 characters with both letters and numbers to continue signing up with ",
"com.affine.auth.later": "Later",
"com.affine.auth.open.affine": "Open AFFiNE",
"com.affine.auth.sign.in.sent.email.subtitle": "Confirm your email",
"com.affine.auth.sign.auth.code.message": "If you haven't received the email, please check your spam folder.",
"com.affine.auth.sign.auth.code.message.password": "If you haven't received the email, please check your spam folder. Or <1>sign in with password</1> instead.",
"com.affine.auth.password.error": "Invalid password",
"com.affine.auth.forget": "Forgot password",
"com.affine.auth.has.signed": " has signed in!",
"com.affine.auth.signed.success.title": "Youre almost there!",
"com.affine.auth.signed.success.subtitle": "You have successfully signed in. The app will automatically open or redirect to the web version. if you encounter any issues, you can also click the button below to manually open the AFFiNE app.",
"com.affine.auth.reset.password": "Reset Password",
"com.affine.auth.reset.password.message": "You will receive an email with a link to reset your password. Please check your inbox.",
"com.affine.auth.send.reset.password.link": "Send reset link",
"com.affine.auth.send.set.password.link": "Send set link",
"com.affine.auth.send.change.email.link": "Send verification link",
"com.affine.auth.sent": "Sent",
"com.affine.auth.sent.change.password.hint": "Reset password link has been sent.",
"com.affine.auth.sent.set.password.hint": "Set password link has been sent.",
"com.affine.auth.sent.change.email.hint": "Verification link has been sent.",
"com.affine.auth.set.password.page.title": "Set your AFFiNE Cloud password",
"com.affine.auth.reset.password.page.title": "Reset your AFFiNE Cloud password",
"com.affine.auth.set.password.page.success": "Password set successful",
"com.affine.auth.set.password.save": "Save Password",
"com.affine.auth.set.email.save": "Save Email",
"com.affine.auth.change.email.page.title": "Change email address",
"com.affine.auth.change.email.page.success.title": "Email address updated!",
"com.affine.auth.change.email.page.subtitle": "Please enter your new email address below. We will send a verification link to this email address to complete the process.",
"com.affine.auth.change.email.page.success.subtitle": "Congratulations! You have successfully updated the email address associated with your AFFiNE Cloud account.",
"com.affine.auth.sign.email.placeholder": "Enter your email address",
"com.affine.auth.sign.email.continue": "Continue with Email",
"com.affine.auth.sign.email.error": "Invalid email",
"com.affine.auth.sign.condition": "Terms of Conditions",
"com.affine.auth.sign.policy": "Privacy Policy",
"com.affine.auth.sign.message": "By clicking “Continue with Google/Email” above, you acknowledge that you agree to AFFiNE's <1>Terms of Conditions</1> and <3>Privacy Policy</3>.",
"com.affine.auth.sign.auth.code.error.hint": "Wrong code, please try again",
"com.affine.auth.sign.auth.code.on.resend.hint": "Send code again",
"com.affine.auth.sign.auth.code.resend.hint": "Resend code",
"com.affine.auth.password": "Password",
"com.affine.auth.set.password": "Set password",
"com.affine.auth.set.password.message": "Please set a password of 8-20 characters with both letters and numbers to continue signing up with ",
"com.affine.auth.set.password.placeholder": "Set a password at least 8 letters long",
"com.affine.auth.set.password.placeholder.confirm": "Confirm password",
"com.affine.auth.create.count": "Create Account",
"com.affine.expired.page.title": "This link has expired...",
"com.affine.expired.page.subtitle": "Please request a new reset password link.",
"com.affine.workspace.cloud.join": "Join Workspace",
"com.affine.workspace.cloud.account.settings": "Account Settings",
"com.affine.workspace.cloud.account.logout": "Log Out",
"com.affine.workspace.cloud.sync": "Cloud sync",
"com.affine.workspace.cloud.auth": "Sign up/ Sign in",
"com.affine.workspace.local.import": "Import Workspace",
"core": "core",
"dark": "Dark",
"emptyAllPages": "Click on the <1>$t(New Page)</1> button to create your first page.",

Some files were not shown because too many files have changed in this diff Show More