mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-12 23:56:36 +08:00
feat: move plugins config to setting (#3259)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import {
|
||||
AiIcon,
|
||||
AppearanceIcon,
|
||||
InformationIcon,
|
||||
KeyboardIcon,
|
||||
@@ -8,9 +9,14 @@ import type { FC, SVGProps } from 'react';
|
||||
|
||||
import { AboutAffine } from './about';
|
||||
import { AppearanceSettings } from './appearance';
|
||||
import { Plugins } from './plugins';
|
||||
import { Shortcuts } from './shortcuts';
|
||||
|
||||
export type GeneralSettingKeys = 'shortcuts' | 'appearance' | 'about';
|
||||
export type GeneralSettingKeys =
|
||||
| 'shortcuts'
|
||||
| 'appearance'
|
||||
| 'plugins'
|
||||
| 'about';
|
||||
|
||||
export type GeneralSettingList = {
|
||||
key: GeneralSettingKeys;
|
||||
@@ -34,6 +40,12 @@ export const useGeneralSettingList = (): GeneralSettingList => {
|
||||
icon: KeyboardIcon,
|
||||
testId: 'shortcuts-panel-trigger',
|
||||
},
|
||||
{
|
||||
key: 'plugins',
|
||||
title: 'Plugins',
|
||||
icon: AiIcon,
|
||||
testId: 'plugins-panel-trigger',
|
||||
},
|
||||
{
|
||||
key: 'about',
|
||||
title: t['About AFFiNE'](),
|
||||
@@ -53,6 +65,8 @@ export const GeneralSetting = ({
|
||||
return <Shortcuts />;
|
||||
case 'appearance':
|
||||
return <AppearanceSettings />;
|
||||
case 'plugins':
|
||||
return <Plugins />;
|
||||
case 'about':
|
||||
return <AboutAffine />;
|
||||
default:
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { SettingHeader } from '@affine/component/setting-components';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { affinePluginsAtom } from '@toeverything/plugin-infra/manager';
|
||||
import { useAtomValue } from 'jotai';
|
||||
|
||||
export const Plugins = () => {
|
||||
const t = useAFFiNEI18N();
|
||||
const plugins = useAtomValue(affinePluginsAtom);
|
||||
return (
|
||||
<>
|
||||
<SettingHeader
|
||||
title={'Plugins'}
|
||||
subtitle={t['None yet']()}
|
||||
data-testid="plugins-title"
|
||||
/>
|
||||
{Object.values(plugins).map(({ definition, uiAdapter }) => {
|
||||
const Content = uiAdapter.debugContent;
|
||||
return <div key={definition.id}>{Content && <Content />}</div>;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const settingWrapper = style({
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
minWidth: '150px',
|
||||
maxWidth: '250px',
|
||||
});
|
||||
@@ -47,6 +47,9 @@ export const SettingSidebar: FC<{
|
||||
<div className={sidebarSubtitle}>{t['General']()}</div>
|
||||
<div className={sidebarItemsWrapper}>
|
||||
{generalSettingList.map(({ title, icon, key, testId }) => {
|
||||
if (!runtimeConfig.enablePlugin && key === 'plugins') {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={clsx(sidebarSelectItem, {
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import { MainContainer } from '@affine/component/workspace';
|
||||
import { NoSsr } from '@mui/material';
|
||||
import { affinePluginsAtom } from '@toeverything/plugin-infra/manager';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import type { ReactElement } from 'react';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
import { AppContainer } from '../components/affine/app-container';
|
||||
|
||||
const Plugins = () => {
|
||||
const plugins = useAtomValue(affinePluginsAtom);
|
||||
return (
|
||||
<NoSsr>
|
||||
<div>
|
||||
{Object.values(plugins).map(({ definition, uiAdapter }) => {
|
||||
const Content = uiAdapter.debugContent;
|
||||
return (
|
||||
<div key={definition.id}>
|
||||
{/* todo: support i18n */}
|
||||
{definition.name.fallback}
|
||||
{Content && <Content />}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</NoSsr>
|
||||
);
|
||||
};
|
||||
|
||||
export default function PluginPage(): ReactElement {
|
||||
if (!runtimeConfig.enablePlugin) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<AppContainer>
|
||||
<MainContainer>
|
||||
<Suspense>
|
||||
<Plugins />
|
||||
</Suspense>
|
||||
</MainContainer>
|
||||
</AppContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Button, Input } from '@affine/component';
|
||||
import { Button, FlexWrapper, Input } from '@affine/component';
|
||||
import { SettingRow } from '@affine/component/setting-components';
|
||||
import { SettingWrapper } from '@affine/component/setting-components';
|
||||
import type { PluginUIAdapter } from '@toeverything/plugin-infra/type';
|
||||
import { useAtom } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
@@ -8,26 +10,43 @@ import { conversationHistoryDBName } from '../core/langchain/message-history';
|
||||
|
||||
export const DebugContent: PluginUIAdapter['debugContent'] = () => {
|
||||
const [key, setKey] = useAtom(openAIApiKeyAtom);
|
||||
const desc = (
|
||||
<>
|
||||
<span>You can get your API key from </span>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://beta.openai.com/account/api-keys"
|
||||
>
|
||||
here.
|
||||
</a>
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<span>OpenAI API Key:</span>
|
||||
<Input
|
||||
defaultValue={key ?? undefined}
|
||||
onChange={useCallback(
|
||||
(newValue: string) => {
|
||||
setKey(newValue);
|
||||
},
|
||||
[setKey]
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
onClick={() => {
|
||||
indexedDB.deleteDatabase(conversationHistoryDBName);
|
||||
location.reload();
|
||||
}}
|
||||
>
|
||||
Clean conversations
|
||||
</Button>
|
||||
<SettingWrapper title={'Ai Copilot'}>
|
||||
<SettingRow name={'openAI API key'} desc={desc}></SettingRow>
|
||||
<FlexWrapper justifyContent="space-between">
|
||||
<Input
|
||||
defaultValue={key ?? undefined}
|
||||
onChange={useCallback(
|
||||
(newValue: string) => {
|
||||
setKey(newValue);
|
||||
},
|
||||
[setKey]
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
size="middle"
|
||||
onClick={() => {
|
||||
indexedDB.deleteDatabase(conversationHistoryDBName);
|
||||
location.reload();
|
||||
}}
|
||||
>
|
||||
{'Clean conversations'}
|
||||
</Button>
|
||||
</FlexWrapper>
|
||||
</SettingWrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import { waitEditorLoad } from '@affine-test/kit/utils/page-logic';
|
||||
import {
|
||||
openAboutPanel,
|
||||
openAppearancePanel,
|
||||
openPluginsPanel,
|
||||
openSettingModal,
|
||||
openShortcutsPanel,
|
||||
} from '@affine-test/kit/utils/setting';
|
||||
@@ -61,6 +62,15 @@ test('Open shortcuts panel', async ({ page }) => {
|
||||
await expect(title).toBeVisible();
|
||||
});
|
||||
|
||||
test('Open plugins panel', async ({ page }) => {
|
||||
await openHomePage(page);
|
||||
await waitEditorLoad(page);
|
||||
await openSettingModal(page);
|
||||
await openPluginsPanel(page);
|
||||
const title = await page.getByTestId('plugins-title');
|
||||
await expect(title).toBeVisible();
|
||||
});
|
||||
|
||||
test('Open about panel', async ({ page }) => {
|
||||
await openHomePage(page);
|
||||
await waitEditorLoad(page);
|
||||
|
||||
@@ -19,6 +19,9 @@ export async function openAppearancePanel(page: Page) {
|
||||
export async function openShortcutsPanel(page: Page) {
|
||||
await page.getByTestId('shortcuts-panel-trigger').click();
|
||||
}
|
||||
export async function openPluginsPanel(page: Page) {
|
||||
await page.getByTestId('plugins-panel-trigger').click();
|
||||
}
|
||||
|
||||
export async function openAboutPanel(page: Page) {
|
||||
await page.getByTestId('about-panel-trigger').click();
|
||||
|
||||
Reference in New Issue
Block a user