mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-11 20:08:37 +00:00
feat: isolated plugin system (#2742)
This commit is contained in:
3
.github/actions/setup-node/action.yml
vendored
3
.github/actions/setup-node/action.yml
vendored
@@ -113,3 +113,6 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
if: inputs.playwright-install == 'true' && steps.playwright-cache.outputs.cache-hit != 'true'
|
if: inputs.playwright-install == 'true' && steps.playwright-cache.outputs.cache-hit != 'true'
|
||||||
run: yarn playwright install --with-deps
|
run: yarn playwright install --with-deps
|
||||||
|
- name: Build Infra
|
||||||
|
shell: bash
|
||||||
|
run: yarn build:infra
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { registerEvents } from './events';
|
|||||||
import { registerHandlers } from './handlers';
|
import { registerHandlers } from './handlers';
|
||||||
import { logger } from './logger';
|
import { logger } from './logger';
|
||||||
import { restoreOrCreateWindow } from './main-window';
|
import { restoreOrCreateWindow } from './main-window';
|
||||||
|
import { registerPlugin } from './plugin';
|
||||||
import { registerProtocol } from './protocol';
|
import { registerProtocol } from './protocol';
|
||||||
import { registerUpdater } from './updater';
|
import { registerUpdater } from './updater';
|
||||||
|
|
||||||
@@ -60,6 +61,7 @@ app
|
|||||||
.then(registerProtocol)
|
.then(registerProtocol)
|
||||||
.then(registerHandlers)
|
.then(registerHandlers)
|
||||||
.then(registerEvents)
|
.then(registerEvents)
|
||||||
|
.then(registerPlugin)
|
||||||
.then(restoreOrCreateWindow)
|
.then(restoreOrCreateWindow)
|
||||||
.then(createApplicationMenu)
|
.then(createApplicationMenu)
|
||||||
.then(registerUpdater)
|
.then(registerUpdater)
|
||||||
|
|||||||
57
apps/electron/layers/main/src/plugin.ts
Normal file
57
apps/electron/layers/main/src/plugin.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { join } from 'node:path';
|
||||||
|
import { Worker } from 'node:worker_threads';
|
||||||
|
|
||||||
|
import { AsyncCall } from 'async-call-rpc';
|
||||||
|
import { ipcMain } from 'electron';
|
||||||
|
|
||||||
|
import { ThreadWorkerChannel } from './utils';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
// fixme(himself65):
|
||||||
|
// remove this when bookmark block plugin is migrated to plugin-infra
|
||||||
|
// eslint-disable-next-line no-var
|
||||||
|
var asyncCall: Record<string, (...args: any) => PromiseLike<any>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function registerPlugin() {
|
||||||
|
const pluginWorkerPath = join(__dirname, './workers/plugin.worker.js');
|
||||||
|
const asyncCall = AsyncCall<
|
||||||
|
Record<string, (...args: any) => PromiseLike<any>>
|
||||||
|
>(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
channel: new ThreadWorkerChannel(new Worker(pluginWorkerPath)),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
globalThis.asyncCall = asyncCall;
|
||||||
|
await import('@toeverything/plugin-infra/manager').then(
|
||||||
|
({ rootStore, affinePluginsAtom }) => {
|
||||||
|
const bookmarkPluginPath = join(
|
||||||
|
process.env.PLUGIN_DIR ?? '../../plugins',
|
||||||
|
'./bookmark-block/index.mjs'
|
||||||
|
);
|
||||||
|
import(bookmarkPluginPath);
|
||||||
|
let dispose: () => void = () => {
|
||||||
|
// noop
|
||||||
|
};
|
||||||
|
rootStore.sub(affinePluginsAtom, () => {
|
||||||
|
dispose();
|
||||||
|
const plugins = rootStore.get(affinePluginsAtom);
|
||||||
|
Object.values(plugins).forEach(plugin => {
|
||||||
|
plugin.definition.commands.forEach(command => {
|
||||||
|
ipcMain.handle(command, (event, ...args) =>
|
||||||
|
asyncCall[command](...args)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
dispose = () => {
|
||||||
|
Object.values(plugins).forEach(plugin => {
|
||||||
|
plugin.definition.commands.forEach(command => {
|
||||||
|
ipcMain.removeHandler(command);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,17 +1,9 @@
|
|||||||
import { join } from 'node:path';
|
|
||||||
|
|
||||||
import { app, BrowserWindow, nativeTheme } from 'electron';
|
import { app, BrowserWindow, nativeTheme } from 'electron';
|
||||||
|
|
||||||
import type { NamespaceHandlers } from '../type';
|
import type { NamespaceHandlers } from '../type';
|
||||||
import { isMacOS } from '../utils';
|
import { isMacOS } from '../utils';
|
||||||
import { getGoogleOauthCode } from './google-auth';
|
import { getGoogleOauthCode } from './google-auth';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
||||||
const handlers = require(join(
|
|
||||||
process.env.PLUGIN_DIR ?? '../../plugins',
|
|
||||||
'./bookmark-block/server'
|
|
||||||
)).default as NamespaceHandlers;
|
|
||||||
|
|
||||||
export const uiHandlers = {
|
export const uiHandlers = {
|
||||||
handleThemeChange: async (_, theme: (typeof nativeTheme)['themeSource']) => {
|
handleThemeChange: async (_, theme: (typeof nativeTheme)['themeSource']) => {
|
||||||
nativeTheme.themeSource = theme;
|
nativeTheme.themeSource = theme;
|
||||||
@@ -47,5 +39,12 @@ export const uiHandlers = {
|
|||||||
getGoogleOauthCode: async () => {
|
getGoogleOauthCode: async () => {
|
||||||
return getGoogleOauthCode();
|
return getGoogleOauthCode();
|
||||||
},
|
},
|
||||||
...handlers,
|
/**
|
||||||
|
* @deprecated Remove this when bookmark block plugin is migrated to plugin-infra
|
||||||
|
*/
|
||||||
|
getBookmarkDataByLink: async (_, link: string) => {
|
||||||
|
return globalThis.asyncCall[
|
||||||
|
'com.blocksuite.bookmark-block.get-bookmark-data-by-link'
|
||||||
|
](link);
|
||||||
|
},
|
||||||
} satisfies NamespaceHandlers;
|
} satisfies NamespaceHandlers;
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
import type { MessagePort, Worker } from 'node:worker_threads';
|
||||||
|
|
||||||
|
import type { EventBasedChannel } from 'async-call-rpc';
|
||||||
|
|
||||||
export function getTime() {
|
export function getTime() {
|
||||||
return new Date().getTime();
|
return new Date().getTime();
|
||||||
}
|
}
|
||||||
@@ -9,3 +13,33 @@ export const isMacOS = () => {
|
|||||||
export const isWindows = () => {
|
export const isWindows = () => {
|
||||||
return process.platform === 'win32';
|
return process.platform === 'win32';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export class ThreadWorkerChannel implements EventBasedChannel {
|
||||||
|
constructor(private worker: Worker) {}
|
||||||
|
|
||||||
|
on(listener: (data: unknown) => void) {
|
||||||
|
this.worker.addListener('message', listener);
|
||||||
|
return () => {
|
||||||
|
this.worker.removeListener('message', listener);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
send(data: unknown) {
|
||||||
|
this.worker.postMessage(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MessagePortChannel implements EventBasedChannel {
|
||||||
|
constructor(private port: MessagePort) {}
|
||||||
|
|
||||||
|
on(listener: (data: unknown) => void) {
|
||||||
|
this.port.addListener('message', listener);
|
||||||
|
return () => {
|
||||||
|
this.port.removeListener('message', listener);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
send(data: unknown) {
|
||||||
|
this.port.postMessage(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
43
apps/electron/layers/main/src/workers/plugin.worker.ts
Normal file
43
apps/electron/layers/main/src/workers/plugin.worker.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { join } from 'node:path';
|
||||||
|
import { parentPort } from 'node:worker_threads';
|
||||||
|
|
||||||
|
import { AsyncCall } from 'async-call-rpc';
|
||||||
|
|
||||||
|
import { MessagePortChannel } from '../utils';
|
||||||
|
|
||||||
|
const commandProxy: Record<string, (...args: any[]) => Promise<any>> = {};
|
||||||
|
|
||||||
|
if (!parentPort) {
|
||||||
|
throw new Error('parentPort is undefined');
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncCall(commandProxy, {
|
||||||
|
channel: new MessagePortChannel(parentPort),
|
||||||
|
});
|
||||||
|
|
||||||
|
import('@toeverything/plugin-infra/manager').then(
|
||||||
|
({ rootStore, affinePluginsAtom }) => {
|
||||||
|
const bookmarkPluginPath = join(
|
||||||
|
process.env.PLUGIN_DIR ?? '../../../plugins',
|
||||||
|
'./bookmark-block/index.mjs'
|
||||||
|
);
|
||||||
|
|
||||||
|
import(bookmarkPluginPath);
|
||||||
|
rootStore.sub(affinePluginsAtom, () => {
|
||||||
|
const plugins = rootStore.get(affinePluginsAtom);
|
||||||
|
Object.values(plugins).forEach(plugin => {
|
||||||
|
if (plugin.serverAdapter) {
|
||||||
|
plugin.serverAdapter({
|
||||||
|
registerCommand: (command, fn) => {
|
||||||
|
console.log('register command', command);
|
||||||
|
commandProxy[command] = fn;
|
||||||
|
},
|
||||||
|
unregisterCommand: command => {
|
||||||
|
delete commandProxy[command];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -7,36 +7,21 @@ import { contextBridge, ipcRenderer } from 'electron';
|
|||||||
contextBridge.exposeInMainWorld('appInfo', affineApis.appInfo);
|
contextBridge.exposeInMainWorld('appInfo', affineApis.appInfo);
|
||||||
|
|
||||||
// Credit to microsoft/vscode
|
// Credit to microsoft/vscode
|
||||||
function validateIPC(channel: string) {
|
|
||||||
if (!channel || !channel.startsWith('affine:')) {
|
|
||||||
throw new Error(`Unsupported event IPC channel '${channel}'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const globals = {
|
const globals = {
|
||||||
ipcRenderer: {
|
ipcRenderer: {
|
||||||
send(channel: string, ...args: any[]) {
|
send(channel: string, ...args: any[]) {
|
||||||
if (validateIPC(channel)) {
|
ipcRenderer.send(channel, ...args);
|
||||||
ipcRenderer.send(channel, ...args);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
invoke(channel: string, ...args: any[]) {
|
invoke(channel: string, ...args: any[]) {
|
||||||
if (validateIPC(channel)) {
|
return ipcRenderer.invoke(channel, ...args);
|
||||||
return ipcRenderer.invoke(channel, ...args);
|
|
||||||
}
|
|
||||||
return void 0;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
on(
|
on(
|
||||||
channel: string,
|
channel: string,
|
||||||
listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void
|
listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void
|
||||||
) {
|
) {
|
||||||
if (validateIPC(channel)) {
|
ipcRenderer.on(channel, listener);
|
||||||
ipcRenderer.on(channel, listener);
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -44,9 +29,7 @@ import { contextBridge, ipcRenderer } from 'electron';
|
|||||||
channel: string,
|
channel: string,
|
||||||
listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void
|
listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void
|
||||||
) {
|
) {
|
||||||
if (validateIPC(channel)) {
|
ipcRenderer.once(channel, listener);
|
||||||
ipcRenderer.once(channel, listener);
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -54,9 +37,7 @@ import { contextBridge, ipcRenderer } from 'electron';
|
|||||||
channel: string,
|
channel: string,
|
||||||
listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void
|
listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void
|
||||||
) {
|
) {
|
||||||
if (validateIPC(channel)) {
|
ipcRenderer.removeListener(channel, listener);
|
||||||
ipcRenderer.removeListener(channel, listener);
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -65,6 +46,6 @@ import { contextBridge, ipcRenderer } from 'electron';
|
|||||||
try {
|
try {
|
||||||
contextBridge.exposeInMainWorld('affine', globals);
|
contextBridge.exposeInMainWorld('affine', globals);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error('Failed to expose affine APIs to window object!', error);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -55,6 +55,8 @@
|
|||||||
"zx": "^7.2.2"
|
"zx": "^7.2.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@toeverything/plugin-infra": "workspace:*",
|
||||||
|
"async-call-rpc": "^6.3.1",
|
||||||
"cheerio": "^1.0.0-rc.12",
|
"cheerio": "^1.0.0-rc.12",
|
||||||
"electron-updater": "^5.3.0",
|
"electron-updater": "^5.3.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
#!/usr/bin/env zx
|
#!/usr/bin/env zx
|
||||||
import 'zx/globals';
|
import 'zx/globals';
|
||||||
|
|
||||||
|
import { resolve } from 'node:path';
|
||||||
|
|
||||||
|
import { spawnSync } from 'child_process';
|
||||||
import * as esbuild from 'esbuild';
|
import * as esbuild from 'esbuild';
|
||||||
|
|
||||||
import { config } from './common.mjs';
|
import { config, rootDir } from './common.mjs';
|
||||||
|
|
||||||
const NODE_ENV =
|
const NODE_ENV =
|
||||||
process.env.NODE_ENV === 'development' ? 'development' : 'production';
|
process.env.NODE_ENV === 'development' ? 'development' : 'production';
|
||||||
@@ -16,6 +19,11 @@ if (process.platform === 'win32') {
|
|||||||
async function buildLayers() {
|
async function buildLayers() {
|
||||||
const common = config();
|
const common = config();
|
||||||
await esbuild.build(common.preload);
|
await esbuild.build(common.preload);
|
||||||
|
console.log('Build plugin infra');
|
||||||
|
spawnSync('yarn', ['build'], {
|
||||||
|
stdio: 'inherit',
|
||||||
|
cwd: resolve(rootDir, './packages/plugin-infra'),
|
||||||
|
});
|
||||||
|
|
||||||
console.log('Build plugins');
|
console.log('Build plugins');
|
||||||
await import('./plugins/build-plugins.mjs');
|
await import('./plugins/build-plugins.mjs');
|
||||||
|
|||||||
@@ -41,12 +41,20 @@ export const config = () => {
|
|||||||
electronDir,
|
electronDir,
|
||||||
'./layers/main/src/workers/merge-update.worker.ts'
|
'./layers/main/src/workers/merge-update.worker.ts'
|
||||||
),
|
),
|
||||||
|
resolve(electronDir, './layers/main/src/workers/plugin.worker.ts'),
|
||||||
],
|
],
|
||||||
outdir: resolve(electronDir, './dist/layers/main'),
|
outdir: resolve(electronDir, './dist/layers/main'),
|
||||||
bundle: true,
|
bundle: true,
|
||||||
target: `node${NODE_MAJOR_VERSION}`,
|
target: `node${NODE_MAJOR_VERSION}`,
|
||||||
platform: 'node',
|
platform: 'node',
|
||||||
external: ['electron', 'yjs', 'electron-updater', '@toeverything/infra'],
|
external: [
|
||||||
|
'electron',
|
||||||
|
'yjs',
|
||||||
|
'better-sqlite3',
|
||||||
|
'electron-updater',
|
||||||
|
'@toeverything/plugin-infra',
|
||||||
|
'async-call-rpc',
|
||||||
|
],
|
||||||
define: define,
|
define: define,
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
loader: {
|
loader: {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
/* eslint-disable no-async-promise-executor */
|
/* eslint-disable no-async-promise-executor */
|
||||||
import { spawn } from 'node:child_process';
|
import { spawn } from 'node:child_process';
|
||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
import path from 'node:path';
|
import path, { resolve } from 'node:path';
|
||||||
|
|
||||||
import electronPath from 'electron';
|
import electronPath from 'electron';
|
||||||
import * as esbuild from 'esbuild';
|
import * as esbuild from 'esbuild';
|
||||||
|
|
||||||
import { config, electronDir } from './common.mjs';
|
import { config, electronDir, rootDir } from './common.mjs';
|
||||||
|
|
||||||
// this means we don't spawn electron windows, mainly for testing
|
// this means we don't spawn electron windows, mainly for testing
|
||||||
const watchMode = process.argv.includes('--watch');
|
const watchMode = process.argv.includes('--watch');
|
||||||
@@ -69,6 +69,10 @@ function spawnOrReloadElectron() {
|
|||||||
const common = config();
|
const common = config();
|
||||||
|
|
||||||
async function watchPlugins() {
|
async function watchPlugins() {
|
||||||
|
spawn('yarn', ['dev'], {
|
||||||
|
stdio: 'inherit',
|
||||||
|
cwd: resolve(rootDir, './packages/plugin-infra'),
|
||||||
|
});
|
||||||
await import('./plugins/dev-plugins.mjs');
|
await import('./plugins/dev-plugins.mjs');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,5 +5,16 @@ import { definePluginServerConfig } from './utils.mjs';
|
|||||||
|
|
||||||
await build({
|
await build({
|
||||||
...definePluginServerConfig('bookmark-block'),
|
...definePluginServerConfig('bookmark-block'),
|
||||||
external: ['cheerio', 'electron', 'puppeteer', 'foxact'],
|
external: [
|
||||||
|
// server.ts
|
||||||
|
'link-preview-js',
|
||||||
|
// ui.ts
|
||||||
|
'@toeverything/plugin-infra',
|
||||||
|
'@affine/component',
|
||||||
|
'@blocksuite/store',
|
||||||
|
'@blocksuite/blocks',
|
||||||
|
'react',
|
||||||
|
'react-dom',
|
||||||
|
'foxact',
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,18 @@ import { definePluginServerConfig } from './utils.mjs';
|
|||||||
|
|
||||||
const plugin = await context({
|
const plugin = await context({
|
||||||
...definePluginServerConfig('bookmark-block'),
|
...definePluginServerConfig('bookmark-block'),
|
||||||
external: ['cheerio', 'electron', 'puppeteer', 'foxact'],
|
external: [
|
||||||
|
// server.ts
|
||||||
|
'link-preview-js',
|
||||||
|
// ui.ts
|
||||||
|
'@toeverything/plugin-infra',
|
||||||
|
'@affine/component',
|
||||||
|
'@blocksuite/store',
|
||||||
|
'@blocksuite/blocks',
|
||||||
|
'react',
|
||||||
|
'react-dom',
|
||||||
|
'foxact',
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
await plugin.watch();
|
await plugin.watch();
|
||||||
|
|||||||
@@ -18,12 +18,17 @@ export const pluginDir = resolve(rootDir, 'plugins');
|
|||||||
*/
|
*/
|
||||||
export function definePluginServerConfig(pluginDirName) {
|
export function definePluginServerConfig(pluginDirName) {
|
||||||
const pluginRootDir = resolve(pluginDir, pluginDirName);
|
const pluginRootDir = resolve(pluginDir, pluginDirName);
|
||||||
const serverEntryFile = resolve(pluginRootDir, 'src/server.ts');
|
const mainEntryFile = resolve(pluginRootDir, 'src/index.ts');
|
||||||
const serverOutputDir = resolve(electronOutputDir, pluginDirName);
|
const serverOutputDir = resolve(electronOutputDir, pluginDirName);
|
||||||
return {
|
return {
|
||||||
entryPoints: [serverEntryFile],
|
entryPoints: [mainEntryFile],
|
||||||
platform: 'node',
|
platform: 'neutral',
|
||||||
|
format: 'esm',
|
||||||
|
outExtension: {
|
||||||
|
'.js': '.mjs',
|
||||||
|
},
|
||||||
outdir: serverOutputDir,
|
outdir: serverOutputDir,
|
||||||
bundle: true,
|
bundle: true,
|
||||||
|
splitting: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,7 @@
|
|||||||
"exclude": ["node_modules", "out", "dist"],
|
"exclude": ["node_modules", "out", "dist"],
|
||||||
"references": [
|
"references": [
|
||||||
{
|
{
|
||||||
"path": "./tsconfig.node.json"
|
"path": "../../packages/plugin-infra"
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "./tests/tsconfig.json"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "../../packages/native"
|
"path": "../../packages/native"
|
||||||
@@ -28,6 +25,14 @@
|
|||||||
{
|
{
|
||||||
"path": "../../packages/infra"
|
"path": "../../packages/infra"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Tests
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tests/tsconfig.json"
|
||||||
|
},
|
||||||
{ "path": "../../tests/kit" }
|
{ "path": "../../tests/kit" }
|
||||||
],
|
],
|
||||||
"ts-node": {
|
"ts-node": {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"extends": "./tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { Unreachable } from '@affine/env/constant';
|
|||||||
import type { AffineLegacyCloudWorkspace } from '@affine/env/workspace';
|
import type { AffineLegacyCloudWorkspace } from '@affine/env/workspace';
|
||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
import { affineApis } from '@affine/workspace/affine/shared';
|
import { affineApis } from '@affine/workspace/affine/shared';
|
||||||
import { rootStore } from '@affine/workspace/atom';
|
|
||||||
import { createAffineProviders } from '@affine/workspace/providers';
|
import { createAffineProviders } from '@affine/workspace/providers';
|
||||||
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
|
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
|
||||||
import { assertExists } from '@blocksuite/store';
|
import { assertExists } from '@blocksuite/store';
|
||||||
|
import { rootStore } from '@toeverything/plugin-infra/manager';
|
||||||
|
|
||||||
import { workspacesAtom } from '../../atoms';
|
import { workspacesAtom } from '../../atoms';
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
SignMethod,
|
SignMethod,
|
||||||
} from '@affine/workspace/affine/login';
|
} from '@affine/workspace/affine/login';
|
||||||
import { affineApis, affineAuth } from '@affine/workspace/affine/shared';
|
import { affineApis, affineAuth } from '@affine/workspace/affine/shared';
|
||||||
import { rootStore, rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
|
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
|
||||||
import {
|
import {
|
||||||
createAffineProviders,
|
createAffineProviders,
|
||||||
createIndexedDBBackgroundProvider,
|
createIndexedDBBackgroundProvider,
|
||||||
@@ -31,6 +31,7 @@ import {
|
|||||||
cleanupWorkspace,
|
cleanupWorkspace,
|
||||||
createEmptyBlockSuiteWorkspace,
|
createEmptyBlockSuiteWorkspace,
|
||||||
} from '@affine/workspace/utils';
|
} from '@affine/workspace/utils';
|
||||||
|
import { rootStore } from '@toeverything/plugin-infra/manager';
|
||||||
import { createJSONStorage } from 'jotai/utils';
|
import { createJSONStorage } from 'jotai/utils';
|
||||||
import type { PropsWithChildren, ReactElement } from 'react';
|
import type { PropsWithChildren, ReactElement } from 'react';
|
||||||
import { Suspense, useEffect } from 'react';
|
import { Suspense, useEffect } from 'react';
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { AffineLegacyCloudWorkspace } from '@affine/env/workspace';
|
import type { AffineLegacyCloudWorkspace } from '@affine/env/workspace';
|
||||||
import { affineApis } from '@affine/workspace/affine/shared';
|
import { affineApis } from '@affine/workspace/affine/shared';
|
||||||
import { rootStore, rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
|
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
|
||||||
|
import { rootStore } from '@toeverything/plugin-infra/manager';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import { createAffineGlobalChannel } from '@affine/workspace/affine/sync';
|
|||||||
import {
|
import {
|
||||||
rootCurrentPageIdAtom,
|
rootCurrentPageIdAtom,
|
||||||
rootCurrentWorkspaceIdAtom,
|
rootCurrentWorkspaceIdAtom,
|
||||||
rootStore,
|
|
||||||
rootWorkspacesMetadataAtom,
|
rootWorkspacesMetadataAtom,
|
||||||
} from '@affine/workspace/atom';
|
} from '@affine/workspace/atom';
|
||||||
import { assertEquals, assertExists, nanoid } from '@blocksuite/store';
|
import { assertEquals, assertExists, nanoid } from '@blocksuite/store';
|
||||||
@@ -35,6 +34,7 @@ import {
|
|||||||
useSensors,
|
useSensors,
|
||||||
} from '@dnd-kit/core';
|
} from '@dnd-kit/core';
|
||||||
import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper';
|
import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper';
|
||||||
|
import { rootStore } from '@toeverything/plugin-infra/manager';
|
||||||
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
|
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import '@affine/component/theme/theme.css';
|
|||||||
// bootstrap code before everything
|
// bootstrap code before everything
|
||||||
import '../bootstrap';
|
import '../bootstrap';
|
||||||
|
|
||||||
|
import { AffineContext } from '@affine/component/context';
|
||||||
import { WorkspaceFallback } from '@affine/component/workspace';
|
import { WorkspaceFallback } from '@affine/component/workspace';
|
||||||
import { config } from '@affine/env';
|
import { config } from '@affine/env';
|
||||||
import { createI18n, I18nextProvider } from '@affine/i18n';
|
import { createI18n, I18nextProvider } from '@affine/i18n';
|
||||||
import type { EmotionCache } from '@emotion/cache';
|
import type { EmotionCache } from '@emotion/cache';
|
||||||
import { CacheProvider } from '@emotion/react';
|
import { CacheProvider } from '@emotion/react';
|
||||||
import { AffinePluginContext } from '@toeverything/plugin-infra/react';
|
|
||||||
import type { AppProps } from 'next/app';
|
import type { AppProps } from 'next/app';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
@@ -64,7 +64,7 @@ const App = function App({
|
|||||||
<MessageCenter />
|
<MessageCenter />
|
||||||
<AffineErrorBoundary router={useRouter()}>
|
<AffineErrorBoundary router={useRouter()}>
|
||||||
<Suspense fallback={<WorkspaceFallback key="RootPageLoading" />}>
|
<Suspense fallback={<WorkspaceFallback key="RootPageLoading" />}>
|
||||||
<AffinePluginContext>
|
<AffineContext>
|
||||||
<Head>
|
<Head>
|
||||||
<title>AFFiNE</title>
|
<title>AFFiNE</title>
|
||||||
<meta
|
<meta
|
||||||
@@ -75,7 +75,7 @@ const App = function App({
|
|||||||
<DebugProvider>
|
<DebugProvider>
|
||||||
{getLayout(<Component {...pageProps} />)}
|
{getLayout(<Component {...pageProps} />)}
|
||||||
</DebugProvider>
|
</DebugProvider>
|
||||||
</AffinePluginContext>
|
</AffineContext>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</AffineErrorBoundary>
|
</AffineErrorBoundary>
|
||||||
</I18nextProvider>
|
</I18nextProvider>
|
||||||
|
|||||||
@@ -17,8 +17,9 @@
|
|||||||
"dev:local": "PORT=8080 API_SERVER_PROFILE=local yarn workspace @affine/web dev",
|
"dev:local": "PORT=8080 API_SERVER_PROFILE=local yarn workspace @affine/web dev",
|
||||||
"dev:electron": "yarn workspace @affine/electron dev:app",
|
"dev:electron": "yarn workspace @affine/electron dev:app",
|
||||||
"dev:plugins": "./apps/electron/scripts/plugins/dev-plugins.mjs",
|
"dev:plugins": "./apps/electron/scripts/plugins/dev-plugins.mjs",
|
||||||
"build": "yarn workspace @affine/web build",
|
"build": "yarn build:infra && yarn workspace @affine/web build",
|
||||||
"build:storybook": "yarn workspace @affine/storybook build-storybook",
|
"build:infra": "yarn workspace @toeverything/plugin-infra build && yarn workspace @toeverything/infra build",
|
||||||
|
"build:storybook": "yarn build:infra && yarn workspace @affine/storybook build-storybook",
|
||||||
"build:plugins": "./apps/electron/scripts/plugins/build-plugins.mjs",
|
"build:plugins": "./apps/electron/scripts/plugins/build-plugins.mjs",
|
||||||
"bump:nightly": "./scripts/bump-blocksuite.sh",
|
"bump:nightly": "./scripts/bump-blocksuite.sh",
|
||||||
"circular": "madge --circular --ts-config ./tsconfig.json ./apps/web/src/pages/**/*.tsx",
|
"circular": "madge --circular --ts-config ./tsconfig.json ./apps/web/src/pages/**/*.tsx",
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
"@radix-ui/react-radio-group": "^1.1.3",
|
"@radix-ui/react-radio-group": "^1.1.3",
|
||||||
"@radix-ui/react-toast": "^1.1.4",
|
"@radix-ui/react-toast": "^1.1.4",
|
||||||
"@toeverything/hooks": "workspace:*",
|
"@toeverything/hooks": "workspace:*",
|
||||||
|
"@toeverything/plugin-infra": "workspace:*",
|
||||||
"@toeverything/theme": "^0.6.1",
|
"@toeverything/theme": "^0.6.1",
|
||||||
"@vanilla-extract/dynamic": "^2.0.3",
|
"@vanilla-extract/dynamic": "^2.0.3",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { ProviderComposer } from '@affine/component/provider-composer';
|
import { ProviderComposer } from '@affine/component/provider-composer';
|
||||||
import { ThemeProvider } from '@affine/component/theme-provider';
|
import { ThemeProvider } from '@affine/component/theme-provider';
|
||||||
import { rootStore } from '@affine/workspace/atom';
|
import { rootStore } from '@toeverything/plugin-infra/manager';
|
||||||
import { Provider } from 'jotai';
|
import { Provider } from 'jotai';
|
||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
export function AffinePluginContext(props: PropsWithChildren) {
|
export function AffineContext(props: PropsWithChildren) {
|
||||||
return (
|
return (
|
||||||
<ProviderComposer
|
<ProviderComposer
|
||||||
contexts={useMemo(
|
contexts={useMemo(
|
||||||
@@ -20,10 +20,10 @@
|
|||||||
{
|
{
|
||||||
"path": "../hooks"
|
"path": "../hooks"
|
||||||
},
|
},
|
||||||
{ "path": "../workspace" },
|
|
||||||
{
|
{
|
||||||
"path": "../../apps/electron"
|
"path": "../plugin-infra"
|
||||||
},
|
},
|
||||||
|
{ "path": "../workspace" },
|
||||||
{ "path": "../../tests/fixtures" }
|
{ "path": "../../tests/fixtures" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
// to prevent the `@affine/components` contains circular references with `@affine/workspace`
|
|
||||||
// the include files should be excluded in `./tsconfig.json`
|
|
||||||
{
|
|
||||||
"extends": "../../tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"composite": true,
|
|
||||||
"noEmit": false,
|
|
||||||
"outDir": "lib"
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"./src/components/page-list/filter/shared-types.tsx",
|
|
||||||
"./src/components/page-list/filter/logical/custom-type.ts",
|
|
||||||
"./src/components/page-list/filter/logical/matcher.ts",
|
|
||||||
"./src/components/page-list/filter/logical/typesystem.ts"
|
|
||||||
],
|
|
||||||
"references": [{ "path": "../env" }],
|
|
||||||
"exclude": ["lib"]
|
|
||||||
}
|
|
||||||
@@ -1,18 +1,23 @@
|
|||||||
{
|
{
|
||||||
"name": "@toeverything/plugin-infra",
|
"name": "@toeverything/plugin-infra",
|
||||||
"private": true,
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vite build"
|
"build": "vite build",
|
||||||
|
"dev": "vite build --watch"
|
||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
"./manager": "./src/manager.ts",
|
"./manager": {
|
||||||
"./type": "./src/type.ts",
|
"type": "./dist/manager.d.ts",
|
||||||
"./react": "./src/react/index.ts"
|
"import": "./dist/manager.js",
|
||||||
|
"require": "./dist/manager.cjs"
|
||||||
|
},
|
||||||
|
"./type": {
|
||||||
|
"type": "./dist/type.d.ts",
|
||||||
|
"import": "./dist/type.js",
|
||||||
|
"require": "./dist/type.cjs"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@affine/component": "workspace:*",
|
|
||||||
"@affine/env": "workspace:*",
|
|
||||||
"@affine/workspace": "workspace:*",
|
|
||||||
"@blocksuite/blocks": "0.0.0-20230607055421-9b20fcaf-nightly",
|
"@blocksuite/blocks": "0.0.0-20230607055421-9b20fcaf-nightly",
|
||||||
"@blocksuite/editor": "0.0.0-20230607055421-9b20fcaf-nightly",
|
"@blocksuite/editor": "0.0.0-20230607055421-9b20fcaf-nightly",
|
||||||
"@blocksuite/global": "0.0.0-20230607055421-9b20fcaf-nightly",
|
"@blocksuite/global": "0.0.0-20230607055421-9b20fcaf-nightly",
|
||||||
@@ -20,7 +25,9 @@
|
|||||||
"@blocksuite/store": "0.0.0-20230607055421-9b20fcaf-nightly"
|
"@blocksuite/store": "0.0.0-20230607055421-9b20fcaf-nightly"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jotai": "^2.1.1"
|
"jotai": "^2.1.1",
|
||||||
|
"vite": "^4.3.9",
|
||||||
|
"vite-plugin-dts": "^2.3.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@blocksuite/blocks": "*",
|
"@blocksuite/blocks": "*",
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
import { DebugLogger } from '@affine/debug';
|
import { atom, createStore } from 'jotai/vanilla';
|
||||||
import { rootStore } from '@affine/workspace/atom';
|
|
||||||
import { atom } from 'jotai';
|
|
||||||
|
|
||||||
import type { AffinePlugin, Definition } from './type';
|
import type { AffinePlugin, Definition, ServerAdapter } from './type';
|
||||||
import type { Loader, PluginUIAdapter } from './type';
|
import type { Loader, PluginUIAdapter } from './type';
|
||||||
import type { PluginBlockSuiteAdapter } from './type';
|
import type { PluginBlockSuiteAdapter } from './type';
|
||||||
|
|
||||||
|
const isServer = typeof window === 'undefined';
|
||||||
|
const isClient = typeof window !== 'undefined';
|
||||||
|
|
||||||
|
// global store
|
||||||
|
export const rootStore = createStore();
|
||||||
|
|
||||||
// todo: for now every plugin is enabled by default
|
// todo: for now every plugin is enabled by default
|
||||||
export const affinePluginsAtom = atom<Record<string, AffinePlugin<string>>>({});
|
export const affinePluginsAtom = atom<Record<string, AffinePlugin<string>>>({});
|
||||||
|
|
||||||
const pluginLogger = new DebugLogger('affine:plugin');
|
|
||||||
|
|
||||||
export function definePlugin<ID extends string>(
|
export function definePlugin<ID extends string>(
|
||||||
definition: Definition<ID>,
|
definition: Definition<ID>,
|
||||||
uiAdapterLoader?: Loader<Partial<PluginUIAdapter>>,
|
uiAdapterLoader?: Loader<Partial<PluginUIAdapter>>,
|
||||||
blockSuiteAdapter?: Loader<Partial<PluginBlockSuiteAdapter>>
|
blockSuiteAdapter?: Loader<Partial<PluginBlockSuiteAdapter>>,
|
||||||
|
serverAdapter?: Loader<ServerAdapter>
|
||||||
) {
|
) {
|
||||||
const basePlugin = {
|
const basePlugin = {
|
||||||
definition,
|
definition,
|
||||||
@@ -27,57 +30,70 @@ export function definePlugin<ID extends string>(
|
|||||||
[definition.id]: basePlugin,
|
[definition.id]: basePlugin,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (blockSuiteAdapter) {
|
if (isServer) {
|
||||||
const updateAdapter = (adapter: Partial<PluginBlockSuiteAdapter>) => {
|
if (serverAdapter) {
|
||||||
rootStore.set(affinePluginsAtom, plugins => ({
|
serverAdapter.load().then(({ default: adapter }) => {
|
||||||
...plugins,
|
rootStore.set(affinePluginsAtom, plugins => ({
|
||||||
[definition.id]: {
|
...plugins,
|
||||||
...basePlugin,
|
[definition.id]: {
|
||||||
blockSuiteAdapter: adapter,
|
...basePlugin,
|
||||||
},
|
serverAdapter: adapter,
|
||||||
}));
|
},
|
||||||
};
|
}));
|
||||||
|
|
||||||
blockSuiteAdapter
|
|
||||||
.load()
|
|
||||||
.then(({ default: adapter }) => updateAdapter(adapter))
|
|
||||||
.catch(err => {
|
|
||||||
pluginLogger.error('[definePlugin] blockSuiteAdapter error', err);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (import.meta.webpackHot) {
|
|
||||||
blockSuiteAdapter.hotModuleReload(async _ => {
|
|
||||||
const adapter = (await _).default;
|
|
||||||
updateAdapter(adapter);
|
|
||||||
pluginLogger.info('[HMR] Plugin', definition.id, 'hot reloaded.');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
} else if (isClient) {
|
||||||
|
if (blockSuiteAdapter) {
|
||||||
|
const updateAdapter = (adapter: Partial<PluginBlockSuiteAdapter>) => {
|
||||||
|
rootStore.set(affinePluginsAtom, plugins => ({
|
||||||
|
...plugins,
|
||||||
|
[definition.id]: {
|
||||||
|
...basePlugin,
|
||||||
|
blockSuiteAdapter: adapter,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
if (uiAdapterLoader) {
|
blockSuiteAdapter
|
||||||
const updateAdapter = (adapter: Partial<PluginUIAdapter>) => {
|
.load()
|
||||||
rootStore.set(affinePluginsAtom, plugins => ({
|
.then(({ default: adapter }) => updateAdapter(adapter))
|
||||||
...plugins,
|
.catch(err => {
|
||||||
[definition.id]: {
|
console.error('[definePlugin] blockSuiteAdapter error', err);
|
||||||
...basePlugin,
|
});
|
||||||
uiAdapter: adapter,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
uiAdapterLoader
|
if (import.meta.webpackHot) {
|
||||||
.load()
|
blockSuiteAdapter.hotModuleReload(async _ => {
|
||||||
.then(({ default: adapter }) => updateAdapter(adapter))
|
const adapter = (await _).default;
|
||||||
.catch(err => {
|
updateAdapter(adapter);
|
||||||
pluginLogger.error('[definePlugin] blockSuiteAdapter error', err);
|
console.info('[HMR] Plugin', definition.id, 'hot reloaded.');
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (uiAdapterLoader) {
|
||||||
|
const updateAdapter = (adapter: Partial<PluginUIAdapter>) => {
|
||||||
|
rootStore.set(affinePluginsAtom, plugins => ({
|
||||||
|
...plugins,
|
||||||
|
[definition.id]: {
|
||||||
|
...basePlugin,
|
||||||
|
uiAdapter: adapter,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
if (import.meta.webpackHot) {
|
uiAdapterLoader
|
||||||
uiAdapterLoader.hotModuleReload(async _ => {
|
.load()
|
||||||
const adapter = (await _).default;
|
.then(({ default: adapter }) => updateAdapter(adapter))
|
||||||
updateAdapter(adapter);
|
.catch(err => {
|
||||||
pluginLogger.info('[HMR] Plugin', definition.id, 'hot reloaded.');
|
console.error('[definePlugin] blockSuiteAdapter error', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (import.meta.webpackHot) {
|
||||||
|
uiAdapterLoader.hotModuleReload(async _ => {
|
||||||
|
const adapter = (await _).default;
|
||||||
|
updateAdapter(adapter);
|
||||||
|
console.info('[HMR] Plugin', definition.id, 'hot reloaded.');
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export * from './context';
|
|
||||||
@@ -141,6 +141,11 @@ export type Definition<ID extends string> = {
|
|||||||
* @example ReleaseStage.PROD
|
* @example ReleaseStage.PROD
|
||||||
*/
|
*/
|
||||||
stage: ReleaseStage;
|
stage: ReleaseStage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registered commands
|
||||||
|
*/
|
||||||
|
commands: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
// todo(himself65): support Vue.js
|
// todo(himself65): support Vue.js
|
||||||
@@ -171,12 +176,16 @@ export type PluginBlockSuiteAdapter = {
|
|||||||
uiDecorator: (root: EditorContainer) => Cleanup;
|
uiDecorator: (root: EditorContainer) => Cleanup;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PluginAdapterCreator = (
|
type AFFiNEServer = {
|
||||||
context: AffinePluginContext
|
registerCommand: (command: string, fn: (...args: any[]) => any) => void;
|
||||||
) => PluginUIAdapter;
|
unregisterCommand: (command: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ServerAdapter = (affine: AFFiNEServer) => () => void;
|
||||||
|
|
||||||
export type AffinePlugin<ID extends string> = {
|
export type AffinePlugin<ID extends string> = {
|
||||||
definition: Definition<ID>;
|
definition: Definition<ID>;
|
||||||
uiAdapter: Partial<PluginUIAdapter>;
|
uiAdapter: Partial<PluginUIAdapter>;
|
||||||
blockSuiteAdapter: Partial<PluginBlockSuiteAdapter>;
|
blockSuiteAdapter: Partial<PluginBlockSuiteAdapter>;
|
||||||
|
serverAdapter?: ServerAdapter;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,10 +8,7 @@
|
|||||||
},
|
},
|
||||||
"references": [
|
"references": [
|
||||||
{
|
{
|
||||||
"path": "../component"
|
"path": "./tsconfig.node.json"
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "../workspace"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
10
packages/plugin-infra/tsconfig.node.json
Normal file
10
packages/plugin-infra/tsconfig.node.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"outDir": "lib"
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
||||||
35
packages/plugin-infra/vite.config.ts
Normal file
35
packages/plugin-infra/vite.config.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { resolve } from 'node:path';
|
||||||
|
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import dts from 'vite-plugin-dts';
|
||||||
|
|
||||||
|
const root = fileURLToPath(new URL('.', import.meta.url));
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
build: {
|
||||||
|
minify: false,
|
||||||
|
lib: {
|
||||||
|
entry: {
|
||||||
|
type: resolve(root, 'src/type.ts'),
|
||||||
|
manager: resolve(root, 'src/manager.ts'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
external: [
|
||||||
|
'jotai',
|
||||||
|
'jotai/vanilla',
|
||||||
|
'@blocksuite/blocks',
|
||||||
|
'@blocksuite/store',
|
||||||
|
'@blocksuite/global',
|
||||||
|
'@blocksuite/editor',
|
||||||
|
'@blocksuite/lit',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
dts({
|
||||||
|
insertTypesEntry: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
"@affine/debug": "workspace:*",
|
"@affine/debug": "workspace:*",
|
||||||
"@affine/env": "workspace:*",
|
"@affine/env": "workspace:*",
|
||||||
"@toeverything/hooks": "workspace:*",
|
"@toeverything/hooks": "workspace:*",
|
||||||
|
"@toeverything/plugin-infra": "workspace:*",
|
||||||
"@toeverything/y-indexeddb": "workspace:*",
|
"@toeverything/y-indexeddb": "workspace:*",
|
||||||
"firebase": "^9.22.1",
|
"firebase": "^9.22.1",
|
||||||
"jotai": "^2.1.1",
|
"jotai": "^2.1.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { prefixUrl } from '@affine/env';
|
import { prefixUrl } from '@affine/env';
|
||||||
|
import { rootStore } from '@toeverything/plugin-infra/manager';
|
||||||
|
|
||||||
import { rootStore } from '../atom';
|
|
||||||
import { createUserApis, createWorkspaceApis } from './api/index';
|
import { createUserApis, createWorkspaceApis } from './api/index';
|
||||||
import { currentAffineUserAtom } from './atom';
|
import { currentAffineUserAtom } from './atom';
|
||||||
import type { LoginResponse } from './login';
|
import type { LoginResponse } from './login';
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ import {
|
|||||||
} from '@affine/env/workspace/legacy-cloud';
|
} from '@affine/env/workspace/legacy-cloud';
|
||||||
import { assertExists } from '@blocksuite/global/utils';
|
import { assertExists } from '@blocksuite/global/utils';
|
||||||
import type { Disposable } from '@blocksuite/store';
|
import type { Disposable } from '@blocksuite/store';
|
||||||
|
import { rootStore } from '@toeverything/plugin-infra/manager';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { WebsocketClient } from '../affine/channel';
|
import { WebsocketClient } from '../affine/channel';
|
||||||
import { storageChangeSlot } from '../affine/login';
|
import { storageChangeSlot } from '../affine/login';
|
||||||
import { rootStore, rootWorkspacesMetadataAtom } from '../atom';
|
import { rootWorkspacesMetadataAtom } from '../atom';
|
||||||
|
|
||||||
const logger = new DebugLogger('affine-sync');
|
const logger = new DebugLogger('affine-sync');
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { WorkspaceFlavour } from '@affine/env/workspace';
|
import type { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
import type { EditorContainer } from '@blocksuite/editor';
|
import type { EditorContainer } from '@blocksuite/editor';
|
||||||
import { atom, createStore } from 'jotai';
|
import { atom } from 'jotai';
|
||||||
import { atomWithStorage, createJSONStorage } from 'jotai/utils';
|
import { atomWithStorage } from 'jotai/utils';
|
||||||
import Router from 'next/router';
|
import Router from 'next/router';
|
||||||
|
|
||||||
export type RootWorkspaceMetadata = {
|
export type RootWorkspaceMetadata = {
|
||||||
@@ -77,13 +77,3 @@ export const rootCurrentEditorAtom = atom<Readonly<EditorContainer> | null>(
|
|||||||
null
|
null
|
||||||
);
|
);
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
const getStorage = () => createJSONStorage(() => localStorage);
|
|
||||||
|
|
||||||
export const getStoredWorkspaceMeta = () => {
|
|
||||||
const storage = getStorage();
|
|
||||||
return storage.getItem('jotai-workspaces', []) as RootWorkspaceMetadata[];
|
|
||||||
};
|
|
||||||
|
|
||||||
// global store
|
|
||||||
export const rootStore = createStore();
|
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import { WorkspaceFlavour } from '@affine/env/workspace';
|
|||||||
import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
|
import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
|
||||||
import type { Generator, StoreOptions } from '@blocksuite/store';
|
import type { Generator, StoreOptions } from '@blocksuite/store';
|
||||||
import { createIndexeddbStorage, Workspace } from '@blocksuite/store';
|
import { createIndexeddbStorage, Workspace } from '@blocksuite/store';
|
||||||
|
import { rootStore } from '@toeverything/plugin-infra/manager';
|
||||||
|
|
||||||
import type { createWorkspaceApis } from './affine/api';
|
import type { createWorkspaceApis } from './affine/api';
|
||||||
import { rootStore, rootWorkspacesMetadataAtom } from './atom';
|
import { rootWorkspacesMetadataAtom } from './atom';
|
||||||
import { createAffineBlobStorage } from './blob';
|
import { createAffineBlobStorage } from './blob';
|
||||||
import { createSQLiteStorage } from './blob/sqlite-blob-storage';
|
import { createSQLiteStorage } from './blob/sqlite-blob-storage';
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
{ "path": "../y-indexeddb" },
|
{ "path": "../y-indexeddb" },
|
||||||
{ "path": "../env" },
|
{ "path": "../env" },
|
||||||
{ "path": "../debug" },
|
{ "path": "../debug" },
|
||||||
{ "path": "../hooks" }
|
{ "path": "../hooks" },
|
||||||
|
{ "path": "../plugin-infra" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"./server": "./src/server.ts"
|
"./server": "./src/server.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@affine/component": "workspace:*",
|
||||||
"@toeverything/plugin-infra": "workspace:*",
|
"@toeverything/plugin-infra": "workspace:*",
|
||||||
"foxact": "^0.2.7",
|
"foxact": "^0.2.7",
|
||||||
"link-preview-js": "^3.0.4"
|
"link-preview-js": "^3.0.4"
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ definePlugin(
|
|||||||
},
|
},
|
||||||
stage: ReleaseStage.NIGHTLY,
|
stage: ReleaseStage.NIGHTLY,
|
||||||
version: '0.0.1',
|
version: '0.0.1',
|
||||||
|
commands: ['com.blocksuite.bookmark-block.get-bookmark-data-by-link'],
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
{
|
{
|
||||||
@@ -28,5 +29,19 @@ definePlugin(
|
|||||||
import.meta.webpackHot.accept('./blocksuite', () =>
|
import.meta.webpackHot.accept('./blocksuite', () =>
|
||||||
onHot(import('./blocksuite/index'))
|
onHot(import('./blocksuite/index'))
|
||||||
),
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
load: () =>
|
||||||
|
import(
|
||||||
|
/* webpackIgnore: true */
|
||||||
|
'./server'
|
||||||
|
),
|
||||||
|
hotModuleReload: onHot =>
|
||||||
|
onHot(
|
||||||
|
import(
|
||||||
|
/* webpackIgnore: true */
|
||||||
|
'./server'
|
||||||
|
)
|
||||||
|
),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { ServerAdapter } from '@toeverything/plugin-infra/type';
|
||||||
import { getLinkPreview } from 'link-preview-js';
|
import { getLinkPreview } from 'link-preview-js';
|
||||||
|
|
||||||
type MetaData = {
|
type MetaData = {
|
||||||
@@ -26,31 +27,41 @@ export interface PreviewType {
|
|||||||
favicons: string[];
|
favicons: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
const adapter: ServerAdapter = affine => {
|
||||||
getBookmarkDataByLink: async (_: unknown, url: string): Promise<MetaData> => {
|
affine.registerCommand(
|
||||||
const previewData = (await getLinkPreview(url, {
|
'com.blocksuite.bookmark-block.get-bookmark-data-by-link',
|
||||||
timeout: 6000,
|
async (url: string): Promise<MetaData> => {
|
||||||
headers: {
|
const previewData = (await getLinkPreview(url, {
|
||||||
'user-agent': 'googlebot',
|
timeout: 6000,
|
||||||
},
|
headers: {
|
||||||
followRedirects: 'follow',
|
'user-agent': 'googlebot',
|
||||||
}).catch(() => {
|
},
|
||||||
return {
|
followRedirects: 'follow',
|
||||||
title: '',
|
}).catch(() => {
|
||||||
siteName: '',
|
return {
|
||||||
description: '',
|
title: '',
|
||||||
images: [],
|
siteName: '',
|
||||||
videos: [],
|
description: '',
|
||||||
contentType: `text/html`,
|
images: [],
|
||||||
favicons: [],
|
videos: [],
|
||||||
};
|
contentType: `text/html`,
|
||||||
})) as PreviewType;
|
favicons: [],
|
||||||
|
};
|
||||||
|
})) as PreviewType;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: previewData.title,
|
title: previewData.title,
|
||||||
description: previewData.description,
|
description: previewData.description,
|
||||||
icon: previewData.favicons[0],
|
icon: previewData.favicons[0],
|
||||||
image: previewData.images[0],
|
image: previewData.images[0],
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
);
|
||||||
|
return () => {
|
||||||
|
affine.unregisterCommand(
|
||||||
|
'com.blocksuite.bookmark-block.get-bookmark-data-by-link'
|
||||||
|
);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default adapter;
|
||||||
|
|||||||
@@ -11,9 +11,6 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "../../packages/plugin-infra"
|
"path": "../../packages/plugin-infra"
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "../../packages/env"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@affine/component": "workspace:*",
|
||||||
"@toeverything/plugin-infra": "workspace:*"
|
"@toeverything/plugin-infra": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Button, Input } from '@affine/component';
|
import { Button, Input } from '@affine/component';
|
||||||
import { rootStore } from '@affine/workspace/atom';
|
import { rootStore } from '@toeverything/plugin-infra/manager';
|
||||||
import type { PluginUIAdapter } from '@toeverything/plugin-infra/type';
|
import type { PluginUIAdapter } from '@toeverything/plugin-infra/type';
|
||||||
import { Provider, useAtom, useAtomValue, useSetAtom } from 'jotai';
|
import { Provider, useAtom, useAtomValue, useSetAtom } from 'jotai';
|
||||||
import type { ReactElement } from 'react';
|
import type { ReactElement } from 'react';
|
||||||
@@ -10,7 +10,7 @@ import { Conversation } from '../core/components/conversation';
|
|||||||
import { Divider } from '../core/components/divider';
|
import { Divider } from '../core/components/divider';
|
||||||
import { openAIApiKeyAtom, useChatAtoms } from '../core/hooks';
|
import { openAIApiKeyAtom, useChatAtoms } from '../core/hooks';
|
||||||
|
|
||||||
if (!environment.isServer) {
|
if (typeof window === 'undefined') {
|
||||||
import('@blocksuite/blocks').then(({ FormatQuickBar }) => {
|
import('@blocksuite/blocks').then(({ FormatQuickBar }) => {
|
||||||
FormatQuickBar.customElements.push((_page, getSelection) => {
|
FormatQuickBar.customElements.push((_page, getSelection) => {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import '@affine/env/config';
|
|
||||||
|
|
||||||
import { definePlugin } from '@toeverything/plugin-infra/manager';
|
import { definePlugin } from '@toeverything/plugin-infra/manager';
|
||||||
import { ReleaseStage } from '@toeverything/plugin-infra/type';
|
import { ReleaseStage } from '@toeverything/plugin-infra/type';
|
||||||
|
|
||||||
@@ -22,6 +20,7 @@ definePlugin(
|
|||||||
},
|
},
|
||||||
stage: ReleaseStage.NIGHTLY,
|
stage: ReleaseStage.NIGHTLY,
|
||||||
version: '0.0.1',
|
version: '0.0.1',
|
||||||
|
commands: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
load: () => import('./UI/index'),
|
load: () => import('./UI/index'),
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
"include": ["./src"],
|
"include": ["./src"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"noEmit": false,
|
"noEmit": false,
|
||||||
"composite": true,
|
|
||||||
"outDir": "lib"
|
"outDir": "lib"
|
||||||
},
|
},
|
||||||
"references": [
|
"references": [
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
packages=(
|
packages=(
|
||||||
"y-indexeddb"
|
"y-indexeddb"
|
||||||
"infra"
|
"infra",
|
||||||
|
"plugin-infra"
|
||||||
)
|
)
|
||||||
|
|
||||||
for package in "${packages[@]}"; do
|
for package in "${packages[@]}"; do
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
|
import { resolve } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
import { spawnSync } from 'child_process';
|
||||||
import { beforeAll } from 'vitest';
|
import { beforeAll } from 'vitest';
|
||||||
|
|
||||||
|
const rootDir = fileURLToPath(new URL('../../', import.meta.url));
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
console.log('Build plugin infra');
|
||||||
|
spawnSync('yarn', ['build'], {
|
||||||
|
stdio: 'inherit',
|
||||||
|
cwd: resolve(rootDir, './packages/plugin-infra'),
|
||||||
|
});
|
||||||
|
|
||||||
console.log('Build plugins');
|
console.log('Build plugins');
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
await import('../../apps/electron/scripts/plugins/build-plugins.mjs');
|
await import('../../apps/electron/scripts/plugins/build-plugins.mjs');
|
||||||
|
|||||||
@@ -106,6 +106,17 @@
|
|||||||
{
|
{
|
||||||
"path": "./packages/debug"
|
"path": "./packages/debug"
|
||||||
},
|
},
|
||||||
|
// Plugins
|
||||||
|
{
|
||||||
|
"path": "./packages/plugin-infra"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./plugins/bookmark-block"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./plugins/copilot"
|
||||||
|
},
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
{
|
{
|
||||||
"path": "./tests"
|
"path": "./tests"
|
||||||
@@ -129,12 +140,7 @@
|
|||||||
{
|
{
|
||||||
"path": "./packages/y-indexeddb"
|
"path": "./packages/y-indexeddb"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "./packages/plugin-infra"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "./plugins/copilot"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "./tests/fixtures"
|
"path": "./tests/fixtures"
|
||||||
},
|
},
|
||||||
|
|||||||
18
yarn.lock
18
yarn.lock
@@ -34,6 +34,7 @@ __metadata:
|
|||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@affine/bookmark-block@workspace:plugins/bookmark-block"
|
resolution: "@affine/bookmark-block@workspace:plugins/bookmark-block"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@affine/component": "workspace:*"
|
||||||
"@toeverything/plugin-infra": "workspace:*"
|
"@toeverything/plugin-infra": "workspace:*"
|
||||||
foxact: ^0.2.7
|
foxact: ^0.2.7
|
||||||
link-preview-js: ^3.0.4
|
link-preview-js: ^3.0.4
|
||||||
@@ -86,6 +87,7 @@ __metadata:
|
|||||||
"@radix-ui/react-radio-group": ^1.1.3
|
"@radix-ui/react-radio-group": ^1.1.3
|
||||||
"@radix-ui/react-toast": ^1.1.4
|
"@radix-ui/react-toast": ^1.1.4
|
||||||
"@toeverything/hooks": "workspace:*"
|
"@toeverything/hooks": "workspace:*"
|
||||||
|
"@toeverything/plugin-infra": "workspace:*"
|
||||||
"@toeverything/theme": ^0.6.1
|
"@toeverything/theme": ^0.6.1
|
||||||
"@types/react": ^18.2.6
|
"@types/react": ^18.2.6
|
||||||
"@types/react-datepicker": ^4.11.2
|
"@types/react-datepicker": ^4.11.2
|
||||||
@@ -121,6 +123,7 @@ __metadata:
|
|||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@affine/copilot@workspace:plugins/copilot"
|
resolution: "@affine/copilot@workspace:plugins/copilot"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@affine/component": "workspace:*"
|
||||||
"@toeverything/plugin-infra": "workspace:*"
|
"@toeverything/plugin-infra": "workspace:*"
|
||||||
"@types/marked": ^5.0.0
|
"@types/marked": ^5.0.0
|
||||||
"@types/react": ^18.2.6
|
"@types/react": ^18.2.6
|
||||||
@@ -162,8 +165,10 @@ __metadata:
|
|||||||
"@electron-forge/shared-types": ^6.1.1
|
"@electron-forge/shared-types": ^6.1.1
|
||||||
"@electron/remote": 2.0.9
|
"@electron/remote": 2.0.9
|
||||||
"@toeverything/infra": "workspace:*"
|
"@toeverything/infra": "workspace:*"
|
||||||
|
"@toeverything/plugin-infra": "workspace:*"
|
||||||
"@types/fs-extra": ^11.0.1
|
"@types/fs-extra": ^11.0.1
|
||||||
"@types/uuid": ^9.0.1
|
"@types/uuid": ^9.0.1
|
||||||
|
async-call-rpc: ^6.3.1
|
||||||
cheerio: ^1.0.0-rc.12
|
cheerio: ^1.0.0-rc.12
|
||||||
cross-env: 7.0.3
|
cross-env: 7.0.3
|
||||||
electron: =25.0.1
|
electron: =25.0.1
|
||||||
@@ -441,6 +446,7 @@ __metadata:
|
|||||||
"@affine/debug": "workspace:*"
|
"@affine/debug": "workspace:*"
|
||||||
"@affine/env": "workspace:*"
|
"@affine/env": "workspace:*"
|
||||||
"@toeverything/hooks": "workspace:*"
|
"@toeverything/hooks": "workspace:*"
|
||||||
|
"@toeverything/plugin-infra": "workspace:*"
|
||||||
"@toeverything/y-indexeddb": "workspace:*"
|
"@toeverything/y-indexeddb": "workspace:*"
|
||||||
"@types/ws": ^8.5.4
|
"@types/ws": ^8.5.4
|
||||||
firebase: ^9.22.1
|
firebase: ^9.22.1
|
||||||
@@ -9080,15 +9086,14 @@ __metadata:
|
|||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@toeverything/plugin-infra@workspace:packages/plugin-infra"
|
resolution: "@toeverything/plugin-infra@workspace:packages/plugin-infra"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@affine/component": "workspace:*"
|
|
||||||
"@affine/env": "workspace:*"
|
|
||||||
"@affine/workspace": "workspace:*"
|
|
||||||
"@blocksuite/blocks": 0.0.0-20230607055421-9b20fcaf-nightly
|
"@blocksuite/blocks": 0.0.0-20230607055421-9b20fcaf-nightly
|
||||||
"@blocksuite/editor": 0.0.0-20230607055421-9b20fcaf-nightly
|
"@blocksuite/editor": 0.0.0-20230607055421-9b20fcaf-nightly
|
||||||
"@blocksuite/global": 0.0.0-20230607055421-9b20fcaf-nightly
|
"@blocksuite/global": 0.0.0-20230607055421-9b20fcaf-nightly
|
||||||
"@blocksuite/lit": 0.0.0-20230607055421-9b20fcaf-nightly
|
"@blocksuite/lit": 0.0.0-20230607055421-9b20fcaf-nightly
|
||||||
"@blocksuite/store": 0.0.0-20230607055421-9b20fcaf-nightly
|
"@blocksuite/store": 0.0.0-20230607055421-9b20fcaf-nightly
|
||||||
jotai: ^2.1.1
|
jotai: ^2.1.1
|
||||||
|
vite: ^4.3.9
|
||||||
|
vite-plugin-dts: ^2.3.0
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@blocksuite/blocks": "*"
|
"@blocksuite/blocks": "*"
|
||||||
"@blocksuite/editor": "*"
|
"@blocksuite/editor": "*"
|
||||||
@@ -11332,6 +11337,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"async-call-rpc@npm:^6.3.1":
|
||||||
|
version: 6.3.1
|
||||||
|
resolution: "async-call-rpc@npm:6.3.1"
|
||||||
|
checksum: 024e1bab4752b3aa66145977d534e6dc4c316e1d746c11a6a5076b30b16d42fa0ce4793be5a330319644f1335b01cbe13ee4ac28454cded37b1c803608400934
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"async-limiter@npm:~1.0.0":
|
"async-limiter@npm:~1.0.0":
|
||||||
version: 1.0.1
|
version: 1.0.1
|
||||||
resolution: "async-limiter@npm:1.0.1"
|
resolution: "async-limiter@npm:1.0.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user