Skip to content

Commit

Permalink
✨ feat: 支持快速刷新与预览 manifest
Browse files Browse the repository at this point in the history
  • Loading branch information
arvinxx committed Sep 9, 2023
1 parent 8709ab3 commit 5bd2eb0
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 26 deletions.
30 changes: 30 additions & 0 deletions src/components/ManifestPreviewer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Highlighter } from '@lobehub/ui';
import { Popover } from 'antd';
import { ReactNode, memo } from 'react';

interface PluginManifestPreviewerProps {
children?: ReactNode;
manifest: object;
trigger?: 'click' | 'hover';
}

const ManifestPreviewer = memo<PluginManifestPreviewerProps>(
({ manifest, children, trigger = 'click' }) => (
<Popover
arrow={false}
content={
<Highlighter language={'json'} style={{ maxHeight: 600, maxWidth: 400 }}>
{JSON.stringify(manifest, null, 2)}
</Highlighter>
}
placement={'right'}
style={{ width: 400 }}
title={'Manifest JSON'}
trigger={trigger}
>
{children}
</Popover>
),
);

export default ManifestPreviewer;
2 changes: 1 addition & 1 deletion src/features/AgentSetting/AgentPlugin/LocalPluginItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import isEqual from 'fast-deep-equal';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';
import DevModal from 'src/features/PluginDevModal';

import DevModal from '@/features/PluginDevModal';
import { pluginSelectors, usePluginStore } from '@/store/plugin';

import { useStore } from '../store';
Expand Down
21 changes: 6 additions & 15 deletions src/features/PluginDevModal/ManifestForm.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { LobeChatPluginManifest, pluginManifestSchema } from '@lobehub/chat-plugin-sdk';
import { ActionIcon, Form, FormItemProps, Highlighter, Input, Tooltip } from '@lobehub/ui';
import { FormInstance, Popover, Radio } from 'antd';
import { ActionIcon, Form, FormItemProps, Input, Tooltip } from '@lobehub/ui';
import { FormInstance, Radio } from 'antd';
import { FileCode, RotateCwIcon } from 'lucide-react';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';

import ManifestPreviewer from '@/components/ManifestPreviewer';

