From e3ffd04804759c309fd8d1f301163c34b11748f1 Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Mon, 26 Jun 2023 21:39:07 +0800 Subject: [PATCH] feat(docs): bootstrapping using blocksuite (#2859) --- apps/docs/entries.ts | 18 ++-- apps/docs/src/app.tsx | 54 +++++++----- apps/docs/src/atom.ts | 11 +++ apps/docs/src/components/editor.tsx | 82 +++++++++--------- .../{sidebar.tsx => sidebar/index.tsx} | 15 ++++ .../src/components/sidebar/save-to-local.tsx | 28 ++++++ apps/docs/src/index.tsx | 12 +-- apps/docs/src/pages/binary | Bin 0 -> 8249 bytes apps/docs/src/server-fns.ts | 10 +++ 9 files changed, 148 insertions(+), 82 deletions(-) create mode 100644 apps/docs/src/atom.ts rename apps/docs/src/components/{sidebar.tsx => sidebar/index.tsx} (50%) create mode 100644 apps/docs/src/components/sidebar/save-to-local.tsx create mode 100644 apps/docs/src/pages/binary create mode 100644 apps/docs/src/server-fns.ts diff --git a/apps/docs/entries.ts b/apps/docs/entries.ts index 6103ac4d5..63d05f6bd 100644 --- a/apps/docs/entries.ts +++ b/apps/docs/entries.ts @@ -1,21 +1,17 @@ -import { defineEntries } from 'waku/server'; +import { defineRouter } from 'waku/router/server'; -export default defineEntries( - // getEntry +export default defineRouter( async id => { switch (id) { - case 'App': - return import('./src/app.js') as any; + case 'index': { + const { default: AppCreator } = await import('./src/app.js'); + return AppCreator(id); + } default: return null; } }, - // getBuildConfig async () => { - return { - '/': { - elements: [['App', {}]], - }, - }; + return ['index']; } ); diff --git a/apps/docs/src/app.tsx b/apps/docs/src/app.tsx index 5f54c19e5..e6c746f48 100644 --- a/apps/docs/src/app.tsx +++ b/apps/docs/src/app.tsx @@ -1,36 +1,44 @@ /// 'use server'; +import { existsSync, readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + import type { ReactElement } from 'react'; import { lazy } from 'react'; -import { Sidebar } from './components/sidebar.js'; +import { Sidebar } from './components/sidebar/index.js'; +import { saveFile } from './server-fns.js'; const Editor = lazy(() => import('./components/editor.js').then(({ Editor }) => ({ default: Editor })) ); -const markdown = `--- -title: AFFiNE Developer Documentation ---- +const __dirname = fileURLToPath(new URL('.', import.meta.url)); -## To Shape, not to adapt +const AppCreator = (pathname: string) => + function App(): ReactElement { + let path = resolve(__dirname, 'pages', 'binary'); + if (!existsSync(path)) { + path = resolve(__dirname, '..', '..', 'src', 'pages', 'binary'); + } + const buffer = [...readFileSync(path)]; ---- + return ( +
+ +
+ +
+
+ ); + }; -**Powered by BlockSuite** -`; - -const App = (): ReactElement => { - return ( -
- -
- -
-
- ); -}; - -export default App; +export default AppCreator; diff --git a/apps/docs/src/atom.ts b/apps/docs/src/atom.ts new file mode 100644 index 000000000..4014bdc1e --- /dev/null +++ b/apps/docs/src/atom.ts @@ -0,0 +1,11 @@ +import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models'; +import { atom } from 'jotai/vanilla'; + +export const workspaceAtom = atom(async () => { + const { Workspace } = await import('@blocksuite/store'); + return new Workspace({ + id: 'test-workspace', + }) + .register(AffineSchemas) + .register(__unstableSchemas); +}); diff --git a/apps/docs/src/components/editor.tsx b/apps/docs/src/components/editor.tsx index ac9bd96cc..a1bb47b88 100644 --- a/apps/docs/src/components/editor.tsx +++ b/apps/docs/src/components/editor.tsx @@ -2,54 +2,52 @@ import '@blocksuite/editor/themes/affine.css'; import { BlockSuiteEditor } from '@affine/component/block-suite-editor'; -import { ContentParser } from '@blocksuite/blocks/content-parser'; -import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models'; -import { assertExists, Workspace } from '@blocksuite/store'; +import type { Page } from '@blocksuite/store'; +import { useAtomValue } from 'jotai/react'; import type { ReactElement } from 'react'; -import { useCallback } from 'react'; +import { use } from 'react'; +import { applyUpdate } from 'yjs'; -const workspace = new Workspace({ - id: 'local-workspace', -}) - .register(AffineSchemas) - .register(__unstableSchemas); - -const page = workspace.createPage({ - id: 'example-page', -}); +import { workspaceAtom } from '../atom.js'; export type EditorProps = { - text: string; + workspaceId: string; + pageId: string; + binary?: number[]; + onSave: (binary: any) => Promise; }; export const Editor = (props: EditorProps): ReactElement => { - return ( - { - const text = props.text; - await page.waitForLoaded(); - const metadata = text.split('---\n')[1]; - assertExists(metadata); + const workspace = useAtomValue(workspaceAtom); + let page = workspace.getPage('page0') as Page; + if (!page) { + page = workspace.createPage({ + id: 'page0', + }); + } - // find title - const title = metadata.split('title: ')[1]?.split('\n')[0]; - const pageBlockId = page.addBlock('affine:page', { - title: new page.Text(title), - }); - page.addBlock('affine:surface', {}, pageBlockId); - const noteBlockId = page.addBlock('affine:note', {}, pageBlockId); - const contentParser = new ContentParser(page); - const content = text.split('---\n').splice(2).join('---\n'); - assertExists(content); - await contentParser.importMarkdown(content, noteBlockId); - page.awarenessStore.setReadonly(page, true); - page.awarenessStore.setFlag('enable_drag_handle', false); - }, - [props.text] - )} - /> - ); + if (props.binary && !page.root) { + use( + page.waitForLoaded().then(() => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + applyUpdate(page._ySpaceDoc, new Uint8Array(props.binary as number[])); + }) + ); + if (import.meta.env.MODE !== 'development') { + page.awarenessStore.setReadonly(page, true); + } + } else if (!page.root) { + use( + page.waitForLoaded().then(() => { + const pageBlockId = page.addBlock('affine:page', { + title: new page.Text(''), + }); + page.addBlock('affine:surface', {}, pageBlockId); + const noteBlockId = page.addBlock('affine:note', {}, pageBlockId); + page.addBlock('affine:paragraph', {}, noteBlockId); + }) + ); + } + return {}} />; }; diff --git a/apps/docs/src/components/sidebar.tsx b/apps/docs/src/components/sidebar/index.tsx similarity index 50% rename from apps/docs/src/components/sidebar.tsx rename to apps/docs/src/components/sidebar/index.tsx index eb401db0b..d7c701a85 100644 --- a/apps/docs/src/components/sidebar.tsx +++ b/apps/docs/src/components/sidebar/index.tsx @@ -1,3 +1,15 @@ +'use server'; + +import { lazy } from 'react'; + +import { saveFile } from '../../server-fns.js'; + +const SaveToLocal = lazy(() => + import('./save-to-local.js').then(({ SaveToLocal }) => ({ + default: SaveToLocal, + })) +); + export const Sidebar = () => { return (
{ AFFiNE
+ {import.meta.env.MODE === 'development' && ( + + )} ); }; diff --git a/apps/docs/src/components/sidebar/save-to-local.tsx b/apps/docs/src/components/sidebar/save-to-local.tsx new file mode 100644 index 000000000..d469a992e --- /dev/null +++ b/apps/docs/src/components/sidebar/save-to-local.tsx @@ -0,0 +1,28 @@ +'use client'; +import { assertExists } from '@blocksuite/global/utils'; +import { useAtomValue } from 'jotai/react'; +import { useCallback } from 'react'; +import { encodeStateAsUpdate } from 'yjs'; + +import { workspaceAtom } from '../../atom.js'; + +type SaveToLocalProps = { + saveFile: (update: number[]) => void; +}; + +export const SaveToLocal = (props: SaveToLocalProps) => { + const workspace = useAtomValue(workspaceAtom); + const saveFile = props.saveFile; + const onSave = useCallback(() => { + const page = workspace.getPage('page0'); + assertExists(page); + saveFile([...encodeStateAsUpdate(page.spaceDoc)]); + }, [saveFile, workspace]); + return ( +
+
+ +
+
+ ); +}; diff --git a/apps/docs/src/index.tsx b/apps/docs/src/index.tsx index 652a0a60d..481940766 100644 --- a/apps/docs/src/index.tsx +++ b/apps/docs/src/index.tsx @@ -1,14 +1,14 @@ +import '@blocksuite/editor/themes/affine.css'; import './index.css'; import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; -import { serve } from 'waku/client'; +import { Router } from 'waku/router/client'; -const App = serve('App'); -const rootElement = ( +const root = createRoot(document.getElementById('root') as HTMLElement); + +root.render( - + ); - -createRoot(document.getElementById('root') as HTMLElement).render(rootElement); diff --git a/apps/docs/src/pages/binary b/apps/docs/src/pages/binary new file mode 100644 index 0000000000000000000000000000000000000000..3d63fdf2b766f0d7d29f8ffe7548bc1aee2cd079 GIT binary patch literal 8249 zcmb7JYit}>72Z1!$E@Qdh!Tk+!MXwBQoFV{YsX0uYFyiili0Q6CT>FAWOirv;a%@; zW@o*-{?$YQ5~L6aq)35+i%8Q}q6GTzp9;u>)Gckr4+s#6{(+>bib$nUrDdWL}>2Vl2$VYD0iYdoxAq`PiSqN^Q9$q&%zGrr%hTyx4=N8 zJSb=aguUlQn|cv(`aODkF9I)9GB0lRfB?E@H7m7Q%P|}$mCK64i>#%xh1BeWdxj^x znsSUFZK&nQkCFO#J4XD(aej^wi6C#uMQnoq3)KH;0N^akINc)3i_`arx^Le;gf+5h zyd3VG%FX6QFr1WNp_bclvIKX>rp}Rw43cHnU2ZF|vuGk)Sm%IGQn1+0a9@ z@_ADw)PU^-p##wj0QWc*N=L+o-bs-E*d07&%ov53oyt2L@=u6bF|tsJPLWT>a<-K> zV(E~THKG|SSb@1u`qo@W8G{)wktV~=hA6*|k;x!&JI2!6*GG)uZCeF#=OyA$%i*7| zB5tWu=6n@E2JB^ic4^M2&L5sv(I(lG8A zw!;hhABi?-=ca*`zYsl6|8Ujc$TG^f;lz@Vu@{MSQuPfct*!^BqG3Z2byXq?wVbG{ zbrN;8Hc?k=5ar1jQ55$At2M6@_QlvjGXUQ|JVe0JyObk#Mgu@Q&j|ke<{L4Y>9J} z1FR>zD!tqG4E8ydh)9#O5?0i5W6#!!^=xgdX9Xgx9UQBtaXVVTdIPYss6|6~S-Gev zYdQe;Z~U(85kOy6R+xrkm_~uT*0I<$9*t?(>K^qe>ahBl`t))!nt9TfEUrgv2$P2thOvocEo2 z$v(vBdPS`-Cay~EfHZu~SAM!_)t1W-%}QYV7b*{(=Y$`0h8CQ2O$dWM59gh03D7r2XaqvfY0H-Capl%l?r7yqTdJvr7=2q68=lj|hQHTt(^S8q zX-n^1yM=}mj90byR4tmzn3`i3El|7-w|qW8Tf z{JNwq=l8dUd#3Uv@lJ%0tzW%I{eoWcqNIE&YYq)*SfC#lKQmCPphuW1o zL+wgcsPTZhB0^2dOizYK-4uyq_1pw~vesHw&<#F|nrgykKAQ1CgMecl)5A5#V z+136~IUzt^mqB`34)S`PAg@<}^rkOWfUnE&7Z}Jlz$tGsteznp(+3(pP~V^tx>rw&!7;eI3!8?uW~jj*=C{kg%Lh-CM^J zvr+Gv=!A~P6>2%$6MB8e)d{`kadkppiY-rr)#s+f78=6YI!OUSkbLZ;r#md}8-AoK z^3Bw2Y+%5N4eiWq5@9hX0YxnbI#&f$99PoK)Ldi_rd1r+I+Z0K_M# z5D%xG&M?2A`WcnWkMNwEQ+iXS;@&bv-SB{bcwSe;)zoSKY93)P>bg6r0CDp?#LWx( zXV_BNGOQMoqG&U_SC^V>vupFVfe~XS8K)eB5@QLKLgx=9!Em15=B?$k{Xrz8L3l@! zax#{Jk-VogJ=#ByD;ZBwdO4Y} zbH%JlN^9ENp$N32w7p=(a#_=`EA6pdCT9ynfaO%ywCLrpVsr|56I(TzGEFN>Du0Rb_&MrDEFT;Zl#fWMtwNeGas>1ZsT z^upK{G!~V7zn1!pZSfn+L#RcvNvyZXeqroqg|S~$@YU<)-6K0nef>l03nZ`%Ub(iC z0KowzBRRZbuQhFyY3Bt4)=BD~#OV6?!-L(shP?S-B>`!J%2YP8b;6A7e$X4R**BnT zY^<+qW?(2jOacwC!43j1%2os#fnNG@Rl0*b!2I#31~C(*It&3=;1L32Le1zFe7Ppv zVxwD7$!WCfbBkTmE%su!2$9sU019e7ZVuL)!cjAyR@tj(EwFNx_JznJa7h^owBAFy zS8pGo?-k}q1HC7|NXP=w>9y#ei1rp)c9ksqH)*6dn7>k#mz4A=Re4su|3h{3l16AJ obVXZ1+ky`@l^%Fs*L8Xl)ACPi`7?U+ysk5&)D_B|%ho3U2Z3BV9RL6T literal 0 HcmV?d00001 diff --git a/apps/docs/src/server-fns.ts b/apps/docs/src/server-fns.ts new file mode 100644 index 000000000..2daf3708e --- /dev/null +++ b/apps/docs/src/server-fns.ts @@ -0,0 +1,10 @@ +'use server'; +import { writeFile } from 'node:fs/promises'; +import { fileURLToPath } from 'node:url'; + +const __dirname = fileURLToPath(new URL('.', import.meta.url)); + +export async function saveFile(binary: any) { + const data = new Uint8Array(binary); + await writeFile(__dirname + 'pages' + '/binary', data); +}