Compare commits

...

125 Commits

Author SHA1 Message Date
Alex Yang
59f53760d1 v0.7.0-canary.54 2023-07-27 05:58:20 -07:00
Alex Yang
2980c1afac fix: plugin not found (#3415) 2023-07-27 05:56:59 -07:00
Alex Yang
39054a7c3d v0.7.0-canary.53 2023-07-27 05:19:20 -07:00
Alex Yang
4b7e47e265 chore: bump blocksuite (#3404)
Co-authored-by: LongYinan <lynweklm@gmail.com>
2023-07-27 05:37:38 +00:00
Peng Xiao
4e7824583d build: add AppImage build (#3401) 2023-07-26 22:38:01 -07:00
JimmFly
ba53c74130 fix: unable to add a second collection (#3405) 2023-07-26 22:37:42 -07:00
Qi
bc263e7afb feat: modify current workspace label to a dot (#3399) 2023-07-26 22:37:31 -07:00
JimmFly
bc27412425 feat: support gif toast (#3389) 2023-07-26 22:37:18 -07:00
Qi
fa8086d525 fix: button style error (#3396) 2023-07-26 22:37:00 -07:00
JimmFly
04534c2008 chore: adjust sidebar padding (#3397) 2023-07-26 22:36:45 -07:00
Alex Yang
780fffb88f fix: plugin infra (#3398) 2023-07-26 22:36:29 -07:00
Alex Yang
1e72d3c270 chore: bump version (#3394) 2023-07-27 04:02:18 +00:00
xiaodong zuo
1e38d36161 fix: inconsistent database content in exported PDF (#3385) 2023-07-26 21:26:53 +00:00
JimmFly
bb9908e1fa fix: filter button conflicts with electron header drag event (#3380) 2023-07-26 09:58:40 +00:00
liuyi
6bafa83cef fix(workspace): should avoid sending providers' update back (#3384) 2023-07-26 09:47:24 +00:00
JimmFly
2c249781a2 feat: add new collection button to slider bar (#3369) 2023-07-26 04:32:55 +00:00
Alex Yang
8334ac031b Revert "chore(cli): build infra (#3375)"
This reverts commit 635ca081e4.
2023-07-25 22:04:58 -07:00
Alex Yang
635ca081e4 chore(cli): build infra (#3375) 2023-07-25 23:33:25 +00:00
Alex Yang
10f879f29a refactor(electron): server side plugin (#3360) 2023-07-25 21:32:34 +00:00
Alex Yang
521e505a01 build: update cli (#3374) 2023-07-25 21:32:18 +00:00
Alex Yang
f968587f6f v0.7.0-canary.52 2023-07-25 12:36:30 -07:00
Whitewater
e70f8e74ec chore: allow custom editor spec presets (#3362)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-07-25 18:57:42 +00:00
Alex Yang
32fd01ed33 build: fix ci (#3373) 2023-07-25 18:41:32 +00:00
TinsFox
00718f8c9a chore: update version label (#3368) 2023-07-25 11:18:02 -07:00
Peng Xiao
20ee9d485d perf: use lazy load provider for IDB and SQLITE (#3351) 2023-07-25 16:56:48 +00:00
JimmFly
e3f66d7e22 style: move trash button group to page bottom (#3352) 2023-07-25 05:21:16 +00:00
JimmFly
be81e63eed chore: update icon size (#3350) 2023-07-24 23:35:10 +00:00
Alex Yang
2cf4e8ebce fix(y-indexeddb): un-track doc when destroy (#3358) 2023-07-24 15:23:16 +00:00
Alex Yang
e6e98975ed fix(core): avoid page full refresh (#3341)
Co-authored-by: Peng Xiao <pengxiao@outlook.com>
2023-07-24 09:02:35 +00:00
Peng Xiao
ccb0df10e4 fix: temp workaround for missing blobs in export (#3347) 2023-07-23 10:45:01 +00:00
Alex Yang
dd31d1e8c6 feat(plugin-infra): add plugin cli (#3344) 2023-07-22 17:17:40 +00:00
Alex Yang
a494bad543 chore: bump version (#3346) 2023-07-22 13:10:20 +00:00
danielchim
363699a175 feat: title editing on workspace title (#3139)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-07-22 13:03:18 +00:00
Qi
439ef1ba90 feat: refactor button with new design (#3343) 2023-07-21 11:07:28 +00:00
Alex Yang
a4f60f22cf v0.7.0-canary.51 2023-07-21 18:46:08 +08:00
Alex Yang
f05cd66368 fix(core): use Link from react-router-dom (#3342) 2023-07-21 10:29:36 +00:00
Peng Xiao
869d98d019 perf: lazy doc provider factory (#3330)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-07-21 05:23:18 +00:00
JimmFly
cff741e9ba style: add text overflow style for collections (#3292)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-07-21 03:48:22 +00:00
Alex Yang
9f105b5806 v0.7.0-canary.50 2023-07-21 11:52:50 +08:00
Alex Yang
cac609d36f fix(core): migration (#3322) 2023-07-20 20:16:15 +00:00
Alex Yang
c319e7e707 fix: type check in plugins (#3337) 2023-07-20 19:28:55 +00:00
Alex Yang
c2f6bb152c v0.7.0-canary.49 2023-07-21 01:21:25 +08:00
Alex Yang
f4b3c70fd4 build: move file 2023-07-21 01:20:29 +08:00
Alex Yang
a9db82ea21 v0.7.0-canary.48 2023-07-21 00:56:46 +08:00
Alex Yang
ecf6f98858 chore: bump version (#3333) 2023-07-20 16:39:16 +00:00
Alex Yang
e3a6204f2d fix: lockdown (#3336) 2023-07-20 16:04:26 +00:00
Alex Yang
19055baa49 feat: init new plugin system (#3323) 2023-07-20 10:52:29 +00:00
Alex Yang
604b53d9a4 feat: init doc monitor (#3320) 2023-07-20 02:44:50 +00:00
Alex Yang
27edd7cd93 fix: enable strict mode (#3321) 2023-07-20 01:59:58 +00:00
Alex Yang
fbd5b36170 feat: use string on origin (#3319) 2023-07-19 16:15:48 +00:00
Alex Yang
19925038ba fix(core): css.ts hmr (#3317) 2023-07-19 15:52:21 +00:00
Alex Yang
ae182bfd78 chore: update runtime (#3312) 2023-07-19 09:58:51 +00:00
Alex Yang
710b34a13a chore(core): update webpack hash logic (#3308) 2023-07-19 07:19:27 +00:00
Alex Yang
8e9535dd27 fix(core): plugin (#3307) 2023-07-19 07:00:42 +00:00
JimmFly
f4aa249138 fix: banner blocking new page button issue (#3301) 2023-07-19 04:51:15 +00:00
Alex Yang
57bac5d36b chore: bump version (#3298) 2023-07-19 04:31:57 +00:00
Alex Yang
b6e5618a2e chore(core): fix missing stuff (#3302) 2023-07-19 04:30:04 +00:00
Alex Yang
e475aa4c99 feat: add bootstrap (#3299) 2023-07-19 03:58:23 +00:00
Alex Yang
4ced66c236 chore: remove next.js dependency (#3297) 2023-07-19 03:13:14 +00:00
Alex Yang
1abcdee2f0 fix(cli): update dev-core (#3296) 2023-07-19 02:55:56 +00:00
Alex Yang
47f12f77f2 refactor!: remove next.js (#3267) 2023-07-18 16:53:10 +00:00
Whitewater
79227a1e7c chore: update block card styles (#3290) 2023-07-18 11:45:03 +00:00
Whitewater
bf41b25988 feat: new import page component (#3277) 2023-07-18 05:36:14 +00:00
Alex Yang
41edacfc81 build: fix nx inputs 2023-07-18 12:59:25 +08:00
JimmFly
9b32db9f62 chore: increase the frequency of the banner (#3264) 2023-07-17 09:45:02 +00:00
JimmFly
f21eb5f272 feat: move plugins config to setting (#3259) 2023-07-17 09:25:00 +00:00
JimmFly
d4cd0e763d fix: temporarily handle all page scroll bar styles (#3269) 2023-07-17 08:04:12 +00:00
Camol
8f06854130 feat(i18n): support i18n in app version (#3263) 2023-07-17 08:03:50 +00:00
Peng Xiao
81bad608bc fix: disable updater button when app updating (#3268) 2023-07-17 07:49:03 +00:00
Alex Yang
eeed398155 fix(plugin-infra): react as peer dependency (#3260) 2023-07-17 15:48:32 +08:00
xiaodong zuo
f173c8b183 chore: update blocksuite version (#3261) 2023-07-17 06:44:53 +00:00
Alex Yang
071d582250 fix: first workspace not found (#3258) 2023-07-17 05:00:30 +00:00
JimmFly
e8f8bd21cf chore: upadete onboarding video and changlog link (#3255) 2023-07-17 04:31:07 +00:00
Alex Yang
c0749fbb9f refactor: use useCallback (#3254) 2023-07-17 03:31:06 +00:00
Si Yang
b317a3e506 docs: update building-desktop-client-app.md (#3248) 2023-07-17 03:11:31 +00:00
Alex Yang
06184a765c fix(plugin-infra): dependencies (#3252) 2023-07-17 03:11:02 +00:00
Alex Yang
a2dae0d592 v0.7.0-canary.47 2023-07-16 23:27:39 +08:00
Alex Yang
202e9b8fe3 chore: bump version (#3250)
Co-authored-by: Alex Yang <himself65@Alexs-MacBook-Pro.local>
2023-07-16 15:13:00 +00:00
angle
ce23817c11 fix: pwa icon (#3246) 2023-07-15 23:20:29 +08:00
Alex Yang
c49cf1c53c fix: create first workspace logic (#3241) 2023-07-14 09:54:11 +00:00
Alex Yang
1bc427e7a6 fix: migration logic (#3238) 2023-07-14 09:28:15 +00:00
Alex Yang
ea592eb150 fix: remove hello-world page (#3234) 2023-07-14 07:49:34 +00:00
Alex Yang
5864f8cb9a refactor: simplify code (#3231) 2023-07-14 07:47:51 +00:00
Alex Yang
2be0ae8906 revert: use stable react (#3228) 2023-07-14 05:33:43 +00:00
Peng Xiao
9a85a14970 fix: internal build updater (#3229) 2023-07-14 05:21:43 +00:00
Alex Yang
6b0d048156 v0.7.0-canary.46 2023-07-14 11:30:06 +08:00
Alex Yang
3421c2803a fix(plugin-infra): dependencies (#3226) 2023-07-14 03:18:08 +00:00
Alex Yang
af6807826b v0.7.0-canary.45 2023-07-14 01:01:58 +08:00
Alex Yang
1336ec562e chore: bump version (#3223) 2023-07-13 16:40:44 +00:00
Alex Yang
1f5995ffc7 refactor(plugin-infra): workspace loading (#3222) 2023-07-13 15:52:06 +00:00
3720
23ac82f845 fix: can't go back to all pages by click All Pages button (#3219) 2023-07-13 15:26:12 +00:00
Alex Yang
492852ba0c refactor: mode atom (#3221) 2023-07-13 15:11:00 +00:00
Alex Yang
d18df12951 test: add workspace ref in e2e (#3217) 2023-07-13 12:41:46 +00:00
Alex Yang
64cf297399 refactor(web): move current atoms into plugin infra (#3220) 2023-07-13 12:14:30 +00:00
Alex Yang
0230cea16e refactor: move test utils to package (#3206) 2023-07-13 09:05:01 +00:00
Alex Yang
4f5ed30298 v0.7.0-canary.44 2023-07-13 17:07:14 +08:00
3720
9c5d91a1d9 fix: collections should be unique for workspaces (#3213) 2023-07-13 08:42:20 +00:00
Alex Yang
24a5b54b67 fix: fullscreen settting not working (#3215)
Co-authored-by: QiShaoXuan <qishaoxuan777@gmail.com>
2023-07-13 16:31:17 +08:00
xiaodong zuo
8be6676ddb chore: update blocksuite version (#3209)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-07-13 07:31:13 +00:00
Alex Yang
6a2dbc0105 fix: page id could be invalid (#3204) 2023-07-13 04:18:18 +00:00
Alex Yang
5dda7d83da fix: block hub might not work (#3199) 2023-07-12 18:02:00 +00:00
Alex Yang
c066224a95 v0.7.0-canary.43 2023-07-13 01:27:02 +08:00
Alex Yang
e475a9cdbc chore: bump version (#3197) 2023-07-12 16:25:35 +00:00
Alex Yang
73a47f31a7 chore: bump version (#3196) 2023-07-12 15:44:48 +00:00
Alex Yang
4109490789 v0.7.0-canary.42 2023-07-12 23:01:11 +08:00
Peng Xiao
e813436af7 fix: iconUrl for windows build (#3194) 2023-07-12 14:35:45 +00:00
Alex Yang
5b87d90ffe fix: first page id conflict (#3192) 2023-07-12 10:43:52 +00:00
Alex Yang
ccbae6f496 fix: unexpected jump 404 page (#3190) 2023-07-12 10:18:02 +00:00
JimmFly
1ac1c33bb1 style: update delete button style (#3180) 2023-07-12 09:23:39 +00:00
Peng Xiao
bd42380f8a fix: add default fonts (#3185) 2023-07-12 08:43:25 +00:00
xiaodong zuo
30dee18835 fix: enhancing the security of image proxy (#3176) 2023-07-12 08:35:46 +00:00
Alex Yang
b509302711 v0.7.0-canary.41 2023-07-12 14:49:08 +08:00
Alex Yang
e51c98c1dd chore: bump version (#3179) 2023-07-12 06:21:11 +00:00
Alex Yang
bbb1387469 feat: display app version in setting panel (#3170) 2023-07-12 02:39:00 +00:00
xiaodong zuo
4f88774999 fix: the image lost after exporting (#3150)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-07-12 02:21:23 +00:00
Alex Yang
3968deb6d4 feat: add suspense to workspace settings (#3167)
Co-authored-by: Qi <474021214@qq.com>
2023-07-11 15:50:30 +00:00
Alex Yang
37c8465af8 fix: jump to index page after deletion (#3169) 2023-07-11 15:44:00 +00:00
Peng Xiao
d88a21d24a fix: settings style update (#3161) 2023-07-11 12:55:28 +00:00
3720
6ad2d106bc fix: some typo and i18n (#3155) 2023-07-11 11:04:45 +00:00
Alex Yang
8c1fcee135 refactor: remove unused code (#3149) 2023-07-11 08:53:01 +00:00
Peng Xiao
0514da9759 fix: updater not working (#3144) 2023-07-11 07:06:04 +00:00
JimmFly
b2fed03f30 style: modify the style of community item (#3143) 2023-07-11 06:44:06 +00:00
Alex Yang
f5e45573af v0.7.0-canary.40 2023-07-11 12:59:12 +08:00
Alex Yang
ddb2931f38 fix: remove workspace not working (#3140) 2023-07-11 04:37:47 +00:00
Alex Yang
acf17ebace chore: bump version (#3138) 2023-07-11 04:28:01 +00:00
575 changed files with 13573 additions and 12430 deletions

View File

@@ -10,7 +10,7 @@
"tasks": {
"start-web": {
"name": "Start Web",
"command": "yarn nx dev @affine/web --port 8080",
"command": "yarn dev-core",
"runAtStart": true,
"preview": {
"port": 8080

View File

@@ -7,7 +7,7 @@
[
"electron",
"server",
"web",
"core",
"docs",
"storybook",
"component",
@@ -22,7 +22,9 @@
"templates",
"y-indexeddb",
"debug",
"storage"
"storage",
"infra",
"plugin-infra"
]
]
}

11
.env.template Normal file
View File

@@ -0,0 +1,11 @@
ENABLE_PLUGIN=
ENABLE_TEST_PROPERTIES=
ENABLE_BC_PROVIDER=
CHANGELOG_URL=
ENABLE_PRELOADING=
ENABLE_NEW_SETTING_MODAL=
ENABLE_SQLITE_PROVIDER=
ENABLE_NEW_SETTING_UNSTABLE_API=
ENABLE_NOTIFICATION_CENTER=
ENABLE_CLOUD=
ENABLE_MOVE_DATABASE=

View File

@@ -21,8 +21,8 @@ body:
label: Distribution version
description: What version of AFFiNE are you using?
options:
- macOS x64
- macOS ARM 64
- macOS x64 (Intel)
- macOS ARM 64 (Apple Silicon)
- Windows x64
- Linux
- Web (app.affine.pro)

View File

@@ -1,6 +1,6 @@
FROM openresty/openresty:1.21.4.1-0-buster
WORKDIR /app
COPY ./apps/web/out ./dist
COPY ./apps/core/dist ./dist
COPY ./.github/deployment/front/nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
COPY ./.github/deployment/front/affine.nginx.conf /etc/nginx/conf.d/affine.nginx.conf

2
.github/labeler.yml vendored
View File

@@ -53,7 +53,7 @@ rust:
package:y-indexeddb: 'packages/y-indexeddb/**/*'
app:web: 'apps/web/**/*'
app:core: 'apps/core/**/*'
app:electron: 'apps/electron/**/*'

View File

@@ -101,8 +101,8 @@ jobs:
path: ./apps/storybook/storybook-static
if-no-files-found: error
build-web:
name: Build @affine/web
build-core:
name: Build @affine/core
runs-on: ubuntu-latest
environment: development
@@ -110,13 +110,13 @@ jobs:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build Web
run: yarn nx build @affine/web
- name: Upload artifact
- name: Build Core
run: yarn nx build @affine/core
- name: Upload core artifact
uses: actions/upload-artifact@v3
with:
name: next-js-static
path: ./apps/web/out
name: core
path: ./apps/core/dist
if-no-files-found: error
server-test:
@@ -215,7 +215,7 @@ jobs:
matrix:
shard: [1, 2, 3, 4, 5]
environment: development
needs: build-web
needs: build-core
steps:
- uses: actions/checkout@v3
@@ -224,14 +224,15 @@ jobs:
with:
playwright-install: true
electron-install: false
- name: Download artifact
- name: Download core artifact
uses: actions/download-artifact@v3
with:
name: next-js-static
path: ./apps/web/out
name: core
path: ./apps/core/dist
- name: Run playwright tests
run: yarn e2e --forbid-only --shard=${{ matrix.shard }}/${{ strategy.job-total }}
working-directory: tests/affine-local
env:
COVERAGE: true
@@ -259,7 +260,7 @@ jobs:
name: E2E Migration Test
runs-on: ubuntu-latest
environment: development
needs: [build-web]
needs: build-core
steps:
- uses: actions/checkout@v3
@@ -269,11 +270,11 @@ jobs:
playwright-install: true
electron-install: false
- name: Download next static
- name: Download core artifact
uses: actions/download-artifact@v3
with:
name: next-js-static
path: ./apps/web/out
name: core
path: ./apps/core/dist
- name: Unzip
run: yarn unzip
@@ -332,7 +333,7 @@ jobs:
target: x86_64-pc-windows-msvc,
test: true,
}
needs: [build-web]
needs: build-core
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
@@ -352,10 +353,10 @@ jobs:
env:
NATIVE_TEST: 'true'
- name: Download static resource artifact
- name: Download core artifact
uses: actions/download-artifact@v3
with:
name: next-js-static
name: core
path: apps/electron/resources/web-static
- name: Build Plugins
@@ -442,11 +443,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Download next static
- name: Download core artifact
uses: actions/download-artifact@v3
with:
name: next-js-static
path: ./apps/web/out
name: core
path: ./apps/core/dist
- name: Download server dist
uses: actions/download-artifact@v3
with:

View File

@@ -34,7 +34,7 @@ jobs:
runs-on: ubuntu-latest
environment: production
outputs:
version: 0.0.0-${{ steps.version.outputs.version }}
version: 0.0.0-internal.${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v3
- uses: toeverything/set-build-version@latest
@@ -62,10 +62,10 @@ jobs:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
RELEASE_VERSION: ${{ needs.set-build-version.outputs.version }}
- name: Upload Artifact (web-static)
- name: Upload core artifact
uses: actions/upload-artifact@v3
with:
name: before-make-web-static
name: core
path: apps/electron/resources/web-static
make-distribution:
@@ -120,7 +120,7 @@ jobs:
run: ./scripts/set-version.sh ${{ needs.set-build-version.outputs.version }}
- uses: actions/download-artifact@v3
with:
name: before-make-web-static
name: core
path: apps/electron/resources/web-static
- name: Build Plugins
@@ -159,6 +159,7 @@ jobs:
run: |
mkdir -p builds
mv apps/electron/out/*/make/zip/linux/x64/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.zip
mv apps/electron/out/*/make/AppImage/x64/*.AppImage ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.AppImage
- name: Upload Artifact
uses: actions/upload-artifact@v3

View File

@@ -66,10 +66,10 @@ jobs:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
RELEASE_VERSION: ${{ github.event.inputs.version || steps.get-canary-version.outputs.RELEASE_VERSION }}
- name: Upload Artifact (web-static)
- name: Upload core artifact
uses: actions/upload-artifact@v3
with:
name: before-make-web-static
name: core
path: apps/electron/resources/web-static
make-distribution:
@@ -120,7 +120,7 @@ jobs:
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
- uses: actions/download-artifact@v3
with:
name: before-make-web-static
name: core
path: apps/electron/resources/web-static
- name: Build Plugins
@@ -159,6 +159,7 @@ jobs:
run: |
mkdir -p builds
mv apps/electron/out/*/make/zip/linux/x64/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.zip
mv apps/electron/out/*/make/AppImage/x64/*.AppImage ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.AppImage
- name: Upload Artifact
uses: actions/upload-artifact@v3

22
.github/workflows/workers.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
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'

8
.gitignore vendored
View File

@@ -13,6 +13,7 @@
/out-tsc
.nyc_output
.coverage
.swc
# dependencies
node_modules
@@ -60,9 +61,10 @@ out/
storybook-static
i18n-generated.ts
/test-results/
/playwright-report/
/playwright/.cache/
test-results
playwright-report
playwright/.cache
download
# Cache
.eslintcache

View File

@@ -12,3 +12,5 @@ tests/affine-legacy/0.7.0-canary.18/static
.github/helm
_next
storybook-static
web-static
public

View File

@@ -28,7 +28,7 @@ See https://github.com/all-?/all-contributors/issues/361#issuecomment-637166066
[![AFFiNE macOS M1/M2 Chip](https://img.shields.io/badge/-macOS_M_Chip%20%E2%86%92-black?style=flat-square&logo=apple&logoColor=white)](https://affine.pro/download)
[![AFFiNE macOS x64](https://img.shields.io/badge/-macOS_x86%20%E2%86%92-black?style=flat-square&logo=apple&logoColor=white)](https://affine.pro/download)
[![AFFiNE Window x64](https://img.shields.io/badge/-Windows%20%E2%86%92-blue?style=flat-square&logo=windows&logoColor=white)](https://affine.pro/download)
[![AFFiNE Linux](https://img.shields.io/badge/-Linux%20%E2%86%92-yellow?style=flat-square&logo=linux&logoColor=white)](https://affine.pro/download)
[![AFFiNE Linux](https://img.shidelds.io/badge/-Linux%20%E2%86%92-yellow?style=flat-square&logo=linux&logoColor=white)](https://affine.pro/download)
[![Releases](https://img.shields.io/github/downloads/toeverything/AFFiNE/total)](https://github.com/toeverything/AFFiNE/releases/latest)
[![stars-icon]](https://github.com/toeverything/AFFiNE)
@@ -202,5 +202,5 @@ See [LICENSE] for details.
[codecov]: https://codecov.io/gh/toeverything/affine/branch/master/graphs/badge.svg?branch=master
[node-version-icon]: https://img.shields.io/badge/node-%3E=18.16.1-success
[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%2Fweb%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%2Fweb%2Fpackage.json&label=blocksuite
[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

View File

@@ -20,6 +20,6 @@ Server using [Nest.js](https://nestjs.com/).
Storybook using [Storybook](https://storybook.js.org/).
## web
## Core
AFFiNE Core Application using [React.js](https://reactjs.org/).

View File

@@ -0,0 +1,82 @@
function testPackageName(regexp: RegExp): (module: any) => boolean {
return (module: any) =>
module.nameForCondition && regexp.test(module.nameForCondition());
}
export const productionCacheGroups = {
asyncVendor: {
test: /[\\/]node_modules[\\/]/,
name(module: any) {
// https://hackernoon.com/the-100-correct-way-to-split-your-chunks-with-webpack-f8a9df5b7758
const name =
module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)?.[1] ??
'unknown';
return `npm-async-${name}`;
},
priority: Number.MAX_SAFE_INTEGER,
chunks: 'async' as const,
},
mui: {
name: `npm-mui`,
test: testPackageName(/[\\/]node_modules[\\/](mui|@mui)[\\/]/),
priority: 200,
enforce: true,
},
blocksuite: {
name: `npm-blocksuite`,
test: testPackageName(/[\\/]node_modules[\\/](@blocksuite)[\\/]/),
priority: 200,
enforce: true,
},
react: {
name: `npm-react`,
test: testPackageName(
/[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/
),
priority: 200,
enforce: true,
},
jotai: {
name: `npm-jotai`,
test: testPackageName(/[\\/]node_modules[\\/](jotai)[\\/]/),
priority: 200,
enforce: true,
},
rxjs: {
name: `npm-rxjs`,
test: testPackageName(/[\\/]node_modules[\\/]rxjs[\\/]/),
priority: 200,
enforce: true,
},
lodash: {
name: `npm-lodash`,
test: testPackageName(/[\\/]node_modules[\\/]lodash[\\/]/),
priority: 200,
enforce: true,
},
emotion: {
name: `npm-emotion`,
test: testPackageName(/[\\/]node_modules[\\/](@emotion)[\\/]/),
priority: 200,
enforce: true,
},
vendor: {
name: 'vendor',
test: /[\\/]node_modules[\\/]/,
priority: 190,
enforce: true,
},
styles: {
name: 'styles',
test: (module: any) =>
module.nameForCondition &&
/\.css$/.test(module.nameForCondition()) &&
!/^javascript/.test(module.type),
chunks: 'all' as const,
minSize: 1,
minChunks: 1,
reuseExistingChunk: true,
priority: 1000,
enforce: true,
},
};

View File

@@ -0,0 +1,346 @@
import { join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
import HTMLPlugin from 'html-webpack-plugin';
import type { Configuration as DevServerConfiguration } from 'webpack-dev-server';
import { PerfseePlugin } from '@perfsee/webpack';
import { sentryWebpackPlugin } from '@sentry/webpack-plugin';
import CopyPlugin from 'copy-webpack-plugin';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import TerserPlugin from 'terser-webpack-plugin';
import webpack from 'webpack';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import { productionCacheGroups } from './cache-group.js';
import type { BuildFlags } from '@affine/cli/config';
import { projectRoot } from '@affine/cli/config';
import { VanillaExtractPlugin } from '@vanilla-extract/webpack-plugin';
import { computeCacheKey } from './utils.js';
import type { RuntimeConfig } from '@affine/env/global';
const IN_CI = !!process.env.CI;
export const rootPath = fileURLToPath(new URL('..', import.meta.url));
const require = createRequire(rootPath);
const OptimizeOptionOptions: (
buildFlags: BuildFlags
) => webpack.Configuration['optimization'] = buildFlags => ({
minimize: buildFlags.mode === 'production',
minimizer: [
new TerserPlugin({
minify: TerserPlugin.swcMinify,
parallel: true,
extractComments: true,
terserOptions: {
ecma: 2020,
compress: {
unused: true,
},
mangle: true,
},
}),
],
removeEmptyChunks: true,
providedExports: true,
usedExports: true,
sideEffects: true,
removeAvailableModules: true,
runtimeChunk: {
name: 'runtime',
},
splitChunks: {
chunks: 'all',
minSize: 1,
minChunks: 1,
maxInitialRequests: Number.MAX_SAFE_INTEGER,
maxAsyncRequests: Number.MAX_SAFE_INTEGER,
cacheGroups:
buildFlags.mode === 'production'
? productionCacheGroups
: {
default: false,
vendors: false,
},
},
});
export const createConfiguration: (
buildFlags: BuildFlags,
runtimeConfig: RuntimeConfig
) => webpack.Configuration = (buildFlags, runtimeConfig) => {
let publicPath = process.env.PUBLIC_PATH ?? '/';
const cacheKey = computeCacheKey(buildFlags);
const config = {
name: 'affine',
// to set a correct base path for the source map
context: projectRoot,
output: {
environment: {
module: true,
dynamicImport: true,
},
filename:
buildFlags.mode === 'production'
? 'js/[name]-[contenthash:8].js'
: 'js/[name].js',
// In some cases webpack will emit files starts with "_" which is reserved in web extension.
chunkFilename: 'js/chunk.[name].js',
assetModuleFilename: 'assets/[contenthash:8][ext][query]',
devtoolModuleFilenameTemplate: 'webpack://[namespace]/[resource-path]',
hotUpdateChunkFilename: 'hot/[id].[fullhash].js',
hotUpdateMainFilename: 'hot/[runtime].[fullhash].json',
path: join(rootPath, 'dist'),
clean: buildFlags.mode === 'production',
globalObject: 'globalThis',
publicPath,
},
target: ['web', 'es2022'],
mode: buildFlags.mode,
devtool:
buildFlags.mode === 'production'
? buildFlags.distribution === 'desktop'
? 'nosources-source-map'
: 'source-map'
: 'eval-cheap-module-source-map',
resolve: {
symlinks: true,
extensionAlias: {
'.js': ['.js', '.tsx', '.ts'],
'.mjs': ['.mjs', '.mts'],
},
extensions: ['.js', '.ts', '.tsx'],
},
cache: {
type: 'filesystem',
buildDependencies: {
config: [fileURLToPath(import.meta.url)],
},
version: cacheKey,
},
module: {
parser: {
javascript: {
// Treat as missing export as error
strictExportPresence: true,
},
},
rules: [
{
test: /\.m?js?$/,
resolve: {
fullySpecified: false,
},
},
{
oneOf: [
{
test: /\.tsx?$/,
// Compile all ts files in the workspace
include: resolve(rootPath, '..', '..'),
loader: require.resolve('swc-loader'),
options: {
// https://swc.rs/docs/configuring-swc/
jsc: {
preserveAllComments: true,
parser: {
syntax: 'typescript',
dynamicImport: true,
topLevelAwait: false,
tsx: true,
},
target: 'es2022',
externalHelpers: true,
transform: {
react: {
runtime: 'automatic',
refresh: buildFlags.mode === 'development' && {
refreshReg: '$RefreshReg$',
refreshSig: '$RefreshSig$',
emitFullSignatures: true,
},
},
},
experimental: {
keepImportAssertions: true,
plugins: [
buildFlags.coverage && [
'swc-plugin-coverage-instrument',
{},
],
].filter(Boolean),
},
},
},
},
{
test: /\.svg$/,
use: [
'thread-loader',
{
loader: '@svgr/webpack',
options: {
icon: true,
},
},
],
exclude: [/node_modules/],
},
{
test: /\.(png|jpg|gif|svg|webp)$/,
type: 'asset/resource',
},
{
test: /\.(ttf|eot|woff|woff2)$/,
type: 'asset/resource',
},
{
test: /\.txt$/,
loader: 'raw-loader',
},
{
test: /\.css$/,
use: [
buildFlags.mode === 'development'
? 'style-loader'
: MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
url: false,
sourceMap: false,
modules: false,
import: true,
importLoaders: 1,
},
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
config: resolve(
rootPath,
'.webpack',
'postcss.config.cjs'
),
},
},
},
],
},
],
},
],
},
plugins: [
...(IN_CI ? [] : [new webpack.ProgressPlugin({ percentBy: 'entries' })]),
...(buildFlags.mode === 'development'
? [new ReactRefreshWebpackPlugin({ overlay: false, esModule: true })]
: [
new MiniCssExtractPlugin({
filename: `[name].[contenthash:8].css`,
ignoreOrder: true,
}),
]),
new HTMLPlugin({
template: join(rootPath, '.webpack', 'template.html'),
inject: 'body',
scriptLoading: 'defer',
minify: false,
chunks: ['index', 'plugin'],
filename: 'index.html',
}),
new VanillaExtractPlugin(),
new webpack.DefinePlugin({
'process.env': JSON.stringify({}),
'process.env.COVERAGE': JSON.stringify(!!buildFlags.coverage),
'process.env.NODE_ENV': JSON.stringify(buildFlags.mode),
runtimeConfig: JSON.stringify(runtimeConfig),
}),
new CopyPlugin({
patterns: [
{ from: resolve(rootPath, 'public'), to: resolve(rootPath, 'dist') },
],
}),
],
optimization: OptimizeOptionOptions(buildFlags),
devServer: {
hot: 'only',
liveReload: true,
client: undefined,
historyApiFallback: true,
static: {
directory: resolve(rootPath, 'public'),
publicPath: '/',
watch: true,
},
} as DevServerConfiguration,
} satisfies webpack.Configuration;
if (buildFlags.mode === 'production' && process.env.PERFSEE_TOKEN) {
config.devtool = 'hidden-nosources-source-map';
config.plugins.push(
new PerfseePlugin({
project: 'affine-toeverything',
})
);
}
if (buildFlags.mode === 'development') {
config.optimization = {
...config.optimization,
minimize: false,
runtimeChunk: false,
splitChunks: {
maxInitialRequests: Infinity,
chunks: 'all',
cacheGroups: {
defaultVendors: {
test: `[\\/]node_modules[\\/](?!.*vanilla-extract)`,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
styles: {
name: 'styles',
type: 'css/mini-extract',
chunks: 'all',
enforce: true,
},
},
},
};
}
if (
process.env.SENTRY_AUTH_TOKEN &&
process.env.SENTRY_ORG &&
process.env.SENTRY_PROJECT
) {
config.plugins.push(
sentryWebpackPlugin({
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT,
authToken: process.env.SENTRY_AUTH_TOKEN,
})
);
}
return config;
};

View File

@@ -0,0 +1,20 @@
const cssnano = require('cssnano');
module.exports = function (context) {
const plugins = [
cssnano({
preset: [
'default',
{
convertValues: false,
},
],
}),
];
return {
from: context.from,
plugins,
to: context.to,
};
};

View File

@@ -0,0 +1,115 @@
import type { BlockSuiteFeatureFlags, RuntimeConfig } from '@affine/env/global';
import type { BuildFlags } from '@affine/cli/config';
import { createRequire } from 'node:module';
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_surface: true,
enable_linked_page: true,
enable_bookmark_operation: false,
};
export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
const buildPreset: Record<string, RuntimeConfig> = {
stable: {
enablePlugin: false,
enableTestProperties: false,
enableBroadcastChannelProvider: true,
enableDebugPage: true,
changelogUrl: 'https://affine.pro/blog/what-is-new-affine-0717',
imageProxyUrl: 'https://workers.toeverything.workers.dev/proxy/image',
enablePreloading: true,
enableNewSettingModal: true,
enableNewSettingUnstableApi: false,
enableSQLiteProvider: true,
enableMoveDatabase: false,
enableNotificationCenter: false,
enableCloud: false,
serverAPI: 'https://localhost:3010',
editorFlags,
appVersion: packageJson.version,
editorVersion: packageJson.dependencies['@blocksuite/editor'],
},
// canary will be aggressive and enable all features
canary: {
enablePlugin: true,
enableTestProperties: true,
enableBroadcastChannelProvider: true,
enableDebugPage: true,
changelogUrl: 'https://affine.pro/blog/what-is-new-affine-0717',
imageProxyUrl: 'https://workers.toeverything.workers.dev/proxy/image',
enablePreloading: true,
enableNewSettingModal: true,
enableNewSettingUnstableApi: false,
enableSQLiteProvider: true,
enableMoveDatabase: false,
enableNotificationCenter: true,
enableCloud: false,
serverAPI: 'https://localhost:3010',
editorFlags,
appVersion: packageJson.version,
editorVersion: packageJson.dependencies['@blocksuite/editor'],
},
};
// beta and internal versions are the same as stable
buildPreset.beta = buildPreset.stable;
buildPreset.internal = buildPreset.stable;
const currentBuild = buildFlags.channel;
if (!(currentBuild in buildPreset)) {
throw new Error(`BUILD_TYPE ${currentBuild} is not supported`);
}
const currentBuildPreset = buildPreset[currentBuild];
const environmentPreset = {
enablePlugin: process.env.ENABLE_PLUGIN
? process.env.ENABLE_PLUGIN === 'true'
: currentBuildPreset.enablePlugin,
enableTestProperties: process.env.ENABLE_TEST_PROPERTIES
? process.env.ENABLE_TEST_PROPERTIES === 'true'
: currentBuildPreset.enableTestProperties,
enableBroadcastChannelProvider: process.env.ENABLE_BC_PROVIDER
? process.env.ENABLE_BC_PROVIDER !== 'false'
: currentBuildPreset.enableBroadcastChannelProvider,
changelogUrl: process.env.CHANGELOG_URL ?? currentBuildPreset.changelogUrl,
enablePreloading: process.env.ENABLE_PRELOADING
? process.env.ENABLE_PRELOADING === 'true'
: currentBuildPreset.enablePreloading,
enableNewSettingModal: process.env.ENABLE_NEW_SETTING_MODAL
? process.env.ENABLE_NEW_SETTING_MODAL === 'true'
: currentBuildPreset.enableNewSettingModal,
enableSQLiteProvider: process.env.ENABLE_SQLITE_PROVIDER
? process.env.ENABLE_SQLITE_PROVIDER === 'true'
: currentBuildPreset.enableSQLiteProvider,
enableNewSettingUnstableApi: process.env.ENABLE_NEW_SETTING_UNSTABLE_API
? process.env.ENABLE_NEW_SETTING_UNSTABLE_API === 'true'
: currentBuildPreset.enableNewSettingUnstableApi,
enableNotificationCenter: process.env.ENABLE_NOTIFICATION_CENTER
? process.env.ENABLE_NOTIFICATION_CENTER === 'true'
: currentBuildPreset.enableNotificationCenter,
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,
};
return {
...currentBuildPreset,
// environment preset will overwrite current build preset
// this environment variable is for debug proposes only
// do not put them into CI
...(process.env.CI ? {} : environmentPreset),
};
}

View File

@@ -0,0 +1,45 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1"
/>
<title>AFFiNE</title>
<meta name="theme-color" content="#fafafa" />
<link rel="manifest" href="/manifest.json" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" sizes="192x192" href="/chrome-192x192.png" />
<meta name="emotion-insertion-point" content="" />
<meta property="description" content="{description}" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:url" content="https://app.affine.pro/" />
<meta
name="twitter:title"
content="AFFiNEThere can be more than Notion and Miro."
/>
<meta name="twitter:description" content="{description}" />
<meta name="twitter:site" content="@AffineOfficial" />
<meta name="twitter:image" content="https://affine.pro/og.jpeg" />
<meta
property="og:title"
content="AFFiNEThere can be more than Notion and Miro."
/>
<meta property="og:type" content="website" />
<meta
property="og:description"
content="There can be more than Notion and Miro. AFFiNE is a next-gen knowledge base that brings planning, sorting and creating all together."
/>
<meta property="og:url" content="https://app.affine.pro/" />
<meta property="og:image" content="https://affine.pro/og.jpeg" />
<link
data-react-helmet="true"
rel="shortcut icon"
href="https://affine.pro/favicon.ico"
/>
</head>
<body>
<div id="app"></div>
</body>
</html>

View File

@@ -0,0 +1,11 @@
import type { BuildFlags } from '@affine/cli/config';
export function computeCacheKey(buildFlags: BuildFlags) {
return [
'1',
'node' + process.version,
buildFlags.mode,
buildFlags.distribution,
buildFlags.channel,
].join('-');
}

View File

@@ -0,0 +1,28 @@
import { createConfiguration, rootPath } from './config.js';
import { merge } from 'webpack-merge';
import { resolve } from 'node:path';
import type { BuildFlags } from '@affine/cli/config';
import { getRuntimeConfig } from './runtime-config.js';
export default async function (cli_env: any, _: any) {
const flags: BuildFlags = JSON.parse(
Buffer.from(cli_env.flags, 'hex').toString('utf-8')
);
console.log('build flags', flags);
const runtimeConfig = getRuntimeConfig(flags);
console.log('runtime config', runtimeConfig);
const config = createConfiguration(flags, runtimeConfig);
return merge(config, {
entry: {
index: {
asyncChunks: false,
import: resolve(rootPath, 'src/index.tsx'),
},
plugin: {
dependOn: ['index'],
asyncChunks: true,
import: resolve(rootPath, 'src/bootstrap/register-plugins.ts'),
},
},
});
}

83
apps/core/package.json Normal file
View File

@@ -0,0 +1,83 @@
{
"name": "@affine/core",
"type": "module",
"private": true,
"version": "0.7.0-canary.54",
"scripts": {
"build": "yarn -T run build-core",
"dev": "yarn -T run dev-core",
"static-server": "ts-node-esm ./server.mts"
},
"dependencies": {
"@affine-test/fixtures": "workspace:*",
"@affine/component": "workspace:*",
"@affine/copilot": "workspace:*",
"@affine/debug": "workspace:*",
"@affine/env": "workspace:*",
"@affine/graphql": "workspace:*",
"@affine/i18n": "workspace:*",
"@affine/jotai": "workspace:*",
"@affine/templates": "workspace:*",
"@affine/workspace": "workspace:*",
"@blocksuite/block-std": "0.0.0-20230720073515-bea92e0f-nightly",
"@blocksuite/blocks": "0.0.0-20230720073515-bea92e0f-nightly",
"@blocksuite/editor": "0.0.0-20230720073515-bea92e0f-nightly",
"@blocksuite/global": "0.0.0-20230720073515-bea92e0f-nightly",
"@blocksuite/icons": "^2.1.27",
"@blocksuite/lit": "0.0.0-20230720073515-bea92e0f-nightly",
"@blocksuite/store": "0.0.0-20230720073515-bea92e0f-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.2",
"@react-hookz/web": "^23.1.0",
"async-call-rpc": "^6.3.1",
"cmdk": "^0.2.0",
"css-spring": "^4.1.0",
"cssnano": "^6.0.1",
"graphql": "^16.7.1",
"jotai": "^2.2.2",
"jotai-devtools": "^0.6.0",
"lit": "^2.7.6",
"lottie-web": "^5.12.2",
"mini-css-extract-plugin": "^2.7.6",
"next-themes": "^0.2.1",
"postcss-loader": "^7.3.3",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-is": "18.2.0",
"react-resizable-panels": "^0.0.54",
"react-router-dom": "^6.14.2",
"rxjs": "^7.8.1",
"ses": "^0.18.5",
"swr": "2.1.5",
"y-protocols": "^1.0.5",
"yjs": "^13.6.7",
"zod": "^3.21.4"
},
"devDependencies": {
"@perfsee/webpack": "^1.8.2",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"@sentry/webpack-plugin": "^2.5.0",
"@svgr/webpack": "^8.0.1",
"@swc/core": "^1.3.71",
"@types/webpack-env": "^1.18.1",
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.8.1",
"express": "^4.18.2",
"html-webpack-plugin": "^5.5.3",
"raw-loader": "^4.0.2",
"style-loader": "^3.3.3",
"swc-loader": "^0.2.3",
"swc-plugin-coverage-instrument": "^0.0.19",
"thread-loader": "^4.0.2",
"ts-node": "^10.9.1",
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"webpack-merge": "^5.9.0"
}
}

51
apps/core/project.json Normal file
View File

@@ -0,0 +1,51 @@
{
"name": "@affine/core",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"root": "apps/core",
"sourceRoot": "apps/core/src",
"targets": {
"build": {
"executor": "nx:run-script",
"dependsOn": ["^build"],
"inputs": [
"{projectRoot}/**/*",
"{workspaceRoot}/packages/component/src/**/*",
"{workspaceRoot}/packages/debug/src/**/*",
"{workspaceRoot}/packages/graphql/src/**/*",
"{workspaceRoot}/packages/hooks/src/**/*",
"{workspaceRoot}/packages/jotai/src/**/*",
"{workspaceRoot}/packages/templates/src/**/*",
"{workspaceRoot}/packages/workspace/src/**/*",
{
"env": "BUILD_TYPE"
},
{
"env": "PERFSEE_TOKEN"
},
{
"env": "SENTRY_ORG"
},
{
"env": "SENTRY_PROJECT"
},
{
"env": "SENTRY_AUTH_TOKEN"
},
{
"env": "NEXT_PUBLIC_SENTRY_DSN"
},
{
"env": "DISTRIBUTION"
},
{
"env": "COVERAGE"
}
],
"options": {
"script": "build"
},
"outputs": ["{projectRoot}/dist", "{projectRoot}/public/plugins"]
}
}
}

4
apps/core/public/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*.js
*.map
plugins

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -0,0 +1,93 @@
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

@@ -0,0 +1,93 @@
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

@@ -0,0 +1,91 @@
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

@@ -0,0 +1,93 @@
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.

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

15
apps/core/server.mts Normal file
View File

@@ -0,0 +1,15 @@
// static server for web app
import express from 'express';
const app = express();
const PORT = process.env.PORT || 8080;
app.use('/', express.static('dist'));
app.get('/*', (req, res) => {
res.sendFile('index.html', { root: 'dist' });
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});

View File

@@ -1,7 +1,7 @@
import { DebugLogger } from '@affine/debug';
import { initEmptyPage, initPageWithPreloading } from '@affine/env/blocksuite';
import {
DEFAULT_HELLO_WORLD_PAGE_ID,
DEFAULT_HELLO_WORLD_PAGE_ID_SUFFIX,
DEFAULT_WORKSPACE_NAME,
PageNotFoundError,
} from '@affine/env/constant';
@@ -16,10 +16,10 @@ import {
CRUD,
saveWorkspaceToLocalStorage,
} from '@affine/workspace/local/crud';
import { getOrCreateWorkspace } from '@affine/workspace/manager';
import { createIndexedDBDownloadProvider } from '@affine/workspace/providers';
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
import { nanoid } from '@blocksuite/store';
import { useStaticBlockSuiteWorkspace } from '@toeverything/hooks/use-block-suite-workspace';
import { useStaticBlockSuiteWorkspace } from '@toeverything/plugin-infra/__internal__/react';
import {
BlockSuitePageList,
@@ -36,13 +36,13 @@ export const LocalAdapter: WorkspaceAdapter<WorkspaceFlavour.LOCAL> = {
loadPriority: LoadPriority.LOW,
Events: {
'app:init': () => {
const blockSuiteWorkspace = createEmptyBlockSuiteWorkspace(
const blockSuiteWorkspace = getOrCreateWorkspace(
nanoid(),
WorkspaceFlavour.LOCAL
);
blockSuiteWorkspace.meta.setName(DEFAULT_WORKSPACE_NAME);
const page = blockSuiteWorkspace.createPage({
id: DEFAULT_HELLO_WORLD_PAGE_ID,
id: `${blockSuiteWorkspace.id}-${DEFAULT_HELLO_WORLD_PAGE_ID_SUFFIX}`,
});
if (runtimeConfig.enablePreloading) {
initPageWithPreloading(page).catch(err => {

56
apps/core/src/app.tsx Normal file
View File

@@ -0,0 +1,56 @@
import '@affine/component/theme/global.css';
import '@affine/component/theme/theme.css';
import { AffineContext } from '@affine/component/context';
import { WorkspaceFallback } from '@affine/component/workspace';
import { createI18n, setUpLanguage } from '@affine/i18n';
import { CacheProvider } from '@emotion/react';
import type { PropsWithChildren, ReactElement } from 'react';
import { lazy, memo, Suspense, useEffect } from 'react';
import { RouterProvider } from 'react-router-dom';
import { router } from './router';
import createEmotionCache from './utils/create-emotion-cache';
const i18n = createI18n();
const cache = createEmotionCache();
const DevTools = lazy(() =>
import('jotai-devtools').then(m => ({ default: m.DevTools }))
);
const DebugProvider = ({ children }: PropsWithChildren): ReactElement => {
return (
<>
<Suspense>{process.env.DEBUG_JOTAI === 'true' && <DevTools />}</Suspense>
{children}
</>
);
};
const future = {
v7_startTransition: true,
} as const;
export const App = memo(function App() {
useEffect(() => {
document.documentElement.lang = i18n.language;
// todo(himself65): this is a hack, we should use a better way to set the language
setUpLanguage(i18n)?.catch(error => {
console.error(error);
});
}, []);
return (
<CacheProvider value={cache}>
<AffineContext>
<DebugProvider>
<RouterProvider
fallbackElement={<WorkspaceFallback key="RouterFallback" />}
router={router}
future={future}
/>
</DebugProvider>
</AffineContext>
</CacheProvider>
);
});

View File

@@ -0,0 +1,105 @@
import { useAtom } from 'jotai';
import { atomWithStorage, createJSONStorage } from 'jotai/utils';
import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { router } from '../router';
export type History = {
stack: string[];
current: number;
skip: boolean;
};
export const MAX_HISTORY = 50;
const historyBaseAtom = atomWithStorage<History>(
'router-history',
{
stack: [],
current: 0,
skip: false,
},
createJSONStorage(() => sessionStorage)
);
historyBaseAtom.onMount = set => {
const unsubscribe = router.subscribe(state => {
set(prev => {
const url = state.location.pathname;
// if stack top is the same as current, skip
if (prev.stack[prev.current] === url) {
return prev;
}
if (prev.skip) {
return {
stack: [...prev.stack],
current: prev.current,
skip: false,
};
} else {
if (prev.current < prev.stack.length - 1) {
const newStack = prev.stack.slice(0, prev.current);
newStack.push(url);
if (newStack.length > MAX_HISTORY) {
newStack.shift();
}
return {
stack: newStack,
current: newStack.length - 1,
skip: false,
};
} else {
const newStack = [...prev.stack, url];
if (newStack.length > MAX_HISTORY) {
newStack.shift();
}
return {
stack: newStack,
current: newStack.length - 1,
skip: false,
};
}
}
});
});
return () => {
unsubscribe();
};
};
export function useHistoryAtom() {
const navigate = useNavigate();
const [base, setBase] = useAtom(historyBaseAtom);
return [
base,
useCallback(
(forward: boolean) => {
setBase(prev => {
if (forward) {
const target = Math.min(prev.stack.length - 1, prev.current + 1);
const url = prev.stack[target];
navigate(url);
return {
...prev,
current: target,
skip: true,
};
} else {
const target = Math.max(0, prev.current - 1);
const url = prev.stack[target];
navigate(url);
return {
...prev,
current: target,
skip: true,
};
}
});
},
[setBase, navigate]
),
] as const;
}

View File

@@ -0,0 +1,12 @@
import { currentPageIdAtom } from '@toeverything/plugin-infra/atom';
import { atom } from 'jotai/vanilla';
import { pageSettingFamily } from './index';
export const currentModeAtom = atom<'page' | 'edgeless'>(get => {
const pageId = get(currentPageIdAtom);
if (!pageId) {
return 'page';
}
return get(pageSettingFamily(pageId)).mode;
});

View File

@@ -1,3 +1,4 @@
import { isDesktop } from '@affine/env/constant';
import { atom, useAtom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
@@ -49,7 +50,7 @@ export const fontStyleOptions = [
}[];
const appSettingBaseAtom = atomWithStorage<AppSetting>('affine-settings', {
clientBorder: false,
clientBorder: isDesktop,
fullWidthLayout: false,
windowFrameStyle: 'frameless',
fontStyle: 'Sans',

View File

@@ -0,0 +1,133 @@
import { migrateToSubdoc } from '@affine/env/blocksuite';
import { setupGlobal } from '@affine/env/global';
import type {
LocalIndexedDBDownloadProvider,
WorkspaceAdapter,
} from '@affine/env/workspace';
import { WorkspaceFlavour, WorkspaceVersion } from '@affine/env/workspace';
import type { RootWorkspaceMetadata } from '@affine/workspace/atom';
import {
type RootWorkspaceMetadataV2,
rootWorkspacesMetadataAtom,
workspaceAdaptersAtom,
} from '@affine/workspace/atom';
import {
migrateLocalBlobStorage,
upgradeV1ToV2,
} from '@affine/workspace/migration';
import { createIndexedDBDownloadProvider } from '@affine/workspace/providers';
import { assertExists } from '@blocksuite/global/utils';
import { rootStore } from '@toeverything/plugin-infra/atom';
import { WorkspaceAdapters } from '../adapters/workspace';
console.log('setup global');
setupGlobal();
rootStore.set(
workspaceAdaptersAtom,
WorkspaceAdapters as Record<
WorkspaceFlavour,
WorkspaceAdapter<WorkspaceFlavour>
>
);
const value = localStorage.getItem('jotai-workspaces');
if (value) {
try {
const metadata = JSON.parse(value) as RootWorkspaceMetadata[];
const promises: Promise<void>[] = [];
const newMetadata = [...metadata];
metadata.forEach(oldMeta => {
if (!('version' in oldMeta)) {
const adapter = WorkspaceAdapters[oldMeta.flavour];
assertExists(adapter);
const upgrade = async () => {
const workspace = await adapter.CRUD.get(oldMeta.id);
if (!workspace) {
console.warn('cannot find workspace', oldMeta.id);
return;
}
if (workspace.flavour !== WorkspaceFlavour.LOCAL) {
console.warn('not supported');
return;
}
const doc = workspace.blockSuiteWorkspace.doc;
const provider = createIndexedDBDownloadProvider(workspace.id, doc, {
awareness: workspace.blockSuiteWorkspace.awarenessStore.awareness,
}) as LocalIndexedDBDownloadProvider;
provider.sync();
await provider.whenReady;
const newDoc = migrateToSubdoc(doc);
if (doc === newDoc) {
console.log('doc not changed');
return;
}
const newWorkspace = upgradeV1ToV2(workspace);
const newId = await adapter.CRUD.create(
newWorkspace.blockSuiteWorkspace
);
await adapter.CRUD.delete(workspace as any);
console.log('migrated', oldMeta.id, newId);
const index = newMetadata.findIndex(meta => meta.id === oldMeta.id);
newMetadata[index] = {
...oldMeta,
id: newId,
version: WorkspaceVersion.SubDoc,
};
await migrateLocalBlobStorage(workspace.id, newId);
};
// create a new workspace and push it to metadata
promises.push(upgrade());
}
});
await Promise.all(promises)
.then(() => {
console.log('migration done');
})
.catch(() => {
console.error('migration failed');
})
.finally(() => {
localStorage.setItem('jotai-workspaces', JSON.stringify(newMetadata));
window.dispatchEvent(new CustomEvent('migration-done'));
window.$migrationDone = true;
});
} catch (e) {
console.error('error when migrating data', e);
}
}
const createFirst = (): RootWorkspaceMetadataV2[] => {
const Plugins = Object.values(WorkspaceAdapters).sort(
(a, b) => a.loadPriority - b.loadPriority
);
return Plugins.flatMap(Plugin => {
return Plugin.Events['app:init']?.().map(
id =>
({
id,
flavour: Plugin.flavour,
// new workspace should all support sub-doc feature
version: WorkspaceVersion.SubDoc,
}) satisfies RootWorkspaceMetadataV2
);
}).filter((ids): ids is RootWorkspaceMetadataV2 => !!ids);
};
await rootStore
.get(rootWorkspacesMetadataAtom)
.then(meta => {
if (meta.length === 0 && localStorage.getItem('is-first-open') === null) {
const result = createFirst();
console.info('create first workspace', result);
localStorage.setItem('is-first-open', 'false');
rootStore.set(rootWorkspacesMetadataAtom, result).catch(console.error);
}
})
.catch(console.error);

View File

@@ -0,0 +1,200 @@
/// <reference types="@types/webpack-env" />
import 'ses';
import * as AFFiNEComponent from '@affine/component';
import * as BlockSuiteBlocksStd from '@blocksuite/blocks/std';
import { DisposableGroup } from '@blocksuite/global/utils';
import * as BlockSuiteGlobalUtils from '@blocksuite/global/utils';
import * as Icons from '@blocksuite/icons';
import * as Atom from '@toeverything/plugin-infra/atom';
import {
editorItemsAtom,
headerItemsAtom,
registeredPluginAtom,
rootStore,
windowItemsAtom,
} from '@toeverything/plugin-infra/atom';
import type {
CallbackMap,
PluginContext,
} from '@toeverything/plugin-infra/entry';
import * as Jotai from 'jotai';
import { Provider } from 'jotai/react';
import * as JotaiUtils from 'jotai/utils';
import type { PropsWithChildren } from 'react';
import * as React from 'react';
import * as ReactJSXRuntime from 'react/jsx-runtime';
import * as ReactDom from 'react-dom';
import * as ReactDomClient from 'react-dom/client';
if (!process.env.COVERAGE) {
lockdown({
evalTaming: 'unsafeEval',
overrideTaming: 'severe',
consoleTaming: 'unsafe',
errorTaming: 'unsafe',
errorTrapping: 'platform',
unhandledRejectionTrapping: 'report',
});
}
const PluginProvider = ({ children }: PropsWithChildren) =>
React.createElement(
Provider,
{
store: rootStore,
},
children
);
const customRequire = (id: string) => {
if (id === '@toeverything/plugin-infra/atom') {
return Atom;
}
if (id === 'react') {
return React;
}
if (id === 'react/jsx-runtime') {
return ReactJSXRuntime;
}
if (id === 'react-dom') {
return ReactDom;
}
if (id === 'react-dom/client') {
return ReactDomClient;
}
if (id === '@blocksuite/icons') {
return Icons;
}
if (id === '@affine/component') {
return AFFiNEComponent;
}
if (id === '@blocksuite/blocks/std') {
return BlockSuiteBlocksStd;
}
if (id === '@blocksuite/global/utils') {
return BlockSuiteGlobalUtils;
}
if (id === 'jotai') {
return Jotai;
}
if (id === 'jotai/utils') {
return JotaiUtils;
}
throw new Error(`Cannot find module '${id}'`);
};
const createGlobalThis = () => {
return {
process: Object.freeze({
env: {
NODE_ENV: process.env.NODE_ENV,
},
}),
// UNSAFE: React will read `window` and `document`
window,
document,
navigator,
userAgent: navigator.userAgent,
// todo: permission control
fetch: globalThis.fetch,
// fixme: use our own db api
indexedDB: globalThis.indexedDB,
IDBRequest: globalThis.IDBRequest,
IDBDatabase: globalThis.IDBDatabase,
IDBCursorWithValue: globalThis.IDBCursorWithValue,
IDBFactory: globalThis.IDBFactory,
IDBKeyRange: globalThis.IDBKeyRange,
IDBOpenDBRequest: globalThis.IDBOpenDBRequest,
IDBTransaction: globalThis.IDBTransaction,
IDBObjectStore: globalThis.IDBObjectStore,
IDBIndex: globalThis.IDBIndex,
IDBCursor: globalThis.IDBCursor,
IDBVersionChangeEvent: globalThis.IDBVersionChangeEvent,
exports: {},
console: globalThis.console,
require: customRequire,
};
};
const group = new DisposableGroup();
const pluginList = (await (
await fetch(new URL(`./plugins/plugin-list.json`, window.location.origin))
).json()) as { name: string; assets: string[]; release: boolean }[];
await Promise.all(
pluginList.map(({ name: plugin, release, assets }) => {
if (!release && process.env.NODE_ENV !== 'development') {
return Promise.resolve();
}
const pluginCompartment = new Compartment(createGlobalThis(), {});
const pluginGlobalThis = pluginCompartment.globalThis;
const baseURL = new URL(`./plugins/${plugin}/`, window.location.origin);
const entryURL = new URL('index.js', baseURL);
rootStore.set(registeredPluginAtom, prev => [...prev, plugin]);
return fetch(entryURL).then(async res => {
if (assets.length > 0) {
await Promise.all(
assets.map(async asset => {
if (asset.endsWith('.css')) {
const res = await fetch(new URL(asset, baseURL));
if (res.ok) {
// todo: how to put css file into sandbox?
return res.text().then(text => {
const style = document.createElement('style');
style.setAttribute('plugin-id', plugin);
style.textContent = text;
document.head.appendChild(style);
});
}
return null;
} else {
return Promise.resolve();
}
})
);
}
const codeText = await res.text();
pluginCompartment.evaluate(codeText, {
__evadeHtmlCommentTest__: true,
});
pluginGlobalThis.__INTERNAL__ENTRY = {
register: (part, callback) => {
if (part === 'headerItem') {
rootStore.set(headerItemsAtom, items => ({
...items,
[plugin]: callback as CallbackMap['headerItem'],
}));
} else if (part === 'editor') {
rootStore.set(editorItemsAtom, items => ({
...items,
[plugin]: callback as CallbackMap['editor'],
}));
} else if (part === 'window') {
rootStore.set(windowItemsAtom, items => ({
...items,
[plugin]: callback as CallbackMap['window'],
}));
} else {
throw new Error(`Unknown part: ${part}`);
}
},
utils: {
PluginProvider,
},
} satisfies PluginContext;
const dispose = pluginCompartment.evaluate(
'exports.entry(__INTERNAL__ENTRY)'
);
if (typeof dispose !== 'function') {
throw new Error('Plugin entry must return a function');
}
pluginGlobalThis.__INTERNAL__ENTRY = undefined;
group.add(dispose);
});
})
);
console.log('register plugins finished');

View File

@@ -1,20 +1,16 @@
import { initEmptyPage } from '@affine/env/blocksuite';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
import { getOrCreateWorkspace } from '@affine/workspace/manager';
import type { EditorContainer } from '@blocksuite/editor';
import type { Page } from '@blocksuite/store';
import { Generator } from '@blocksuite/store';
import type React from 'react';
import { useCallback } from 'react';
import { BlockSuiteEditor } from '../../blocksuite/block-suite-editor';
const blockSuiteWorkspace = createEmptyBlockSuiteWorkspace(
const blockSuiteWorkspace = getOrCreateWorkspace(
'test',
WorkspaceFlavour.LOCAL,
{
idGenerator: Generator.AutoIncrement,
}
WorkspaceFlavour.LOCAL
);
const page = blockSuiteWorkspace.createPage({ id: 'page0' });

View File

@@ -4,22 +4,19 @@ import type {
WorkspaceNotFoundError,
} from '@affine/env/constant';
import { PageNotFoundError } from '@affine/env/constant';
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
import {
rootCurrentPageIdAtom,
rootCurrentWorkspaceIdAtom,
rootWorkspacesMetadataAtom,
} from '@affine/workspace/atom';
import { rootStore } from '@toeverything/plugin-infra/manager';
currentPageIdAtom,
currentWorkspaceIdAtom,
rootStore,
} from '@toeverything/plugin-infra/atom';
import { useAtomValue } from 'jotai/react';
import { Provider } from 'jotai/react';
import type { NextRouter } from 'next/router';
import type { ErrorInfo, ReactElement, ReactNode } from 'react';
import type React from 'react';
import { Component } from 'react';
export type AffineErrorBoundaryProps = React.PropsWithChildren<{
router: NextRouter;
}>;
import { useLocation, useParams } from 'react-router-dom';
export type AffineErrorBoundaryProps = React.PropsWithChildren;
type AffineError =
| QueryParamError
@@ -32,13 +29,13 @@ interface AffineErrorBoundaryState {
error: AffineError | null;
}
export const DumpInfo = (props: Pick<AffineErrorBoundaryProps, 'router'>) => {
const router = props.router;
export const DumpInfo = () => {
const location = useLocation();
const metadata = useAtomValue(rootWorkspacesMetadataAtom);
const currentWorkspaceId = useAtomValue(rootCurrentWorkspaceIdAtom);
const currentPageId = useAtomValue(rootCurrentPageIdAtom);
const path = router.asPath;
const query = router.query;
const currentWorkspaceId = useAtomValue(currentWorkspaceIdAtom);
const currentPageId = useAtomValue(currentPageIdAtom);
const path = location.pathname;
const query = useParams();
return (
<>
<div>
@@ -91,24 +88,6 @@ export class AffineErrorBoundary extends Component<
Cannot find page {error.pageId} in workspace{' '}
{error.workspace.id}
</span>
<button
onClick={() => {
this.props.router
.replace({
pathname: '/workspace/[workspaceId]/[pageId]',
query: {
workspaceId: error.workspace.id,
pageId: error.workspace.meta.pageMetas[0].id,
},
})
.finally(() => {
this.setState({ error: null });
});
}}
>
{' '}
refresh{' '}
</button>
</>
</>
);
@@ -124,7 +103,7 @@ export class AffineErrorBoundary extends Component<
<>
{errorDetail}
<Provider key="JotaiProvider" store={rootStore}>
<DumpInfo router={this.props.router} />
<DumpInfo />
</Provider>
</>
);

View File

@@ -79,7 +79,7 @@ const NameWorkspaceContent = ({
<div className={style.buttonGroup}>
<Button
data-testid="create-workspace-close-button"
type="light"
type="primary"
onClick={onClose}
>
{t.Cancel()}
@@ -155,7 +155,7 @@ const SetDBLocationContent = ({
<Button
disabled={opening}
data-testid="create-workspace-customize-button"
type="light"
type="primary"
onClick={handleSelectDBFileLocation}
>
{t['Customize']()}

View File

@@ -47,7 +47,7 @@ const LanguageMenuContent: FC<{
</>
);
};
export const LanguageMenu: FC<{ triggerProps: ButtonProps }> = ({
export const LanguageMenu: FC<{ triggerProps?: ButtonProps }> = ({
triggerProps,
}) => {
const i18n = useI18N();

View File

@@ -99,7 +99,7 @@ export const WorkspaceDeleteModal = ({
data-testid="delete-workspace-confirm-button"
disabled={!allowDelete}
onClick={handleDelete}
type="danger"
type="error"
shape="circle"
style={{ marginLeft: '24px' }}
>

View File

@@ -37,7 +37,7 @@ export const WorkspaceLeave = ({ open, onClose }: WorkspaceDeleteProps) => {
</Button>
<Button
onClick={handleLeave}
type="danger"
type="error"
shape="circle"
style={{ marginLeft: '24px' }}
>

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