feat: move plugins config to setting (#3259)
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import {
|
import {
|
||||||
|
AiIcon,
|
||||||
AppearanceIcon,
|
AppearanceIcon,
|
||||||
InformationIcon,
|
InformationIcon,
|
||||||
KeyboardIcon,
|
KeyboardIcon,
|
||||||
@@ -8,9 +9,14 @@ import type { FC, SVGProps } from 'react';
|
|||||||
|
|
||||||
import { AboutAffine } from './about';
|
import { AboutAffine } from './about';
|
||||||
import { AppearanceSettings } from './appearance';
|
import { AppearanceSettings } from './appearance';
|
||||||
|
import { Plugins } from './plugins';
|
||||||
import { Shortcuts } from './shortcuts';
|
import { Shortcuts } from './shortcuts';
|
||||||
|
|
||||||
export type GeneralSettingKeys = 'shortcuts' | 'appearance' | 'about';
|
export type GeneralSettingKeys =
|
||||||
|
| 'shortcuts'
|
||||||
|
| 'appearance'
|
||||||
|
| 'plugins'
|
||||||
|
| 'about';
|
||||||
|
|
||||||
export type GeneralSettingList = {
|
export type GeneralSettingList = {
|
||||||
key: GeneralSettingKeys;
|
key: GeneralSettingKeys;
|
||||||
@@ -34,6 +40,12 @@ export const useGeneralSettingList = (): GeneralSettingList => {
|
|||||||
icon: KeyboardIcon,
|
icon: KeyboardIcon,
|
||||||
testId: 'shortcuts-panel-trigger',
|
testId: 'shortcuts-panel-trigger',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'plugins',
|
||||||
|
title: 'Plugins',
|
||||||
|
icon: AiIcon,
|
||||||
|
testId: 'plugins-panel-trigger',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'about',
|
key: 'about',
|
||||||
title: t['About AFFiNE'](),
|
title: t['About AFFiNE'](),
|
||||||
@@ -53,6 +65,8 @@ export const GeneralSetting = ({
|
|||||||
return <Shortcuts />;
|
return <Shortcuts />;
|
||||||
case 'appearance':
|
case 'appearance':
|
||||||
return <AppearanceSettings />;
|
return <AppearanceSettings />;
|
||||||
|
case 'plugins':
|
||||||
|
return <Plugins />;
|
||||||
case 'about':
|
case 'about':
|
||||||
return <AboutAffine />;
|
return <AboutAffine />;
|
||||||
default:
|
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={sidebarSubtitle}>{t['General']()}</div>
|
||||||
<div className={sidebarItemsWrapper}>
|
<div className={sidebarItemsWrapper}>
|
||||||
{generalSettingList.map(({ title, icon, key, testId }) => {
|
{generalSettingList.map(({ title, icon, key, testId }) => {
|
||||||
|
if (!runtimeConfig.enablePlugin && key === 'plugins') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(sidebarSelectItem, {
|
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 type { PluginUIAdapter } from '@toeverything/plugin-infra/type';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
@@ -8,26 +10,43 @@ import { conversationHistoryDBName } from '../core/langchain/message-history';
|
|||||||
|
|
||||||
export const DebugContent: PluginUIAdapter['debugContent'] = () => {
|
export const DebugContent: PluginUIAdapter['debugContent'] = () => {
|
||||||
const [key, setKey] = useAtom(openAIApiKeyAtom);
|
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<span>OpenAI API Key:</span>
|
<SettingWrapper title={'Ai Copilot'}>
|
||||||
<Input
|
<SettingRow name={'openAI API key'} desc={desc}></SettingRow>
|
||||||
defaultValue={key ?? undefined}
|
<FlexWrapper justifyContent="space-between">
|
||||||
onChange={useCallback(
|
<Input
|
||||||
(newValue: string) => {
|
defaultValue={key ?? undefined}
|
||||||
setKey(newValue);
|
onChange={useCallback(
|
||||||
},
|
(newValue: string) => {
|
||||||
[setKey]
|
setKey(newValue);
|
||||||
)}
|
},
|
||||||
/>
|
[setKey]
|
||||||
<Button
|
)}
|
||||||
onClick={() => {
|
/>
|
||||||
indexedDB.deleteDatabase(conversationHistoryDBName);
|
<Button
|
||||||
location.reload();
|
size="middle"
|
||||||
}}
|
onClick={() => {
|
||||||
>
|
indexedDB.deleteDatabase(conversationHistoryDBName);
|
||||||
Clean conversations
|
location.reload();
|
||||||
</Button>
|
}}
|
||||||
|
>
|
||||||
|
{'Clean conversations'}
|
||||||
|
</Button>
|
||||||
|
</FlexWrapper>
|
||||||
|
</SettingWrapper>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { waitEditorLoad } from '@affine-test/kit/utils/page-logic';
|
|||||||
import {
|
import {
|
||||||
openAboutPanel,
|
openAboutPanel,
|
||||||
openAppearancePanel,
|
openAppearancePanel,
|
||||||
|
openPluginsPanel,
|
||||||
openSettingModal,
|
openSettingModal,
|
||||||
openShortcutsPanel,
|
openShortcutsPanel,
|
||||||
} from '@affine-test/kit/utils/setting';
|
} from '@affine-test/kit/utils/setting';
|
||||||
@@ -61,6 +62,15 @@ test('Open shortcuts panel', async ({ page }) => {
|
|||||||
await expect(title).toBeVisible();
|
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 }) => {
|
test('Open about panel', async ({ page }) => {
|
||||||
await openHomePage(page);
|
await openHomePage(page);
|
||||||
await waitEditorLoad(page);
|
await waitEditorLoad(page);
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ export async function openAppearancePanel(page: Page) {
|
|||||||
export async function openShortcutsPanel(page: Page) {
|
export async function openShortcutsPanel(page: Page) {
|
||||||
await page.getByTestId('shortcuts-panel-trigger').click();
|
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) {
|
export async function openAboutPanel(page: Page) {
|
||||||
await page.getByTestId('about-panel-trigger').click();
|
await page.getByTestId('about-panel-trigger').click();
|
||||||
|
|||||||
Reference in New Issue
Block a user