Compare commits

..

117 Commits

Author SHA1 Message Date
Alex Yang
3976c37d41 v0.7.0-canary.33 2023-07-04 21:52:04 +08:00
Fangdun Tsai
2bc15665b9 chore(electron): renaming clipboard api (#3005) 2023-07-04 12:51:59 +00:00
Alex Yang
e4539dfeb1 fix: bookmark block output missing (#3010) 2023-07-04 12:48:47 +00:00
Qi
1070e17310 feat: modify setting modal (#3008) 2023-07-04 12:37:46 +00:00
Alex Yang
b4f7eb36ef v0.7.0-canary.32 2023-07-04 16:12:09 +08:00
3720
000f802baa feat: add tags support (#2988)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-07-04 07:32:11 +00:00
Alex Yang
e871ffcba0 refactor: input component (#2999) 2023-07-04 06:52:46 +00:00
Alex Yang
8d2ffe3936 chore: bump version (#2998) 2023-07-04 06:47:35 +00:00
ShortCipher5
9e253420d2 docs: update README.md (#2997) 2023-07-04 14:13:25 +08:00
Alex Yang
edb7847e95 test: use static server (#2996) 2023-07-04 05:37:06 +00:00
Alex Yang
3d70148e0f chore: add circular check (#2995) 2023-07-04 04:54:08 +00:00
Alex Yang
7f89b197da build: enable next server (#2992) 2023-07-04 01:59:06 +00:00
danielchim
32692bd54a feat: page mode shortcut (#2985) 2023-07-03 16:23:53 +00:00
Alex Yang
7b2acec7c3 v0.7.0-canary.31 2023-07-03 23:14:38 +08:00
Alex Yang
f1adf23631 chore: bump version (#2989) 2023-07-03 14:51:49 +00:00
Alex Yang
a5d2fafad6 refactor: remove legacy cloud (#2987) 2023-07-03 14:29:37 +00:00
xiaodong zuo
3d0a907b49 fix: dark mode export PDF leaves margin and notification (#2978) 2023-07-03 12:11:07 +00:00
LongYinan
bacd00655d ci: reduce yarn cache (#2983) 2023-07-03 11:09:17 +00:00
Peng Xiao
08e003b0f6 fix: potential updater issue (#2973) 2023-07-03 11:04:45 +00:00
Alex Yang
0f1c5163a1 feat: remove old setting page by default (#2980) 2023-07-03 10:59:23 +00:00
JimmFly
18874d0d1e chore: add import to sidebar (#2981) 2023-07-03 10:51:28 +00:00
Peng Xiao
7f0a74c694 fix: some potential tests issue (#2982) 2023-07-03 10:46:47 +00:00
Peng Xiao
901fc87716 fix: potential race condition on app load when migration (#2977)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-07-03 09:40:02 +00:00
Alex Yang
ee2ab4086f fix(web): hydration issue (#2974) 2023-07-03 09:06:12 +00:00
JimmFly
af94674c18 style: adjust icon button hover color (#2976) 2023-07-03 08:46:16 +00:00
Alex Yang
262289a398 chore: add affine-cloud build config (#2971) 2023-07-03 07:25:14 +00:00
Alex Yang
467eab4ddf build: update build config (#2967) 2023-07-03 06:17:13 +00:00
Alex Yang
63517e4912 chore: update 'lint-staged' rules (#2969) 2023-07-03 06:00:50 +00:00
JimmFly
6f9487deb7 style: adjust copilot chat style (#2915) 2023-07-03 05:57:30 +00:00
JimmFly
8d0edd5255 i18n: update translation resources (#2968) 2023-07-03 05:15:46 +00:00
Qi
bdea153c82 feat: modify preloading data (#2947) 2023-07-03 02:49:55 +00:00
Alex Yang
d447883b7d v0.7.0-canary.30 2023-07-02 14:18:33 +08:00
Alex Yang
03ec51a96c chore: bump version 2023-07-02 14:17:22 +08:00
Alex Yang
0adf18f5e6 v0.7.0-canary.29 2023-07-02 14:06:14 +08:00
Alex Yang
5e7dc9ff21 chore: bump version (#2960) 2023-07-02 05:26:47 +00:00
Alex Yang
33097382c6 chore: replace 'eslint-plugin-import' (#2957) 2023-07-01 16:35:16 +00:00
Alex Yang
b9df2cdabb ci: update labeler.yml 2023-07-01 23:13:35 +08:00
Alex Yang
158338508a ci: use yarn run test 2023-07-01 23:08:14 +08:00
Alex Yang
640967d9ae v0.7.0-canary.28 2023-07-01 21:35:05 +08:00
Alex Yang
ec973395da fix: remove export script 2023-07-01 21:34:24 +08:00
Alex Yang
b35d99d935 v0.7.0-canary.27 2023-07-01 21:29:11 +08:00
Alex Yang
c0f6e751d2 build: update nx.json 2023-07-01 18:58:14 +08:00
Alex Yang
6af454ceed chore: improve ci build speed (#2953) 2023-07-01 10:47:26 +00:00
Alex Yang
ed829dd43b build: update nx.json 2023-07-01 16:54:21 +08:00
Alex Yang
a9adb4dda2 build: fix nx.json (#2951) 2023-07-01 16:34:30 +08:00
Alex Yang
54a7eeda37 chore: bump version (#2950) 2023-07-01 16:26:43 +08:00
Alex Yang
711e683c6f build: skip type check in next.js build (#2952) 2023-07-01 16:22:21 +08:00
Alex Yang
81c5e6d3d2 build: enhance nx build (#2948) 2023-07-01 01:17:31 +08:00
3720
7a5a5d503a chore: adjust icon and style (#2949) 2023-06-30 23:38:47 +08:00
Alex Yang
b597dbd80f docs: update react badge 2023-06-30 18:03:20 +08:00
Alex Yang
ebdf724012 v0.7.0-canary.26 2023-06-30 17:39:42 +08:00
Alex Yang
14f63e91a9 ci: fix build desktop 2023-06-30 17:39:11 +08:00
Alex Yang
ad218ec65d ci: update paths-ignore 2023-06-30 17:39:11 +08:00
Alex Yang
9fda82564b ci: chmod 777 on output directory 2023-06-30 17:39:11 +08:00
Alex Yang
a52fc54d80 v0.7.0-canary.25 2023-06-30 16:27:59 +08:00
Alex Yang
524c342b5e chore: bump blocksuite to '0.0.0-20230630081054-55a25248-nightly' (#2944) 2023-06-30 16:27:38 +08:00
regischen
f4fc084a0a fix(web): migrate connector (#2941)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-06-30 16:11:04 +08:00
Alex Yang
38a2aa9d17 build(electron): use nx (#2942) 2023-06-30 16:10:35 +08:00
Peng Xiao
9e90242ddb fix: disable sqlite blob storage (#2943) 2023-06-30 16:09:43 +08:00
Alex Yang
fd0c1da608 fix(cli): run dev-web crash 2023-06-30 15:58:13 +08:00
Alex Yang
68c4fccf98 ci: cancel previous build (#2794) 2023-06-30 07:39:27 +00:00
Alex Yang
3c93f4162d build: remove unused package (#2937) 2023-06-30 07:09:12 +00:00
Alex Yang
b6c314e180 refactor(cli): use typescript (#2938) 2023-06-30 06:58:57 +00:00
Alex Yang
62b465a889 ci: build infra code before build layers 2023-06-30 15:20:36 +08:00
3720
9d0db78f64 feat: support for view management (#2892) 2023-06-30 05:40:00 +00:00
Fangdun Tsai
d3393cb0fc feat: expose clipboard apis (#2932) 2023-06-30 04:47:30 +00:00
Alex Yang
79cded302f chore: bump blocksuite to 0.0.0-20230629103121-76e6587d-nightly (#2931) 2023-06-30 04:36:56 +00:00
Alex Yang
53d90a11de chore: tag deprecated files (#2936) 2023-06-30 04:01:14 +00:00
Alex Yang
271ad57160 feat: special ip address 'localhost' (#2935) 2023-06-30 03:54:24 +00:00
Alex Yang
4adbe64a54 fix(web): disable notification center (#2934) 2023-06-30 03:29:05 +00:00
DarkSky
50a8a147fd ci: make helm release only on bump version (#2928) 2023-06-30 02:02:46 +00:00
Ikko Eltociear Ashimine
eaea8e9368 refactor: fix typo in notification-center/index.tsx (#2929) 2023-06-30 09:55:09 +08:00
Hyden Liu
9873baae9f fix: z-index on app sidebar (#2761)
Co-authored-by: Himself65 <himself65@outlook.com>
2023-06-30 01:14:44 +00:00
xiaodong zuo
bc3ce7395e feat: export page as file (#2923) 2023-06-29 21:58:02 +00:00
Alex Yang
8a7908c692 fix(electron): window only ui (#2926) 2023-06-29 16:15:44 +00:00
LongYinan
8021efd81a build: affine Node.js server charts (#2895) 2023-06-29 14:02:46 +00:00
Qi
d7fcad2d0d feat: add and modify test case for new settings modal (#2925) 2023-06-29 12:54:45 +00:00
Alex Yang
b1d2d77263 docs: set nodejs version to 18.16.1 2023-06-29 20:00:57 +08:00
Alex Yang
2c772bd81b v0.7.0-canary.24 2023-06-29 18:50:48 +08:00
JimmFly
7f00011542 chore: update changelog link and remove obsolete changelog components (#2918) 2023-06-29 10:19:26 +00:00
Alex Yang
f76d8b8818 chore: bump blocksuite to 0.0.0-20230629084521-542de4e8-nightly (#2921) 2023-06-29 09:42:47 +00:00
Alex Yang
1d6b39dec9 ci: allow codecov upload failure (#2922) 2023-06-29 09:39:16 +00:00
Qi
5cfdf6c7e2 fix: a serise of ui issues of new setting (#2920)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-06-29 09:25:42 +00:00
Alex Yang
8410d83744 refactor: rootWorkspacesMetadataAtom loading logic (#2882) 2023-06-29 08:48:12 +00:00
DarkSky
8a2dac9718 fix: incorrect formatting (#2917) 2023-06-29 08:25:43 +00:00
JimmFly
5ad2908760 chore: update translation (#2916)
Co-authored-by: zuozijian3720 <zuozijian1994@gmail.com>
2023-06-29 08:20:25 +00:00
Alex Yang
5b8771485e docs: add apps/README.md 2023-06-29 16:07:30 +08:00
Alex Yang
ed8480caf0 ci: split migration test 2023-06-29 15:11:16 +08:00
Alex Yang
42ef3c0fc2 test: migration test in real world (#2885) 2023-06-29 06:50:26 +00:00
Alex Yang
e08ee9b7ff ci: add prettier format check (#2908) 2023-06-29 04:13:35 +00:00
liuyi
2c95bfcc3d feat(storage): binding jwst storage to node (#2808) 2023-06-29 01:45:45 +00:00
Alex Yang
86616e152d build: disable sqlite provider in canary 2023-06-29 10:00:41 +08:00
Peng Xiao
b1f478ee5e fix: updater color updates (#2913) 2023-06-28 17:21:07 +00:00
DarkSky
6b0f9fbdad feat: add deployment guide & fix pod label (#2912) 2023-06-28 17:12:23 +00:00
Alex Yang
da3f2b784a ci: fix output variable 2023-06-29 01:20:35 +08:00
Alex Yang
acb140ab78 v0.7.0-canary.23 2023-06-29 00:40:50 +08:00
Alex Yang
0b74bd9bfe ci: use production environment 2023-06-29 00:40:50 +08:00
Alex Yang
acfc030d16 ci: fix package version output 2023-06-29 00:40:50 +08:00
Alex Yang
d0d04ce376 v0.7.0-canary.22 2023-06-29 00:27:17 +08:00
Alex Yang
2250f42d2a ci: fix tag version 2023-06-29 00:26:48 +08:00
Alex Yang
887434fea4 v0.7.0-canary.21 2023-06-29 00:23:06 +08:00
Alex Yang
9b817c4b79 ci: automatically build canary release (#2911) 2023-06-28 15:53:32 +00:00
Alex Yang
ea03bbfb2d ci: add codeql check to merge group (#2909) 2023-06-28 15:07:27 +00:00
Qi
db40cd35c6 feat: migrate workspace setting with new design to setting modal (#2900)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-06-28 14:45:33 +00:00
Alex Yang
aabac9e921 chore: bump typescript version (#2906) 2023-06-28 12:57:33 +00:00
Alex Yang
0a91c41e0a chore: codesandbox setup (#2907) 2023-06-28 12:32:56 +00:00
DarkSky
d6addc0d0b docs: improve helm ci & document (#2902) 2023-06-28 12:30:02 +00:00
Alex Yang
91d3b76be5 refactor(storybook): move to apps folder (#2901) 2023-06-28 12:29:52 +00:00
Alex Yang
3eed009270 feat: add rule 'sonarjs/no-identical-functions' (#2905) 2023-06-28 12:29:12 +00:00
Alex Yang
bc14d54cfa chore: update pre-commit hook (#2904) 2023-06-28 11:24:37 +00:00
Alex Yang
5496969e58 refactor: environment setup (#2898)
Co-authored-by: Simon He <57086651+Simon-He95@users.noreply.github.com>
2023-06-28 11:19:19 +00:00
Alex Yang
80c2a78273 fix(web): bypass adapter list error (#2903) 2023-06-28 11:06:13 +00:00
Alex Yang
92f378aefc test(server): watch mode (#2893) 2023-06-28 10:00:06 +00:00
Alex Yang
877ceee698 ci: enable merge group (#2899) 2023-06-28 09:56:02 +00:00
Alex Yang
7960b6a22e feat: update migration test page (#2871) 2023-06-28 16:46:08 +08:00
Alex Yang
fa45d8a718 build: unify build flags (#2891) 2023-06-28 16:45:05 +08:00
Alex Yang
87574c9993 build: fix i18n output (#2896) 2023-06-28 16:40:41 +08:00
390 changed files with 6596 additions and 6952 deletions

View File

@@ -22,9 +22,7 @@
"templates",
"y-indexeddb",
"debug",
"storage",
"infra",
"plugin-infra"
"storage"
]
]
}

View File

@@ -8,4 +8,3 @@ _next
lib
.eslintrc.js
packages/i18n/src/i18n-generated.ts
e2e-dist-*

View File

@@ -21,11 +21,6 @@ const createPattern = packageName => [
message: 'Do not import package itself',
allowTypeImports: false,
},
{
group: ['@blocksuite/store'],
message: "Import from '@blocksuite/global/utils'",
importNames: ['assertExists', 'assertEquals'],
},
];
const allPackages = [
@@ -139,11 +134,6 @@ const config = {
message: "Don't import from src",
allowTypeImports: false,
},
{
group: ['@blocksuite/store'],
message: "Import from '@blocksuite/global/utils'",
importNames: ['assertExists', 'assertEquals'],
},
],
},
],

View File

@@ -121,7 +121,3 @@ runs:
run: node apps/electron/node_modules/electron/install.js
env:
ELECTRON_OVERRIDE_DIST_PATH: ./node_modules/.cache/electron
- name: Build Infra
shell: bash
run: yarn run build:infra

View File

@@ -50,12 +50,12 @@ jobs:
- name: Run Type Check
run: yarn typecheck
- name: Run ESLint
run: yarn lint:eslint --max-warnings=0
run: yarn lint --max-warnings=0 --cache
- name: Run Prettier
# Set nmMode in `actions/setup-node` will modify the .yarnrc.yml
run: |
git checkout .yarnrc.yml
yarn lint:prettier
yarn prettier . --ignore-unknown --cache --check
- name: Run circular
run: yarn circular
- name: Upload server dist
@@ -232,7 +232,6 @@ jobs:
- name: Run playwright tests
run: yarn e2e --forbid-only --shard=${{ matrix.shard }}/${{ strategy.job-total }}
working-directory: tests/affine-local
env:
COVERAGE: true
@@ -353,11 +352,8 @@ jobs:
env:
NATIVE_TEST: 'true'
- name: Download static resource artifact
uses: actions/download-artifact@v3
with:
name: next-js-static
path: apps/electron/resources/web-static
- name: Build Infra
run: yarn run build:infra
- name: Build Plugins
run: yarn run build:plugins
@@ -365,6 +361,12 @@ jobs:
- name: Build Desktop Layers
run: yarn workspace @affine/electron build
- name: Download static resource artifact
uses: actions/download-artifact@v3
with:
name: next-js-static
path: ./apps/electron/resources/web-static
- name: Run desktop tests
if: ${{ matrix.spec.test && matrix.spec.os == 'ubuntu-latest' }}
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine/electron test
@@ -377,17 +379,6 @@ jobs:
env:
COVERAGE: true
- name: Make bundle
if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }}
run: yarn workspace @affine/electron make --platform=darwin --arch=arm64
- name: Bundle output check
if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }}
run: |
./scripts/unzip-macos-arm64.sh
yarn ts-node-esm ./scripts/macos-arm64-output-check.mts
working-directory: apps/electron
- name: Collect code coverage report
if: ${{ matrix.spec.test }}
run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov
@@ -421,6 +412,9 @@ jobs:
with:
electron-install: false
- name: Build Infra
run: yarn run build:infra
- name: Unit Test
run: yarn nx test:coverage @affine/monorepo

View File

@@ -34,7 +34,7 @@ jobs:
runs-on: ubuntu-latest
environment: production
outputs:
version: 0.0.0-internal.${{ steps.version.outputs.version }}
version: 0.0.0-${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v3
- uses: toeverything/set-build-version@latest
@@ -123,6 +123,9 @@ jobs:
name: before-make-web-static
path: apps/electron/resources/web-static
- name: Build Infra
run: yarn run build:infra
- name: Build Plugins
run: yarn run build:plugins

View File

@@ -34,11 +34,11 @@ jobs:
main-branch-name: master
number-of-agents: 5
init-commands: |
yarn exec nx-cloud start-ci-run --stop-agents-after="build" --agent-count=5
yarn exec nx-cloud start-ci-run --stop-agents-after="build" --agent-count=3
environment-variables: |
BUILD_TYPE=canary
# parallel-commands: |
# yarn exec nx-cloud record -- yarn exec nx format:check
parallel-commands: |
yarn exec nx-cloud record -- yarn exec nx format:check
parallel-commands-on-agents: |
yarn exec nx affected --target=build --parallel=5
timeout: 60

View File

@@ -123,6 +123,9 @@ jobs:
name: before-make-web-static
path: apps/electron/resources/web-static
- name: Build Infra
run: yarn run build:infra
- name: Build Plugins
run: yarn run build:plugins

View File

@@ -1,22 +0,0 @@
name: Deploy Cloudflare Worker
on:
push:
branches:
- master
paths:
- packages/workers/**
jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy
environment: production
steps:
- uses: actions/checkout@v2
- name: Publish
uses: cloudflare/wrangler-action@2.0.0
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
workingDirectory: 'packages/workers'

7
.gitignore vendored
View File

@@ -60,10 +60,9 @@ out/
storybook-static
i18n-generated.ts
test-results
playwright-report
playwright/.cache
download
/test-results/
/playwright-report/
/playwright/.cache/
# Cache
.eslintcache

View File

@@ -2,16 +2,7 @@
. "$(dirname -- "$0")/_/husky.sh"
# check lockfile is up to date
yarn install --mode=skip-build --inline-builds --immutable
# build infra code
yarn -T run build:infra
# generate prisma client type
yarn workspace @affine/server prisma generate
# generate i18n
yarn i18n-codegen gen
yarn install --mode=update-lockfile
# lint staged files
yarn exec lint-staged

1
.npmrc
View File

@@ -1,3 +1,2 @@
shell-emulator=true
electron_mirror="https://cdn.npmmirror.com/binaries/electron/"
engine-strict=true

View File

@@ -10,5 +10,3 @@ dist
.yarn
tests/affine-legacy/0.7.0-canary.18/static
.github/helm
_next
storybook-static

View File

@@ -123,8 +123,6 @@ If you have questions, you are welcome to contact us. One of the best places to
## Plugins
> Plugins are a way to extend the functionality of AFFiNE.
>
> (Currently, plugins are under heavy development, and the SDK is not yet available.)
| Name | |
| ------------------------------------------------ | ----------------------------------------- |

View File

@@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />

View File

@@ -1,6 +1,6 @@
{
"name": "@affine/docs",
"version": "0.7.0-beta.0",
"version": "0.7.0-canary.33",
"type": "module",
"private": true,
"scripts": {
@@ -10,14 +10,14 @@
},
"dependencies": {
"@affine/component": "workspace:*",
"@blocksuite/block-std": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/blocks": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/editor": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/global": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/lit": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/store": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/block-std": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/blocks": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/editor": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/global": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/lit": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/store": "0.0.0-20230704023331-d9fc4854-nightly",
"express": "^4.18.2",
"jotai": "^2.2.2",
"jotai": "^2.2.1",
"react": "18.3.0-canary-1fdacbefd-20230630",
"react-dom": "18.3.0-canary-1fdacbefd-20230630",
"react-server-dom-webpack": "18.3.0-canary-1fdacbefd-20230630",

View File

@@ -13,5 +13,3 @@ resources/web-static
!.yarn/sdks
!.yarn/versions
dev.json
zip-out

View File

@@ -149,35 +149,3 @@ test('windows only check', async ({ page }) => {
await expect(windowOnlyUI).not.toBeVisible();
}
});
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('current-workspace-label').click();
expect(await page.getByTestId('workspace-name-input').inputValue()).toBe(
'Delete Me'
);
const contentElement = await page.getByTestId('setting-modal-content');
const boundingBox = await contentElement.boundingBox();
if (!boundingBox) {
throw new Error('boundingBox is null');
}
await page.mouse.move(
boundingBox.x + boundingBox.width / 2,
boundingBox.y + boundingBox.height / 2
);
await page.mouse.wheel(0, 500);
await page.getByTestId('delete-workspace-button').click();
await page.getByTestId('delete-workspace-input').type('Delete Me');
await page.getByTestId('delete-workspace-confirm-button').click();
await page.waitForTimeout(1000);
expect(await page.getByTestId('workspace-name').textContent()).toBe(
'Demo Workspace'
);
});

View File

@@ -16,8 +16,6 @@ function generateUUID() {
return crypto.randomUUID();
}
type RoutePath = 'setting';
export const test = base.extend<{
page: Page;
electronApp: ElectronApplication;
@@ -26,8 +24,9 @@ export const test = base.extend<{
appData: string;
sessionData: string;
};
router: {
goto: (path: RoutePath) => Promise<void>;
workspace: {
// get current workspace
current: () => Promise<any>; // todo: type
};
}>({
page: async ({ electronApp }, use) => {
@@ -42,6 +41,10 @@ export const test = base.extend<{
});
});
}
const logFilePath = await page.evaluate(async () => {
// @ts-expect-error
return window.apis?.debug.logFilePath();
});
// wat for blocksuite to be loaded
await page.waitForSelector('v-line');
if (enableCoverage) {
@@ -68,6 +71,10 @@ export const test = base.extend<{
);
}
await page.close();
if (logFilePath) {
const logs = await fs.readFile(logFilePath, 'utf-8');
console.log(logs);
}
},
electronApp: async ({}, use) => {
// a random id to avoid conflicts between tests
@@ -117,4 +124,14 @@ export const test = base.extend<{
});
await use(appInfo);
},
workspace: async ({ page }, use) => {
await use({
current: async () => {
return await page.evaluate(async () => {
// @ts-expect-error
return globalThis.currentWorkspace;
});
},
});
},
});

View File

@@ -5,7 +5,7 @@ import fs from 'fs-extra';
import { test } from './fixture';
test('check workspace has a DB file', async ({ appInfo, workspace }) => {
test.skip('check workspace has a DB file', async ({ appInfo, workspace }) => {
const w = await workspace.current();
const dbPath = path.join(
appInfo.sessionData,
@@ -19,11 +19,9 @@ test('check workspace has a DB file', async ({ appInfo, workspace }) => {
test.skip('move workspace db file', async ({ page, appInfo, workspace }) => {
const w = await workspace.current();
await page.getByTestId('slider-bar-workspace-setting-button').click();
await expect(page.getByTestId('setting-modal')).toBeVisible();
// goto workspace setting
await page.getByTestId('workspace-list-item').click();
const settingButton = page.getByTestId('slider-bar-workspace-setting-button');
// goto settings
await settingButton.click();
const tmpPath = path.join(appInfo.sessionData, w.id + '-tmp-dir');
@@ -44,26 +42,21 @@ test.skip('move workspace db file', async ({ page, appInfo, workspace }) => {
expect(files.some(f => f.endsWith('.affine'))).toBe(true);
});
test('export then add', async ({ page, appInfo, workspace }) => {
test.skip('export then add', async ({ page, appInfo, workspace }) => {
const w = await workspace.current();
await page.getByTestId('slider-bar-workspace-setting-button').click();
await expect(page.getByTestId('setting-modal')).toBeVisible();
const settingButton = page.getByTestId('slider-bar-workspace-setting-button');
// goto settings
await settingButton.click();
const originalId = w.id;
const newWorkspaceName = 'new-test-name';
// goto workspace setting
await page.getByTestId('workspace-list-item').click();
await page.waitForTimeout(500);
// change workspace name
await page.getByTestId('workspace-name-input').fill(newWorkspaceName);
await page.getByTestId('save-workspace-name').click();
await page.waitForSelector('text="Update workspace name success"');
await page.waitForTimeout(500);
await page.click('[data-tab-key="export"]');
const tmpPath = path.join(appInfo.sessionData, w.id + '-tmp.db');
@@ -80,11 +73,10 @@ test('export then add', async ({ page, appInfo, workspace }) => {
expect(await fs.exists(tmpPath)).toBe(true);
await page.getByTestId('modal-close-button').click();
// add workspace
// we are reusing the same db file so that we don't need to maintain one
// in the codebase
await page.getByTestId('current-workspace').click();
await page.getByTestId('add-or-new-workspace').click();

View File

@@ -26,8 +26,6 @@ const arch =
? process.argv[process.argv.indexOf('--arch') + 1]
: process.arch;
const windowsIconUrl = `https://cdn.affine.pro/app-icons/icon_${buildType}.ico`;
/**
* @type {import('@electron-forge/shared-types').ForgeConfig}
*/
@@ -97,7 +95,6 @@ module.exports = {
config: {
name: 'AFFiNE',
setupIcon: icoPath,
iconUrl: windowsIconUrl,
loadingGif: './resources/icons/affine_installing.gif',
},
},

View File

@@ -1,7 +1,7 @@
{
"name": "@affine/electron",
"private": true,
"version": "0.7.0-beta.0",
"version": "0.7.0-canary.33",
"author": "affine",
"repository": {
"url": "https://github.com/toeverything/AFFiNE",
@@ -12,11 +12,11 @@
"scripts": {
"dev": "yarn cross-env DEV_SERVER_URL=http://localhost:8080 node scripts/dev.mjs",
"dev:prod": "yarn node scripts/dev.mjs",
"build": "NODE_ENV=production zx scripts/build-layers.mjs",
"build": "zx scripts/build-layers.mjs",
"generate-assets": "zx scripts/generate-assets.mjs",
"package": "electron-forge package",
"make": "electron-forge make",
"test": "DEBUG=pw:browser yarn -T run playwright test -c ./playwright.config.ts"
"test": "DEBUG=pw:browser playwright test"
},
"config": {
"forge": "./forge.config.js"
@@ -24,12 +24,11 @@
"main": "./dist/main.js",
"devDependencies": {
"@affine-test/kit": "workspace:*",
"@affine/env": "workspace:*",
"@affine/native": "workspace:*",
"@blocksuite/blocks": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/editor": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/lit": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/store": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/blocks": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/editor": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/lit": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/store": "0.0.0-20230704023331-d9fc4854-nightly",
"@electron-forge/cli": "^6.2.1",
"@electron-forge/core": "^6.2.1",
"@electron-forge/core-utils": "^6.2.1",
@@ -49,7 +48,8 @@
"electron-window-state": "^5.0.3",
"esbuild": "^0.18.11",
"fs-extra": "^11.1.1",
"jotai": "^2.2.2",
"jotai": "^2.2.1",
"playwright": "=1.33.0",
"ts-node": "^10.9.1",
"undici": "^5.22.1",
"uuid": "^9.0.0",
@@ -81,6 +81,7 @@
"hoistingLimits": "workspaces"
},
"peerDependencies": {
"playwright": "*",
"ts-node": "*"
}
}

View File

@@ -38,12 +38,7 @@ export const config = () => {
bundle: true,
target: `node${NODE_MAJOR_VERSION}`,
platform: 'node',
external: [
'electron',
'electron-updater',
'@toeverything/plugin-infra',
'yjs',
],
external: ['electron', 'electron-updater', '@toeverything/plugin-infra'],
define: define,
format: 'cjs',
loader: {

View File

@@ -1,44 +0,0 @@
import { fileURLToPath } from 'node:url';
import { readdir } from 'node:fs/promises';
const outputRoot = fileURLToPath(
new URL(
'../zip-out/AFFiNE-canary.app/Contents/Resources/app',
import.meta.url
)
);
const outputList = [
[
'dist',
[
'main.js',
'helper.js',
'preload.js',
'affine.darwin-arm64.node',
'plugins',
'workers',
],
],
['dist/plugins', ['bookmark-block']],
['dist/plugins/bookmark-block', ['index.mjs']],
['dist/workers', ['plugin.worker.js']],
[
'node_modules/@toeverything/plugin-infra/dist',
['manager.js', 'manager.cjs'],
],
['node_modules/@blocksuite/global/dist', ['utils.js']],
['node_modules/jotai', ['vanilla.js']],
] as [entry: string, expected: string[]][];
await Promise.all(
outputList.map(async ([entry, output]) => {
const files = await readdir(`${outputRoot}/${entry}`);
output.forEach(file => {
if (!files.includes(file)) {
throw new Error(`File ${entry}/${file} not found`);
}
});
})
);
console.log('Output check passed');

View File

@@ -1,16 +0,0 @@
#!/bin/bash
# Set the directory
dir="./out/canary/make/zip/darwin/arm64"
# Get the first file
file=$(ls -1 $dir | head -n 1)
# Check if file exists and is a zip file
if [ -f "$dir/$file" ] && [ ${file: -4} == ".zip" ]
then
# Unzip the file
unzip "$dir/$file" -d "zip-out"
else
echo "No zip file found"
fi

View File

@@ -1,69 +0,0 @@
import path from 'node:path';
import { SqliteConnection } from '@affine/native';
import { afterEach, describe, expect, it, vi } from 'vitest';
import * as Y from 'yjs';
import { removeWithRetry } from '../../../../tests/utils';
import { copyToTemp, migrateToSubdocAndReplaceDatabase } from '../migration';
const tmpDir = path.join(__dirname, 'tmp');
const testDBFilePath = path.resolve(__dirname, 'old-db.affine');
const appDataPath = path.join(tmpDir, 'app-data');
vi.mock('../../main-rpc', () => ({
mainRPC: {
getPath: async () => appDataPath,
},
}));
afterEach(async () => {
await removeWithRetry(tmpDir);
});
describe('migrateToSubdocAndReplaceDatabase', () => {
it('should migrate and replace the database', async () => {
const copiedDbFilePath = await copyToTemp(testDBFilePath);
await migrateToSubdocAndReplaceDatabase(copiedDbFilePath);
const db = new SqliteConnection(copiedDbFilePath);
await db.connect();
// check if db has two rows, one for root doc and one for subdoc
const rows = await db.getAllUpdates();
expect(rows.length).toBe(2);
const rootUpdate = rows.find(row => row.docId === undefined)!.data;
const subdocUpdate = rows.find(row => row.docId !== undefined)!.data;
expect(rootUpdate).toBeDefined();
expect(subdocUpdate).toBeDefined();
// apply updates
const rootDoc = new Y.Doc();
Y.applyUpdate(rootDoc, rootUpdate);
// check if root doc has one subdoc
expect(rootDoc.subdocs.size).toBe(1);
// populates subdoc
Y.applyUpdate(rootDoc.subdocs.values().next().value, subdocUpdate);
// check if root doc's meta is correct
const meta = rootDoc.getMap('meta').toJSON();
expect(meta.workspaceVersion).toBe(1);
expect(meta.name).toBe('hiw');
expect(meta.pages.length).toBe(1);
const pageMeta = meta.pages[0];
expect(pageMeta.title).toBe('Welcome to AFFiNEd');
// get the subdoc through id
const subDoc = rootDoc
.getMap('spaces')
.get(`space:${pageMeta.id}`) as Y.Doc;
expect(subDoc).toEqual(rootDoc.subdocs.values().next().value);
await db.close();
});
});

View File

@@ -119,8 +119,6 @@ export abstract class BaseSQLiteAdapter {
`[SQLiteAdapter][${this.role}] addUpdateToSQLite`,
'length:',
updates.length,
'docids',
updates.map(u => u.docId),
performance.now() - start,
'ms'
);

View File

@@ -1,55 +0,0 @@
import { resolve } from 'node:path';
import { migrateToSubdoc } from '@affine/env/blocksuite';
import { SqliteConnection } from '@affine/native';
import fs from 'fs-extra';
import { nanoid } from 'nanoid';
import * as Y from 'yjs';
import { mainRPC } from '../main-rpc';
export const migrateToSubdocAndReplaceDatabase = async (path: string) => {
const db = new SqliteConnection(path);
await db.connect();
const rows = await db.getAllUpdates();
const originalDoc = new Y.Doc();
// 1. apply all updates to the root doc
rows.forEach(row => {
Y.applyUpdate(originalDoc, row.data);
});
// 2. migrate using migrateToSubdoc
const migratedDoc = migrateToSubdoc(originalDoc);
// 3. replace db rows with the migrated doc
await replaceRows(db, migratedDoc, true);
// 4. close db
await db.close();
};
export const copyToTemp = async (path: string) => {
const tmpDirPath = resolve(await mainRPC.getPath('sessionData'), 'tmp');
const tmpFilePath = resolve(tmpDirPath, nanoid());
await fs.ensureDir(tmpDirPath);
await fs.copyFile(path, tmpFilePath);
return tmpFilePath;
};
async function replaceRows(
db: SqliteConnection,
doc: Y.Doc,
isRoot: boolean
): Promise<void> {
const migratedUpdates = Y.encodeStateAsUpdate(doc);
const docId = isRoot ? undefined : doc.guid;
const rows = [{ data: migratedUpdates, docId: docId }];
await db.replaceUpdates(docId, rows);
await Promise.all(
[...doc.subdocs].map(async subdoc => {
await replaceRows(db, subdoc, false);
})
);
}

View File

@@ -115,43 +115,19 @@ export class SecondaryWorkspaceSQLiteDB extends BaseSQLiteAdapter {
}
setupListener(docId?: string) {
logger.debug(
'SecondaryWorkspaceSQLiteDB:setupListener',
this.workspaceId,
docId
);
const doc = this.getDoc(docId);
const upstreamDoc = this.upstream.getDoc(docId);
if (!doc || !upstreamDoc) {
logger.warn(
'[SecondaryWorkspaceSQLiteDB] setupListener: doc not found',
docId
);
if (!doc) {
return;
}
const onUpstreamUpdate = (update: Uint8Array, origin: YOrigin) => {
logger.debug(
'SecondaryWorkspaceSQLiteDB:onUpstreamUpdate',
origin,
this.workspaceId,
docId,
update.length
);
if (origin === 'renderer' || origin === 'self') {
if (origin === 'renderer') {
// update to upstream yDoc should be replicated to self yDoc
this.applyUpdate(update, 'upstream', docId);
}
};
const onSelfUpdate = async (update: Uint8Array, origin: YOrigin) => {
logger.debug(
'SecondaryWorkspaceSQLiteDB:onSelfUpdate',
origin,
this.workspaceId,
docId,
update.length
);
// for self update from upstream, we need to push it to external DB
if (origin === 'upstream') {
await this.addUpdateToUpdateQueue({
@@ -171,19 +147,15 @@ export class SecondaryWorkspaceSQLiteDB extends BaseSQLiteAdapter {
});
};
doc.subdocs.forEach(subdoc => {
this.setupListener(subdoc.guid);
});
// listen to upstream update
this.upstream.yDoc.on('update', onUpstreamUpdate);
doc.on('update', onSelfUpdate);
doc.on('subdocs', onSubdocs);
this.yDoc.on('update', onSelfUpdate);
this.yDoc.on('subdocs', onSubdocs);
this.unsubscribers.add(() => {
this.upstream.yDoc.off('update', onUpstreamUpdate);
doc.off('update', onSelfUpdate);
doc.off('subdocs', onSubdocs);
this.yDoc.off('update', onSelfUpdate);
this.yDoc.off('subdocs', onSubdocs);
});
}
@@ -216,10 +188,7 @@ export class SecondaryWorkspaceSQLiteDB extends BaseSQLiteAdapter {
if (doc) {
Y.applyUpdate(this.yDoc, data, origin);
} else {
logger.warn(
'[SecondaryWorkspaceSQLiteDB] applyUpdate: doc not found',
docId
);
logger.warn('applyUpdate: doc not found', docId);
}
};

View File

@@ -18,10 +18,7 @@ export class WorkspaceSQLiteDB extends BaseSQLiteAdapter {
update$ = new Subject<void>();
constructor(
public override path: string,
public workspaceId: string
) {
constructor(public override path: string, public workspaceId: string) {
super(path);
}
@@ -52,21 +49,9 @@ export class WorkspaceSQLiteDB extends BaseSQLiteAdapter {
};
setupListener(docId?: string) {
logger.debug(
'WorkspaceSQLiteDB:setupListener',
this.workspaceId,
docId,
this.getWorkspaceName()
);
const doc = this.getDoc(docId);
if (doc) {
const onUpdate = async (update: Uint8Array, origin: YOrigin) => {
logger.debug(
'WorkspaceSQLiteDB:onUpdate',
this.workspaceId,
docId,
update.length
);
const insertRows = [{ data: update, docId }];
if (origin === 'renderer') {
await this.addUpdateToSQLite(insertRows);
@@ -80,11 +65,7 @@ export class WorkspaceSQLiteDB extends BaseSQLiteAdapter {
logger.debug('external update', this.workspaceId);
}
};
doc.subdocs.forEach(subdoc => {
this.setupListener(subdoc.guid);
});
const onSubdocs = ({ added }: { added: Set<Y.Doc> }) => {
logger.info('onSubdocs', this.workspaceId, docId, added);
added.forEach(subdoc => {
this.setupListener(subdoc.guid);
});
@@ -151,7 +132,7 @@ export class WorkspaceSQLiteDB extends BaseSQLiteAdapter {
if (doc) {
Y.applyUpdate(doc, data, origin);
} else {
logger.warn('[WorkspaceSQLiteDB] applyUpdate: doc not found', docId);
logger.warn('applyUpdate: doc not found', docId);
}
};

View File

@@ -1,11 +1,9 @@
import path from 'node:path';
import { ValidationResult } from '@affine/native';
import fs from 'fs-extra';
import { nanoid } from 'nanoid';
import { ensureSQLiteDB } from '../db/ensure-db';
import { copyToTemp, migrateToSubdocAndReplaceDatabase } from '../db/migration';
import type { WorkspaceSQLiteDB } from '../db/workspace-db-adapter';
import { logger } from '../logger';
import { mainRPC } from '../main-rpc';
@@ -57,7 +55,6 @@ const ErrorMessages = [
'DB_FILE_ALREADY_LOADED',
'DB_FILE_PATH_INVALID',
'DB_FILE_INVALID',
'DB_FILE_MIGRATION_FAILED',
'FILE_ALREADY_EXISTS',
'UNKNOWN_ERROR',
] as const;
@@ -194,42 +191,27 @@ export async function loadDBFile(): Promise<LoadDBFileResult> {
],
message: 'Load Workspace from a AFFiNE file',
}));
let originalPath = ret.filePaths?.[0];
if (ret.canceled || !originalPath) {
const filePath = ret.filePaths?.[0];
if (ret.canceled || !filePath) {
logger.info('loadDBFile canceled');
return { canceled: true };
}
// the imported file should not be in app data dir
if (originalPath.startsWith(await getWorkspacesBasePath())) {
if (filePath.startsWith(await getWorkspacesBasePath())) {
logger.warn('loadDBFile: db file in app data dir');
return { error: 'DB_FILE_PATH_INVALID' };
}
if (await dbFileAlreadyLoaded(originalPath)) {
if (await dbFileAlreadyLoaded(filePath)) {
logger.warn('loadDBFile: db file already loaded');
return { error: 'DB_FILE_ALREADY_LOADED' };
}
const { SqliteConnection } = await import('@affine/native');
const validationResult = await SqliteConnection.validate(originalPath);
if (validationResult === ValidationResult.MissingDocIdColumn) {
try {
const tmpDBPath = await copyToTemp(originalPath);
await migrateToSubdocAndReplaceDatabase(tmpDBPath);
originalPath = tmpDBPath;
} catch (error) {
logger.warn(`loadDBFile, migration failed: ${originalPath}`, error);
return { error: 'DB_FILE_MIGRATION_FAILED' };
}
}
if (
validationResult !== ValidationResult.MissingDocIdColumn &&
validationResult !== ValidationResult.Valid
) {
if (!(await SqliteConnection.validate(filePath))) {
// TODO: report invalid db file error?
return { error: 'DB_FILE_INVALID' }; // invalid db file
}
@@ -238,12 +220,14 @@ export async function loadDBFile(): Promise<LoadDBFileResult> {
const internalFilePath = await getWorkspaceDBPath(workspaceId);
await fs.ensureDir(await getWorkspacesBasePath());
await fs.copy(originalPath, internalFilePath);
logger.info(`loadDBFile, copy: ${originalPath} -> ${internalFilePath}`);
await fs.copy(filePath, internalFilePath);
logger.info(`loadDBFile, copy: ${filePath} -> ${internalFilePath}`);
await storeWorkspaceMeta(workspaceId, {
id: workspaceId,
mainDBPath: internalFilePath,
secondaryDBPath: filePath,
});
return { workspaceId };

View File

@@ -1,4 +1,3 @@
import type { RendererToHelper } from '@toeverything/infra/preload/electron';
import { AsyncCall } from 'async-call-rpc';
import { events, handlers } from './exposed';
@@ -31,7 +30,7 @@ function setupRendererConnection(rendererPort: Electron.MessagePortMain) {
});
}
);
const rpc = AsyncCall<RendererToHelper>(
const rpc = AsyncCall<PeersAPIs.RendererToHelper>(
Object.fromEntries(flattenedHandlers),
{
channel: {

View File

@@ -1,16 +1,12 @@
import type {
HelperToMain,
MainToHelper,
} from '@toeverything/infra/preload/electron';
import { AsyncCall } from 'async-call-rpc';
import { getExposedMeta } from './exposed';
const helperToMainServer: HelperToMain = {
const helperToMainServer: PeersAPIs.HelperToMain = {
getMeta: () => getExposedMeta(),
};
export const mainRPC = AsyncCall<MainToHelper>(helperToMainServer, {
export const mainRPC = AsyncCall<PeersAPIs.MainToHelper>(helperToMainServer, {
strict: {
unknownMessage: false,
},

View File

@@ -21,7 +21,7 @@ type WithoutFirstParameter<T> = T extends (_: any, ...args: infer P) => infer R
// however this is too hard to be typed correctly
async function dispatch<
T extends keyof MainIPCHandlerMap,
F extends keyof MainIPCHandlerMap[T],
F extends keyof MainIPCHandlerMap[T]
>(
namespace: T,
functionName: F,

View File

@@ -1,7 +1,7 @@
import { app, Menu } from 'electron';
import { revealLogFile } from '../logger';
import { checkForUpdates } from '../updater';
import { checkForUpdatesAndNotify } from '../updater';
import { isMacOS } from '../utils';
import { applicationMenuSubjects } from './subject';
@@ -125,7 +125,7 @@ export function createApplicationMenu() {
{
label: 'Check for Updates',
click: async () => {
await checkForUpdates(true);
await checkForUpdatesAndNotify(true);
},
},
],

View File

@@ -1,9 +1,5 @@
import path from 'node:path';
import type {
HelperToMain,
MainToHelper,
} from '@toeverything/infra/preload/electron';
import { type _AsyncVersionOf, AsyncCall } from 'async-call-rpc';
import {
app,
@@ -40,7 +36,7 @@ class HelperProcessManager {
#process: UtilityProcess;
// a rpc server for the main process -> helper process
rpc?: _AsyncVersionOf<HelperToMain>;
rpc?: _AsyncVersionOf<PeersAPIs.HelperToMain>;
static instance = new HelperProcessManager();
@@ -90,13 +86,13 @@ class HelperProcessManager {
]);
const appMethods = pickAndBind(app, ['getPath']);
const mainToHelperServer: MainToHelper = {
const mainToHelperServer: PeersAPIs.MainToHelper = {
...dialogMethods,
...shellMethods,
...appMethods,
};
this.rpc = AsyncCall<HelperToMain>(mainToHelperServer, {
this.rpc = AsyncCall<PeersAPIs.HelperToMain>(mainToHelperServer, {
strict: {
// the channel is shared for other purposes as well so that we do not want to
// restrict to only JSONRPC messages

View File

@@ -1,5 +1,5 @@
import { app } from 'electron';
import { autoUpdater } from 'electron-updater';
import type { AppUpdater } from 'electron-updater';
import { z } from 'zod';
import { logger } from '../logger';
@@ -20,55 +20,56 @@ export const buildType = ReleaseTypeSchema.parse(envBuildType);
const mode = process.env.NODE_ENV;
const isDev = mode === 'development';
let _autoUpdater: AppUpdater | null = null;
export const quitAndInstall = async () => {
autoUpdater.quitAndInstall();
_autoUpdater?.quitAndInstall();
};
let lastCheckTime = 0;
export const checkForUpdates = async (force = true) => {
export const checkForUpdatesAndNotify = async (force = true) => {
if (!_autoUpdater) {
return void 0;
}
// check every 30 minutes (1800 seconds) at most
if (force || lastCheckTime + 1000 * 1800 < Date.now()) {
lastCheckTime = Date.now();
return await autoUpdater.checkForUpdates();
return await _autoUpdater.checkForUpdatesAndNotify();
}
return void 0;
};
export const registerUpdater = async () => {
// so we wrap it in a function
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { autoUpdater } = require('electron-updater');
_autoUpdater = autoUpdater;
// skip auto update in dev mode
if (isDev) {
if (!_autoUpdater || isDev) {
return;
}
// TODO: support auto update on windows and linux
const allowAutoUpdate = isMacOS();
autoUpdater.logger = logger;
autoUpdater.autoDownload = false;
autoUpdater.allowPrerelease = buildType !== 'stable';
autoUpdater.autoInstallOnAppQuit = false;
autoUpdater.autoRunAppAfterInstall = true;
const feedUrl: Parameters<typeof autoUpdater.setFeedURL>[0] = {
_autoUpdater.autoDownload = false;
_autoUpdater.allowPrerelease = buildType !== 'stable';
_autoUpdater.autoInstallOnAppQuit = false;
_autoUpdater.autoRunAppAfterInstall = true;
_autoUpdater.setFeedURL({
channel: buildType,
provider: 'github',
repo: buildType !== 'internal' ? 'AFFiNE' : 'AFFiNE-Releases',
owner: 'toeverything',
releaseType: buildType === 'stable' ? 'release' : 'prerelease',
};
logger.debug('auto-updater feed config', feedUrl);
autoUpdater.setFeedURL(feedUrl);
});
// register events for checkForUpdatesAndNotify
autoUpdater.on('checking-for-update', () => {
logger.info('Checking for update');
});
autoUpdater.on('update-available', info => {
logger.info('Update available', info);
_autoUpdater.on('update-available', info => {
if (allowAutoUpdate) {
autoUpdater?.downloadUpdate().catch(e => {
_autoUpdater?.downloadUpdate().catch(e => {
logger.error('Failed to download update', e);
});
logger.info('Update available, downloading...', info);
@@ -78,14 +79,11 @@ export const registerUpdater = async () => {
allowAutoUpdate,
});
});
autoUpdater.on('update-not-available', info => {
logger.info('Update not available', info);
});
autoUpdater.on('download-progress', e => {
_autoUpdater.on('download-progress', e => {
logger.info(`Download progress: ${e.percent}`);
updaterSubjects.downloadProgress.next(e.percent);
});
autoUpdater.on('update-downloaded', e => {
_autoUpdater.on('update-downloaded', e => {
updaterSubjects.updateReady.next({
version: e.version,
allowAutoUpdate,
@@ -94,12 +92,12 @@ export const registerUpdater = async () => {
// updaterSubjects.clientDownloadProgress.next(100);
logger.info('Update downloaded, ready to install');
});
autoUpdater.on('error', e => {
_autoUpdater.on('error', e => {
logger.error('Error while updating client', e);
});
autoUpdater.forceDevUpdateConfig = isDev;
_autoUpdater.forceDevUpdateConfig = isDev;
app.on('activate', async () => {
await checkForUpdates(false);
await checkForUpdatesAndNotify(false);
});
};

View File

@@ -1,7 +1,7 @@
import { app } from 'electron';
import type { NamespaceHandlers } from '../type';
import { checkForUpdates, quitAndInstall } from './electron-updater';
import { checkForUpdatesAndNotify, quitAndInstall } from './electron-updater';
export const updaterHandlers = {
currentVersion: async () => {
@@ -11,7 +11,7 @@ export const updaterHandlers = {
return quitAndInstall();
},
checkForUpdatesAndNotify: async () => {
const res = await checkForUpdates(true);
const res = await checkForUpdatesAndNotify(true);
if (res) {
const { updateInfo } = res;
return {

View File

@@ -1,38 +1,14 @@
// Please add modules to `external` in `rollupOptions` to avoid wrong bundling.
// NOTE: we will generate preload types from this file
import { AsyncCall, type EventBasedChannel } from 'async-call-rpc';
import type { app, dialog, shell } from 'electron';
import { ipcRenderer } from 'electron';
import { Subject } from 'rxjs';
export interface ExposedMeta {
handlers: [string, string[]][];
events: [string, string[]][];
}
type ExposedMeta = {
handlers: [namespace: string, handlerNames: string[]][];
events: [namespace: string, eventNames: string[]][];
};
// render <-> helper
export interface RendererToHelper {
postEvent: (channel: string, ...args: any[]) => void;
}
export interface HelperToRenderer {
[key: string]: (...args: any[]) => Promise<any>;
}
// helper <-> main
export interface HelperToMain {
getMeta: () => ExposedMeta;
}
export type MainToHelper = Pick<
typeof dialog & typeof shell & typeof app,
| 'showOpenDialog'
| 'showSaveDialog'
| 'openExternal'
| 'showItemInFolder'
| 'getPath'
>;
export function getElectronAPIs() {
export function getAffineAPIs() {
const mainAPIs = getMainAPIs();
const helperAPIs = getHelperAPIs();
@@ -150,13 +126,13 @@ function getHelperAPIs() {
return val ? JSON.parse(val) : null;
})();
const rendererToHelperServer: RendererToHelper = {
const rendererToHelperServer: PeersAPIs.RendererToHelper = {
postEvent: (channel, ...args) => {
events$.next({ channel, args });
},
};
const rpc = AsyncCall<HelperToRenderer>(rendererToHelperServer, {
const rpc = AsyncCall<PeersAPIs.HelperToRenderer>(rendererToHelperServer, {
channel: helperPort$.then(helperPort =>
createMessagePortChannel(helperPort)
),
@@ -181,10 +157,10 @@ function getHelperAPIs() {
};
const setup = (meta: ExposedMeta) => {
const { handlers, events } = meta;
const { handlers: handlersMeta, events: eventsMeta } = meta;
const helperHandlers = Object.fromEntries(
handlers.map(([namespace, functionNames]) => {
handlersMeta.map(([namespace, functionNames]) => {
return [
namespace,
Object.fromEntries(
@@ -197,7 +173,7 @@ function getHelperAPIs() {
);
const helperEvents = Object.fromEntries(
events.map(([namespace, eventNames]) => {
eventsMeta.map(([namespace, eventNames]) => {
return [
namespace,
Object.fromEntries(

View File

@@ -1,10 +1,8 @@
import { contextBridge, ipcRenderer } from 'electron';
(async () => {
const { appInfo, getElectronAPIs } = await import(
'@toeverything/infra/preload/electron'
);
const { apis, events } = getElectronAPIs();
const { appInfo, getAffineAPIs } = await import('./affine-apis');
const { apis, events } = getAffineAPIs();
contextBridge.exposeInMainWorld('appInfo', appInfo);
contextBridge.exposeInMainWorld('apis', apis);

35
apps/electron/src/types.d.ts vendored Normal file
View File

@@ -0,0 +1,35 @@
declare namespace PeersAPIs {
import type { app, dialog, shell } from 'electron';
interface ExposedMeta {
handlers: [string, string[]][];
events: [string, string[]][];
}
// render <-> helper
interface RendererToHelper {
postEvent: (channel: string, ...args: any[]) => void;
}
interface HelperToRenderer {
[key: string]: (...args: any[]) => Promise<any>;
}
// helper <-> main
interface HelperToMain {
getMeta: () => ExposedMeta;
}
type MainToHelper = Pick<
typeof dialog & typeof shell & typeof app,
| 'showOpenDialog'
| 'showSaveDialog'
| 'openExternal'
| 'showItemInFolder'
| 'getPath'
>;
// render <-> main
// these are handled via IPC
// TODO: fix type
}

View File

@@ -25,9 +25,6 @@
{
"path": "../../packages/infra"
},
{
"path": "../../packages/env"
},
// Tests
{

View File

@@ -7,8 +7,7 @@
"resolveJsonModule": true,
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true,
"noEmit": false,
"outDir": "./lib/scripts"
"noEmit": false
},
"include": ["./scripts", "esbuild.main.config.ts", "esbuild.plugin.config.ts"]
}

View File

@@ -1,2 +1,2 @@
SECRET_KEY="secret"
DATABASE_URL="postgresql://affine@localhost:5432/affine"
NEXTAUTH_URL="http://localhost:8080"

View File

@@ -1,7 +1,7 @@
{
"name": "@affine/server",
"private": true,
"version": "0.7.0-beta.0",
"version": "0.7.0-canary.33",
"description": "Affine Node.js server",
"type": "module",
"bin": {
@@ -27,7 +27,6 @@
"@node-rs/crc32": "^1.7.0",
"@node-rs/jsonwebtoken": "^0.2.0",
"@prisma/client": "^4.16.2",
"cookie-parser": "^1.4.6",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"graphql": "^16.7.1",
@@ -35,22 +34,18 @@
"graphql-upload": "^16.0.2",
"lodash-es": "^4.17.21",
"next-auth": "^4.22.1",
"nodemailer": "^6.9.3",
"parse-duration": "^1.1.0",
"prisma": "^4.16.2",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1",
"semver": "^7.5.4"
"rxjs": "^7.8.1"
},
"devDependencies": {
"@affine/storage": "workspace:*",
"@napi-rs/image": "^1.6.1",
"@nestjs/testing": "^10.0.4",
"@types/cookie-parser": "^1.4.3",
"@types/express": "^4.17.17",
"@types/lodash-es": "^4.17.7",
"@types/node": "^18.16.19",
"@types/nodemailer": "^6.4.8",
"@types/supertest": "^2.0.12",
"c8": "^8.0.0",
"nodemon": "^2.0.22",

View File

@@ -233,11 +233,5 @@ export interface AFFiNEConfig {
}
>
>;
email: {
server: string;
port: number;
sender: string;
password: string;
};
};
}

View File

@@ -65,10 +65,6 @@ export const getDefaultAFFiNEConfig: () => AFFiNEConfig = () => {
OAUTH_GOOGLE_CLIENT_SECRET: 'auth.oauthProviders.google.clientSecret',
OAUTH_GITHUB_CLIENT_ID: 'auth.oauthProviders.github.clientId',
OAUTH_GITHUB_CLIENT_SECRET: 'auth.oauthProviders.github.clientSecret',
OAUTH_EMAIL_SENDER: 'auth.email.sender',
OAUTH_EMAIL_SERVER: 'auth.email.server',
OAUTH_EMAIL_PORT: 'auth.email.port',
OAUTH_EMAIL_PASSWORD: 'auth.email.password',
} satisfies AFFiNEConfig['ENV_MAP'],
env: process.env.NODE_ENV ?? 'development',
get prod() {
@@ -106,6 +102,7 @@ export const getDefaultAFFiNEConfig: () => AFFiNEConfig = () => {
},
introspection: true,
playground: true,
debug: true,
},
auth: {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -117,16 +114,8 @@ export const getDefaultAFFiNEConfig: () => AFFiNEConfig = () => {
publicKey: jwtKeyPair.publicKey,
enableSignup: true,
enableOauth: false,
get nextAuthSecret() {
return this.privateKey;
},
nextAuthSecret: '',
oauthProviders: {},
email: {
server: 'smtp.gmail.com',
port: 465,
sender: '',
password: '',
},
},
objectStorage: {
r2: {
@@ -140,7 +129,7 @@ export const getDefaultAFFiNEConfig: () => AFFiNEConfig = () => {
path: join(homedir(), '.affine-storage'),
},
},
} satisfies AFFiNEConfig;
} as const;
applyEnvToConfig(defaultConfig);

View File

@@ -1,7 +1,6 @@
/// <reference types="./global.d.ts" />
import { NestFactory } from '@nestjs/core';
import type { NestExpressApplication } from '@nestjs/platform-express';
import cookieParser from 'cookie-parser';
import { static as staticMiddleware } from 'express';
// @ts-expect-error graphql-upload is not typed
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
@@ -28,8 +27,6 @@ app.use(
})
);
app.use(cookieParser());
const config = app.get(Config);
const host = config.host ?? 'localhost';

View File

@@ -41,10 +41,7 @@ export const CurrentUser = createParamDecorator(
@Injectable()
class AuthGuard implements CanActivate {
constructor(
private auth: AuthService,
private prisma: PrismaService
) {}
constructor(private auth: AuthService, private prisma: PrismaService) {}
async canActivate(context: ExecutionContext) {
const { req } = getRequestResponseFromContext(context);

View File

@@ -2,10 +2,11 @@ import { randomUUID } from 'node:crypto';
import { PrismaAdapter } from '@auth/prisma-adapter';
import {
All,
BadRequestException,
Controller,
Get,
Next,
Post,
Query,
Req,
Res,
@@ -14,7 +15,6 @@ import { Algorithm, sign, verify as jwtVerify } from '@node-rs/jsonwebtoken';
import type { NextFunction, Request, Response } from 'express';
import type { AuthAction, AuthOptions } from 'next-auth';
import { AuthHandler } from 'next-auth/core';
import Email from 'next-auth/providers/email';
import Github from 'next-auth/providers/github';
import Google from 'next-auth/providers/google';
@@ -28,44 +28,16 @@ const BASE_URL = '/api/auth/';
export class NextAuthController {
private readonly nextAuthOptions: AuthOptions;
constructor(
readonly config: Config,
readonly prisma: PrismaService
) {
const prismaAdapter = PrismaAdapter(prisma);
// createUser exists in the adapter
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const createUser = prismaAdapter.createUser!.bind(prismaAdapter);
prismaAdapter.createUser = async data => {
if (data.email && !data.name) {
data.name = data.email.split('@')[0];
}
return createUser(data);
};
constructor(readonly config: Config, readonly prisma: PrismaService) {
this.nextAuthOptions = {
providers: [
// @ts-expect-error esm interop issue
Email.default({
server: {
host: config.auth.email.server,
port: config.auth.email.port,
auth: {
user: config.auth.email.sender,
pass: config.auth.email.password,
},
},
from: `AFFiNE <no-reply@toeverything.info>`,
}),
],
providers: [],
// @ts-expect-error Third part library type mismatch
adapter: prismaAdapter,
debug: !config.prod,
adapter: PrismaAdapter(prisma),
};
if (config.auth.oauthProviders.github) {
this.nextAuthOptions.providers.push(
// @ts-expect-error esm interop issue
Github.default({
Github({
clientId: config.auth.oauthProviders.github.clientId,
clientSecret: config.auth.oauthProviders.github.clientSecret,
})
@@ -74,14 +46,12 @@ export class NextAuthController {
if (config.auth.oauthProviders.google) {
this.nextAuthOptions.providers.push(
// @ts-expect-error esm interop issue
Google.default({
Google({
clientId: config.auth.oauthProviders.google.clientId,
clientSecret: config.auth.oauthProviders.google.clientSecret,
})
);
}
this.nextAuthOptions.jwt = {
encode: async ({ token, maxAge }) => {
if (!token?.email) {
@@ -138,7 +108,8 @@ export class NextAuthController {
this.nextAuthOptions.secret ??= config.auth.nextAuthSecret;
}
@All('*')
@Get()
@Post()
async auth(
@Req() req: Request,
@Res() res: Response,

View File

@@ -19,10 +19,7 @@ export const getUtcTimestamp = () => Math.floor(new Date().getTime() / 1000);
@Injectable()
export class AuthService {
constructor(
private config: Config,
private prisma: PrismaService
) {}
constructor(private config: Config, private prisma: PrismaService) {}
sign(user: UserClaim) {
const now = getUtcTimestamp();

View File

@@ -101,6 +101,7 @@ type Query {
type Mutation {
register(name: String!, email: String!, password: String!): UserType!
signIn(email: String!, password: String!): UserType!
signUp(email: String!, password: String!, name: String!): UserType!
"""
Create a new workspace

View File

@@ -28,7 +28,7 @@ export type LeafPaths<
T,
Path extends string = '',
MaxDepth extends string = '...',
Depth extends string = '',
Depth extends string = ''
> = Depth extends MaxDepth
? never
: T extends Record<string | number, any>

View File

@@ -30,15 +30,15 @@
"wait-on": "^7.0.1"
},
"devDependencies": {
"@blocksuite/block-std": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/blocks": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/editor": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/global": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/icons": "^2.1.25",
"@blocksuite/lit": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/store": "0.0.0-20230717055529-79180930-nightly",
"react": "18.2.0",
"react-dom": "18.2.0"
"@blocksuite/block-std": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/blocks": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/editor": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/global": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/icons": "^2.1.24",
"@blocksuite/lit": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/store": "0.0.0-20230704023331-d9fc4854-nightly",
"react": "18.3.0-canary-1fdacbefd-20230630",
"react-dom": "18.3.0-canary-1fdacbefd-20230630"
},
"peerDependencies": {
"@blocksuite/blocks": "*",
@@ -48,5 +48,5 @@
"@blocksuite/lit": "*",
"@blocksuite/store": "*"
},
"version": "0.7.0-beta.0"
"version": "0.7.0-canary.33"
}

View File

@@ -0,0 +1,24 @@
import { AffineLoading } from '@affine/component/affine-loading';
import type { StoryFn } from '@storybook/react';
export default {
title: 'AFFiNE/Loading',
component: AffineLoading,
};
export const Default: StoryFn = ({ width, loop, autoplay, autoReverse }) => (
<div
style={{
width: width,
height: width,
}}
>
<AffineLoading loop={loop} autoplay={autoplay} autoReverse={autoReverse} />
</div>
);
Default.args = {
width: 100,
loop: true,
autoplay: true,
autoReverse: true,
};

View File

@@ -1,15 +1,13 @@
/* 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, Workspace } from '@blocksuite/store';
import { expect } from '@storybook/jest';
import type { Meta, StoryFn } from '@storybook/react';
import { use } from 'foxact/use';
import { use } from 'react';
const blockSuiteWorkspace = new Workspace({
id: 'test',
@@ -56,13 +54,13 @@ const Template: StoryFn<EditorProps> = (props: Partial<EditorProps>) => {
}}
>
<BlockSuiteEditor onInit={initPage} page={page} mode="page" {...props} />
<BlockHubWrapper
<div
style={{
position: 'absolute',
right: 12,
bottom: 12,
}}
blockHubAtom={rootBlockHubAtom}
id="toolWrapper"
/>
</div>
);

View File

@@ -2,27 +2,27 @@ import { toast } from '@affine/component';
import { BlockCard } from '@affine/component/card/block-card';
import { WorkspaceCard } from '@affine/component/card/workspace-card';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
import { Workspace } from '@blocksuite/store';
export default {
title: 'AFFiNE/Card',
component: WorkspaceCard,
};
const blockSuiteWorkspace = createEmptyBlockSuiteWorkspace(
'blocksuite-local',
WorkspaceFlavour.LOCAL
);
const blockSuiteWorkspace = new Workspace({
id: 'blocksuite-local',
});
blockSuiteWorkspace.meta.setName('Hello World');
export const AffineWorkspaceCard = () => {
return (
<WorkspaceCard
meta={{
id: 'blocksuite-local',
workspace={{
flavour: WorkspaceFlavour.LOCAL,
id: 'local',
blockSuiteWorkspace,
}}
onClick={() => {}}
onSettingClick={() => {}}

View File

@@ -0,0 +1,35 @@
import { Button } from '@affine/component';
import type { ContactModalProps } from '@affine/component/contact-modal';
import { ContactModal } from '@affine/component/contact-modal';
import type { StoryFn } from '@storybook/react';
import { useState } from 'react';
export default {
title: 'AFFiNE/ContactModal',
component: ContactModal,
};
export const Basic: StoryFn<ContactModalProps> = args => {
const [open, setOpen] = useState(false);
return (
<>
<Button
onClick={() => {
setOpen(true);
}}
>
Open
</Button>
<ContactModal
{...args}
open={open}
onClose={() => {
setOpen(false);
}}
/>
</>
);
};
Basic.args = {
logoSrc: '/imgs/affine-text-logo.png',
};

View File

@@ -1,9 +1,7 @@
import { BlockHubWrapper } from '@affine/component/block-hub';
import { BlockSuiteEditor } from '@affine/component/block-suite-editor';
import { ImagePreviewModal } from '@affine/component/image-preview-modal';
import { initEmptyPage } from '@affine/env/blocksuite';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { rootBlockHubAtom } from '@affine/workspace/atom';
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
import type { Meta } from '@storybook/react';
@@ -56,13 +54,13 @@ export const Default = () => {
>
<BlockSuiteEditor mode="page" page={page} onInit={initEmptyPage} />
</div>
<BlockHubWrapper
<div
style={{
position: 'absolute',
right: 12,
bottom: 12,
}}
blockHubAtom={rootBlockHubAtom}
id="toolWrapper"
/>
</>
);

View File

@@ -1,5 +1,6 @@
import { Empty } from '@affine/component';
import { toast } from '@affine/component';
import { AffineLoading } from '@affine/component/affine-loading';
import type { OperationCellProps } from '@affine/component/page-list';
import { PageListTrashView } from '@affine/component/page-list';
import { PageList } from '@affine/component/page-list';
@@ -12,7 +13,7 @@ import { userEvent } from '@storybook/testing-library';
export default {
title: 'AFFiNE/PageList',
component: PageList,
component: AffineLoading,
};
export const AffineOperationCell: StoryFn<OperationCellProps> = ({

View File

@@ -13,8 +13,7 @@ import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
import type { Page } from '@blocksuite/store';
import { expect } from '@storybook/jest';
import type { StoryFn } from '@storybook/react';
import { use } from 'foxact/use';
import { useState } from 'react';
import { use, useState } from 'react';
export default {
title: 'AFFiNE/ShareMenu',

View File

@@ -1,5 +1,6 @@
import type { WorkspaceAvatarProps } from '@affine/component/workspace-avatar';
import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { Workspace } from '@blocksuite/store';
import type { Meta, StoryFn } from '@storybook/react';
@@ -24,7 +25,16 @@ const basicBlockSuiteWorkspace = new Workspace({
basicBlockSuiteWorkspace.meta.setName('Hello World');
export const Basic: StoryFn<WorkspaceAvatarProps> = props => {
return <WorkspaceAvatar {...props} workspace={basicBlockSuiteWorkspace} />;
return (
<WorkspaceAvatar
{...props}
workspace={{
flavour: WorkspaceFlavour.LOCAL,
id: 'local',
blockSuiteWorkspace: basicBlockSuiteWorkspace,
}}
/>
);
};
Basic.args = {
@@ -50,7 +60,16 @@ fetch(new URL('@affine-test/fixtures/smile.png', import.meta.url))
});
export const BlobExample: StoryFn<WorkspaceAvatarProps> = props => {
return <WorkspaceAvatar {...props} workspace={avatarBlockSuiteWorkspace} />;
return (
<WorkspaceAvatar
{...props}
workspace={{
flavour: WorkspaceFlavour.LOCAL,
id: 'local',
blockSuiteWorkspace: avatarBlockSuiteWorkspace,
}}
/>
);
};
BlobExample.args = {

View File

@@ -6,7 +6,8 @@
"baseUrl": "../..",
"composite": true,
"noEmit": false,
"outDir": "lib"
"outDir": "lib",
"types": ["react/experimental"]
},
"references": [
{

View File

@@ -13,9 +13,6 @@ import { blockSuiteFeatureFlags, buildFlags } from './preset.config.mjs';
import { getCommitHash, getGitVersion } from './scripts/git-info.mjs';
const require = createRequire(import.meta.url);
const packageJson = require('./package.json');
const appVersion = packageJson.version;
const editorVersion = packageJson.dependencies['@blocksuite/editor'];
const { createVanillaExtractPlugin } = require('@vanilla-extract/next-plugin');
const withVanillaExtract = createVanillaExtractPlugin();
@@ -105,8 +102,6 @@ const nextConfig = {
publicRuntimeConfig: {
PROJECT_NAME: process.env.npm_package_name ?? 'AFFiNE',
BUILD_DATE: new Date().toISOString(),
appVersion,
editorVersion,
gitVersion: getGitVersion(),
hash: getCommitHash(),
serverAPI: profileTarget.local,

View File

@@ -1,7 +1,7 @@
{
"name": "@affine/web",
"private": true,
"version": "0.7.0-beta.0",
"version": "0.7.0-canary.33",
"scripts": {
"dev": "next dev",
"build": "next build && next export",
@@ -19,13 +19,13 @@
"@affine/jotai": "workspace:*",
"@affine/templates": "workspace:*",
"@affine/workspace": "workspace:*",
"@blocksuite/block-std": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/blocks": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/editor": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/global": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/icons": "^2.1.25",
"@blocksuite/lit": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/store": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/block-std": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/blocks": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/editor": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/global": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/icons": "^2.1.24",
"@blocksuite/lit": "0.0.0-20230704023331-d9fc4854-nightly",
"@blocksuite/store": "0.0.0-20230704023331-d9fc4854-nightly",
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/sortable": "^7.0.2",
"@emotion/cache": "^11.11.0",
@@ -42,13 +42,13 @@
"cmdk": "^0.2.0",
"css-spring": "^4.1.0",
"graphql": "^16.7.1",
"jotai": "^2.2.2",
"jotai": "^2.2.1",
"jotai-devtools": "^0.6.0",
"lit": "^2.7.5",
"lottie-web": "^5.12.2",
"next-themes": "^0.2.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react": "18.3.0-canary-1fdacbefd-20230630",
"react-dom": "18.3.0-canary-1fdacbefd-20230630",
"react-is": "^18.2.0",
"react-resizable-panels": "^0.0.53",
"rxjs": "^7.8.1",

View File

@@ -23,13 +23,14 @@ const buildPreset = {
enableTestProperties: false,
enableBroadcastChannelProvider: true,
enableDebugPage: true,
changelogUrl: 'https://affine.pro/blog/what-is-new-affine-0717',
imageProxyUrl: 'https://workers.toeverything.workers.dev/proxy/image',
// never set this to true in stable, because legacy cloud has deprecated
// and related code will be removed in the future
enableLegacyCloud: false,
changelogUrl: 'https://affine.pro/blog/whats-new-affine-0630',
enablePreloading: true,
enableNewSettingModal: true,
enableNewSettingUnstableApi: false,
enableSQLiteProvider: true,
enableMoveDatabase: false,
enableSQLiteProvider: false,
enableNotificationCenter: false,
enableCloud: false,
},
@@ -41,13 +42,12 @@ const buildPreset = {
enableTestProperties: true,
enableBroadcastChannelProvider: true,
enableDebugPage: true,
changelogUrl: 'https://affine.pro/blog/what-is-new-affine-0717',
imageProxyUrl: 'https://workers.toeverything.workers.dev/proxy/image',
enableLegacyCloud: false,
changelogUrl: 'https://affine.pro/blog/whats-new-affine-0630',
enablePreloading: true,
enableNewSettingModal: true,
enableNewSettingUnstableApi: false,
enableSQLiteProvider: true,
enableMoveDatabase: false,
enableSQLiteProvider: false,
enableNotificationCenter: true,
enableCloud: false,
},
@@ -72,6 +72,9 @@ const environmentPreset = {
enableTestProperties: process.env.ENABLE_TEST_PROPERTIES
? process.env.ENABLE_TEST_PROPERTIES === 'true'
: currentBuildPreset.enableTestProperties,
enableLegacyCloud: process.env.ENABLE_LEGACY_PROVIDER
? process.env.ENABLE_LEGACY_PROVIDER === 'true'
: currentBuildPreset.enableLegacyCloud,
enableBroadcastChannelProvider: process.env.ENABLE_BC_PROVIDER
? process.env.ENABLE_BC_PROVIDER !== 'false'
: currentBuildPreset.enableBroadcastChannelProvider,
@@ -94,9 +97,6 @@ const environmentPreset = {
enableCloud: process.env.ENABLE_CLOUD
? process.env.ENABLE_CLOUD === 'true'
: currentBuildPreset.enableCloud,
enableMoveDatabase: process.env.ENABLE_MOVE_DATABASE
? process.env.ENABLE_MOVE_DATABASE === 'true'
: currentBuildPreset.enableMoveDatabase,
};
/**

View File

@@ -8,16 +8,6 @@
"build": {
"executor": "nx:run-script",
"dependsOn": ["^build"],
"inputs": [
"{projectRoot}/**/*",
"{workspaceRoot}/packages/component/src/**/*",
"{workspaceRoot}/packages/debug/src/**/*",
"{workspaceRoot}/packages/debug/graphql/**/*",
"{workspaceRoot}/packages/hooks/src/**/*",
"{workspaceRoot}/packages/jotai/src/**/*",
"{workspaceRoot}/packages/templates/src/**/*",
"{workspaceRoot}/packages/workspace/src/**/*"
],
"options": {
"script": "build"
},

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@@ -1,93 +0,0 @@
Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -1,93 +0,0 @@
Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -1,91 +0,0 @@
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -1,93 +0,0 @@
Copyright 2016 Google Inc. All Rights Reserved.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 710 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 822 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 363 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 945 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 KiB

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