const ManifestForm = memo<{ form: FormInstance; mode?: 'url' | 'local' }>(
({ form, mode = 'url' }) => {
const { t } = useTranslation('plugin');
Expand Down Expand Up @@ -37,24 +39,13 @@ const ManifestForm = memo<{ form: FormInstance; mode?: 'url' | 'local' }>(
<Flexbox horizontal justify={'space-between'} style={{ marginTop: 8 }}>
{t('dev.meta.manifest.desc')}
{manifest && (
<Popover
arrow={false}
content={
<Highlighter language={'json'} style={{ maxHeight: 600, maxWidth: 400 }}>
{JSON.stringify(manifest, null, 2)}
</Highlighter>
}
placement={'right'}
style={{ width: 400 }}
title={'Manifest JSON'}
trigger={'click'}
>
<ManifestPreviewer manifest={manifest}>
<ActionIcon
icon={FileCode}
size={'small'}
title={t('dev.meta.manifest.preview')}
/>
</Popover>
</ManifestPreviewer>
)}
</Flexbox>
),
Expand Down
2 changes: 1 addition & 1 deletion src/locales/default/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default {
inbox: {
defaultMessage:
'你好,我是你的智能助手,你可以问我任何问题,我会尽力回答你。如果需要获得更加专业或定制的助手,可以点击<kbd>+</kbd>创建自定义助手',
desc: '开启大脑集群,激发思维火花。你的智能助理,就在这里与你交流一切',
desc: '开启大脑集群,激发思维火花。你的智能助理,在这里与你交流一切',
title: '随便聊聊',
},
message: {
Expand Down
68 changes: 63 additions & 5 deletions src/pages/chat/features/Header/PluginTag/PluginStatus.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import { ActionIcon } from '@lobehub/ui';
import { Badge } from 'antd';
import { LucideRotateCw } from 'lucide-react';
import { Badge, Button, Tag } from 'antd';
import { LucideRotateCw, LucideTrash2, RotateCwIcon } from 'lucide-react';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';

import ManifestPreviewer from '@/components/ManifestPreviewer';
import { pluginSelectors, usePluginStore } from '@/store/plugin';
import { useSessionStore } from '@/store/session';

const PluginStatus = memo<{ id: string; title?: string }>(({ title, id }) => {
interface PluginStatusProps {
deprecated?: boolean;
id: string;
title?: string;
}
const PluginStatus = memo<PluginStatusProps>(({ title, id, deprecated }) => {
const { t } = useTranslation('common');
const [status, fetchPluginManifest] = usePluginStore((s) => [
const [status, isCustom, fetchPluginManifest] = usePluginStore((s) => [
pluginSelectors.getPluginManifestLoadingStatus(id)(s),
pluginSelectors.isCustomPlugin(id)(s),
s.fetchPluginManifest,
]);

const manifest = usePluginStore(pluginSelectors.getPluginManifestById(id));

const removePlugin = useSessionStore((s) => s.removePlugin);

const renderStatus = useMemo(() => {
switch (status) {
case 'loading': {
Expand All @@ -37,9 +49,55 @@ const PluginStatus = memo<{ id: string; title?: string }>(({ title, id }) => {
}
}, [status]);

const tag =
// 废弃标签
deprecated ? (
<Tag bordered={false} color={'red'} style={{ marginRight: 0 }}>
{t('list.item.deprecated.title', { ns: 'plugin' })}
</Tag>
) : // 自定义标签
isCustom ? (
<Tag bordered={false} color={'gold'}>
{t('list.item.local.title', { ns: 'plugin' })}
</Tag>
) : null;

return (
<Flexbox gap={12} horizontal justify={'space-between'}>
{title} {renderStatus}
<Flexbox align={'center'} gap={8} horizontal>
{title || id}
{tag}
</Flexbox>

{deprecated ? (
<ActionIcon
icon={LucideTrash2}
onClick={(e) => {
e.stopPropagation();
removePlugin(id);
}}
size={'small'}
title={t('settingPlugin.clearDeprecated', { ns: 'setting' })}
/>
) : (
<Flexbox align={'center'} horizontal>
{isCustom ? (
<ActionIcon
icon={RotateCwIcon}
onClick={(e) => {
e.stopPropagation();
fetchPluginManifest(id);
// form.validateFields(['manifest']);
}}
size={'small'}
title={t('dev.meta.manifest.refresh', { ns: 'plugin' })}
/>
) : null}
<ManifestPreviewer manifest={manifest || {}} trigger={'hover'}>
<Button icon={renderStatus} size={'small'} type={'text'} />
</ManifestPreviewer>
</Flexbox>
)}
</Flexbox>
);
});
Expand Down
8 changes: 5 additions & 3 deletions src/pages/chat/features/Header/PluginTag/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ const PluginTag = memo<PluginTagProps>(({ plugins }) => {

const items: MenuProps['items'] = plugins.map((id) => {
const item = list.find((i) => i.identifier === id);
const isDeprecated = !item?.title;
const avatar = isDeprecated ? '♻️' : item?.avatar || '🧩';

return {
icon: <Avatar avatar={item?.avatar} size={24} style={{ marginLeft: -6, marginRight: 2 }} />,
icon: <Avatar avatar={avatar} size={24} style={{ marginLeft: -6, marginRight: 2 }} />,
key: id,
label: <PluginStatus id={id} title={item?.title} />,
label: <PluginStatus deprecated={isDeprecated} id={id} title={item?.title} />,
};
});

Expand All @@ -36,7 +38,7 @@ const PluginTag = memo<PluginTagProps>(({ plugins }) => {
<div>
<Tag>
{<Icon icon={LucideToyBrick} />}
{pluginHelpers.getPluginTitle(displayPlugin?.meta)}
{pluginHelpers.getPluginTitle(displayPlugin?.meta) || plugins[0]}
{count > 1 && <div>({plugins.length - 1}+)</div>}
</Tag>
</div>
Expand Down
6 changes: 6 additions & 0 deletions src/store/plugin/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { LobeChatPluginMeta } from '@lobehub/chat-plugin-sdk';
import i18n from 'i18next';

import { CustomPlugin } from '@/types/plugin';

const getI18nValue = (value: string | Record<string, string> | undefined) => {
if (!value) return;

Expand All @@ -18,9 +20,13 @@ const getPluginTitle = (meta?: LobeChatPluginMeta['meta']) => getI18nValue(meta?
const getPluginDesc = (meta?: LobeChatPluginMeta['meta']) => getI18nValue(meta?.description);
const getPluginAvatar = (meta?: LobeChatPluginMeta['meta']) => meta?.avatar;

const isCustomPlugin = (id: string, pluginList: CustomPlugin[]) =>
pluginList.some((i) => i.identifier === id);

export const pluginHelpers = {
getPluginAvatar,
getPluginDesc,
getPluginFormList,
getPluginTitle,
isCustomPlugin,
};
4 changes: 4 additions & 0 deletions src/store/plugin/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ const getPluginManifestLoadingStatus = (id: string) => (s: PluginStoreState) =>
if (!!manifest) return 'success';
};

const isCustomPlugin = (id: string) => (s: PluginStoreState) =>
pluginHelpers.isCustomPlugin(id, s.customPluginList);

const displayPluginList = (s: PluginStoreState) =>
pluginList(s).map((p) => ({
author: p.author,
Expand All @@ -69,5 +72,6 @@ export const pluginSelectors = {
getPluginManifestLoadingStatus,
getPluginMetaById,
getPluginSettingsById,
isCustomPlugin,
pluginList,
};
17 changes: 16 additions & 1 deletion src/store/session/slices/agentConfig/action.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { produce } from 'immer';
import { StateCreator } from 'zustand/vanilla';

import { MetaData } from '@/types/meta';
Expand All @@ -7,9 +8,10 @@ import { SessionStore } from '../../store';
import { sessionSelectors } from '../session/selectors';

/**
* 代理行为接口
* 助手接口
*/
export interface AgentAction {
removePlugin: (id: string) => void;
/**
* 更新代理配置
* @param config - 部分 LobeAgentConfig 的配置
Expand All @@ -24,13 +26,26 @@ export const createAgentSlice: StateCreator<
[],
AgentAction
> = (set, get) => ({
removePlugin: (id) => {
const { activeId } = get();
const session = sessionSelectors.currentSession(get());
if (!activeId || !session) return;

const config = produce(session.config, (draft) => {
draft.plugins = draft.plugins?.filter((i) => i !== id) || [];
});

get().dispatchSession({ config, id: activeId, type: 'updateSessionConfig' });
},

updateAgentConfig: (config) => {
const { activeId } = get();
const session = sessionSelectors.currentSession(get());
if (!activeId || !session) return;

get().dispatchSession({ config, id: activeId, type: 'updateSessionConfig' });
},

updateAgentMeta: (meta) => {
const { activeId } = get();
const session = sessionSelectors.currentSession(get());
Expand Down

0 comments on commit 5bd2eb0

Please sign in to comment.