@lobehub/chat 0.94.4 → 0.95.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/.github/workflows/pr-welcome.yml +1 -1
  2. package/CHANGELOG.md +60 -0
  3. package/CONTRIBUTING.md +88 -0
  4. package/package.json +4 -4
  5. package/src/app/chat/(desktop)/features/ChatInput.tsx +0 -1
  6. package/src/app/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +1 -1
  7. package/src/app/chat/features/Conversation/ChatList/Messages/Assistant.tsx +12 -15
  8. package/src/app/chat/features/Conversation/ChatList/Messages/Function.tsx +26 -15
  9. package/src/app/chat/features/Conversation/ChatList/Plugins/Inspector/Settings.tsx +36 -0
  10. package/src/app/chat/features/Conversation/ChatList/Plugins/{FunctionCall.tsx → Inspector/index.tsx} +53 -25
  11. package/src/app/chat/features/Conversation/ChatList/Plugins/{IFrameRender → Render/DefaultType/IFrameRender}/index.tsx +5 -16
  12. package/src/app/chat/features/Conversation/ChatList/Plugins/{PluginMessage.tsx → Render/DefaultType/index.tsx} +3 -3
  13. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/Iframe.tsx +136 -0
  14. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/index.tsx +28 -0
  15. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/index.tsx +28 -0
  16. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/utils/iframeOnReady.ts +23 -0
  17. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/utils/listenToPlugin.ts +65 -0
  18. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/utils/pluginSettings.ts +17 -0
  19. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/utils/pluginState.ts +20 -0
  20. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/utils/postMessage.ts +17 -0
  21. package/src/app/market/page.tsx +2 -22
  22. package/src/chains/summaryAgentName.ts +13 -8
  23. package/src/chains/summaryDescription.ts +28 -5
  24. package/src/chains/summaryTags.ts +8 -7
  25. package/src/chains/summaryTitle.ts +5 -3
  26. package/src/const/plugin.ts +1 -1
  27. package/src/features/PluginSettings/index.tsx +5 -3
  28. package/src/features/PluginSettingsModal/index.tsx +45 -0
  29. package/src/services/agentMarket.ts +9 -7
  30. package/src/services/pluginMarket.ts +2 -2
  31. package/src/store/global/helpers.ts +5 -0
  32. package/src/store/global/selectors/settings.ts +1 -0
  33. package/src/store/plugin/selectors.ts +10 -4
  34. package/src/store/session/slices/chat/actions/index.ts +4 -1
  35. package/src/store/session/slices/chat/actions/message.ts +102 -117
  36. package/src/store/session/slices/chat/actions/plugin.ts +109 -0
  37. package/src/store/session/slices/chat/reducers/message.ts +31 -1
  38. package/src/store/session/slices/chat/selectors/chat.ts +17 -20
  39. package/src/store/session/slices/chat/selectors/index.ts +4 -2
  40. package/src/types/chatMessage.ts +1 -0
  41. package/src/utils/message.ts +8 -1
  42. package/src/app/chat/features/Conversation/ChatList/Plugins/IFrameRender/hooks.ts +0 -29
  43. package/src/app/chat/features/Conversation/ChatList/Plugins/IFrameRender/utils.ts +0 -17
  44. /package/src/app/chat/features/Conversation/ChatList/Plugins/{PluginResultRender.tsx → Inspector/PluginResultJSON.tsx} +0 -0
  45. /package/src/app/chat/features/Conversation/ChatList/Plugins/{style.ts → Inspector/style.ts} +0 -0
  46. /package/src/app/chat/features/Conversation/ChatList/Plugins/{SystemJsRender → Render/DefaultType/SystemJsRender}/index.tsx +0 -0
  47. /package/src/app/chat/features/Conversation/ChatList/Plugins/{SystemJsRender → Render/DefaultType/SystemJsRender}/utils.ts +0 -0
@@ -1,7 +1,7 @@
1
1
  name: PR Welcome
2
2
 
3
3
  on:
4
- pull_request:
4
+ pull_request_target:
5
5
  types:
6
6
  - closed
7
7
 
package/CHANGELOG.md CHANGED
@@ -2,6 +2,66 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 0.95.0](https://github.com/lobehub/lobe-chat/compare/v0.94.5...v0.95.0)
6
+
7
+ <sup>Released on **2023-10-24**</sup>
8
+
9
+ #### ♻ Code Refactoring
10
+
11
+ - **misc**: 优化 plugin 文件夹命名以支持 standalone 类型的插件.
12
+
13
+ #### ✨ Features
14
+
15
+ - **misc**: Support function call at message end, support plugin settings modal, support plugin state and settings.
16
+
17
+ <br/>
18
+
19
+ <details>
20
+ <summary><kbd>Improvements and Fixes</kbd></summary>
21
+
22
+ #### Code refactoring
23
+
24
+ - **misc**: 优化 plugin 文件夹命名以支持 standalone 类型的插件 ([98860a8](https://github.com/lobehub/lobe-chat/commit/98860a8))
25
+
26
+ #### What's improved
27
+
28
+ - **misc**: Support function call at message end, closes [#357](https://github.com/lobehub/lobe-chat/issues/357) ([e195fdb](https://github.com/lobehub/lobe-chat/commit/e195fdb))
29
+ - **misc**: Support plugin settings modal ([f47b6fa](https://github.com/lobehub/lobe-chat/commit/f47b6fa))
30
+ - **misc**: Support plugin state and settings ([10829a4](https://github.com/lobehub/lobe-chat/commit/10829a4))
31
+
32
+ </details>
33
+
34
+ <div align="right">
35
+
36
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
37
+
38
+ </div>
39
+
40
+ ### [Version 0.94.5](https://github.com/lobehub/lobe-chat/compare/v0.94.4...v0.94.5)
41
+
42
+ <sup>Released on **2023-10-22**</sup>
43
+
44
+ #### 🐛 Bug Fixes
45
+
46
+ - **misc**: Fallback agent market index to en when not find correct locale.
47
+
48
+ <br/>
49
+
50
+ <details>
51
+ <summary><kbd>Improvements and Fixes</kbd></summary>
52
+
53
+ #### What's fixed
54
+
55
+ - **misc**: Fallback agent market index to en when not find correct locale, closes [#355](https://github.com/lobehub/lobe-chat/issues/355) ([7a45ab4](https://github.com/lobehub/lobe-chat/commit/7a45ab4))
56
+
57
+ </details>
58
+
59
+ <div align="right">
60
+
61
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
62
+
63
+ </div>
64
+
5
65
  ### [Version 0.94.4](https://github.com/lobehub/lobe-chat/compare/v0.94.3...v0.94.4)
6
66
 
7
67
  <sup>Released on **2023-10-21**</sup>
@@ -0,0 +1,88 @@
1
+ # Lobe Chat - Contributing Guide 🌟
2
+
3
+ We're thrilled that you want to contribute to Lobe Chat, the future of communication! 😄
4
+
5
+ Lobe Chat is an open-source project, and we welcome your collaboration. Before you jump in, let's make sure you're all set to contribute effectively and have loads of fun along the way!
6
+
7
+ ## Table of Contents
8
+
9
+ - [Fork the Repository](#fork-the-repository)
10
+ - [Clone Your Fork](#clone-your-fork)
11
+ - [Create a New Branch](#create-a-new-branch)
12
+ - [Code Like a Wizard](#code-like-a-wizard)
13
+ - [Committing Your Work](#committing-your-work)
14
+ - [Sync with Upstream](#sync-with-upstream)
15
+ - [Open a Pull Request](#open-a-pull-request)
16
+ - [Review and Collaboration](#review-and-collaboration)
17
+ - [Celebrate 🎉](#celebrate-)
18
+
19
+ ## Fork the Repository
20
+
21
+ 🍴 Fork this repository to your GitHub account by clicking the "Fork" button at the top right. This creates a personal copy of the project you can work on.
22
+
23
+ ## Clone Your Fork
24
+
25
+ 📦 Clone your forked repository to your local machine using the `git clone` command:
26
+
27
+ ```bash
28
+ git clone https://github.com/YourUsername/lobe-chat.git
29
+ ```
30
+
31
+ ## Create a New Branch
32
+
33
+ 🌿 Create a new branch for your contribution. This helps keep your work organized and separate from the main codebase.
34
+
35
+ ```bash
36
+ git checkout -b your-branch-name
37
+ ```
38
+
39
+ Choose a meaningful branch name related to your work. It makes collaboration easier!
40
+
41
+ ## Code Like a Wizard
42
+
43
+ 🧙‍♀️ Time to work your magic! Write your code, fix bugs, or add new features. Be sure to follow our project's coding style. You can check if your code adheres to our style using:
44
+
45
+ ```bash
46
+ yarn lint
47
+ ```
48
+
49
+ This adds a bit of enchantment to your coding experience! ✨
50
+
51
+ ## Committing Your Work
52
+
53
+ 📝 Ready to save your progress? Commit your changes to your branch.
54
+
55
+ ```bash
56
+ git add .
57
+ git commit -m "Your meaningful commit message"
58
+ ```
59
+
60
+ Please keep your commits focused and clear. And remember to be kind to your fellow contributors; keep your commits concise.
61
+
62
+ ## Sync with Upstream
63
+
64
+ ⚙️ Periodically, sync your forked repository with the original (upstream) repository to stay up-to-date with the latest changes.
65
+
66
+ ```bash
67
+ git remote add upstream https://github.com/lobehub/lobe-chat.git
68
+ git fetch upstream
69
+ git merge upstream/main
70
+ ```
71
+
72
+ This ensures you're working on the most current version of Lobe Chat. Stay fresh! 💨
73
+
74
+ ## Open a Pull Request
75
+
76
+ 🚀 Time to share your contribution! Head over to the original Lobe Chat repository and open a Pull Request (PR). Our maintainers will review your work.
77
+
78
+ ## Review and Collaboration
79
+
80
+ 👓 Your PR will undergo thorough review and testing. The maintainers will provide feedback, and you can collaborate to make your contribution even better. We value teamwork!
81
+
82
+ ## Celebrate 🎉
83
+
84
+ 🎈 Congratulations! Your contribution is now part of Lobe Chat. 🥳
85
+
86
+ Thank you for making Lobe Chat even more magical. We can't wait to see what you create! 🌠
87
+
88
+ Happy Coding! 🚀🦄
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.94.4",
3
+ "version": "0.95.0",
4
4
  "description": "An open-source, extensible (Function Calling), high-performance chatbot framework. It supports one-click free deployment of your private ChatGPT/LLM web application",
5
5
  "keywords": [
6
6
  "chatbot",
@@ -76,7 +76,7 @@
76
76
  "ahooks": "^3",
77
77
  "ai": "^2",
78
78
  "antd": "^5",
79
- "antd-style": "^3.5",
79
+ "antd-style": "^3",
80
80
  "brotli-wasm": "^1",
81
81
  "chroma-js": "^2",
82
82
  "copy-to-clipboard": "^3",
@@ -122,7 +122,7 @@
122
122
  "zustand-utils": "^1"
123
123
  },
124
124
  "devDependencies": {
125
- "@commitlint/cli": "^17",
125
+ "@commitlint/cli": "^18",
126
126
  "@lobehub/i18n-cli": "latest",
127
127
  "@lobehub/lint": "latest",
128
128
  "@next/bundle-analyzer": "^13",
@@ -141,7 +141,7 @@
141
141
  "@types/uuid": "^9",
142
142
  "@umijs/lint": "^4",
143
143
  "@vitest/coverage-v8": "latest",
144
- "commitlint": "^17",
144
+ "commitlint": "^18",
145
145
  "consola": "^3",
146
146
  "dpdm": "^3",
147
147
  "eslint": "^8",
@@ -15,7 +15,6 @@ const ChatInputDesktopLayout = memo(() => {
15
15
 
16
16
  return (
17
17
  <DraggablePanel
18
- expandable={false}
19
18
  fullscreen={expand}
20
19
  headerHeight={HEADER_HEIGHT}
21
20
  minHeight={CHAT_TEXTAREA_HEIGHT}
@@ -36,7 +36,7 @@ const PluginSettings: RenderErrorMessage = memo(({ id, plugin }) => {
36
36
  <Flexbox className={styles.desc}>{t('pluginSettings.desc')}</Flexbox>
37
37
  <Divider style={{ margin: '0 16px' }} />
38
38
  {manifest.settings && (
39
- <PluginSettingsConfig id={manifest.identifier} settings={manifest.settings} />
39
+ <PluginSettingsConfig id={manifest.identifier} schema={manifest.settings} />
40
40
  )}
41
41
  <Button
42
42
  block
@@ -3,23 +3,20 @@ import { memo } from 'react';
3
3
 
4
4
  import { useSessionStore } from '@/store/session';
5
5
  import { chatSelectors } from '@/store/session/selectors';
6
- import { isFunctionMessage } from '@/utils/message';
6
+ import { isFunctionMessageAtStart } from '@/utils/message';
7
7
 
8
- import FunctionCall from '../Plugins/FunctionCall';
8
+ import Inspector from '../Plugins/Inspector';
9
9
  import { DefaultMessage } from './Default';
10
10
 
11
- export const AssistantMessage: RenderMessage = memo(
12
- ({ id, plugin, function_call, content, ...props }) => {
13
- const genFunctionCallProps = useSessionStore(chatSelectors.getFunctionMessageParams);
11
+ export const AssistantMessage: RenderMessage = memo(({ id, plugin, content, ...props }) => {
12
+ const fcProps = useSessionStore(chatSelectors.getFunctionMessageProps({ content, id, plugin }));
14
13
 
15
- if (!isFunctionMessage(content)) return <DefaultMessage content={content} id={id} {...props} />;
14
+ if (!isFunctionMessageAtStart(content))
15
+ return <DefaultMessage content={content} id={id} {...props} />;
16
16
 
17
- const fcProps = genFunctionCallProps({ content, function_call, id, plugin });
18
-
19
- return (
20
- <div id={id}>
21
- <FunctionCall {...fcProps} />
22
- </div>
23
- );
24
- },
25
- );
17
+ return (
18
+ <div id={id}>
19
+ <Inspector {...fcProps} />
20
+ </div>
21
+ );
22
+ });
@@ -1,23 +1,34 @@
1
1
  import { RenderMessage } from '@lobehub/ui';
2
- import { memo } from 'react';
2
+ import isEqual from 'fast-deep-equal';
3
+ import { memo, useState } from 'react';
3
4
  import { Flexbox } from 'react-layout-kit';
4
5
 
5
6
  import { useSessionStore } from '@/store/session';
6
7
  import { chatSelectors } from '@/store/session/selectors';
7
8
 
8
- import FunctionCall from '../Plugins/FunctionCall';
9
- import PluginMessage from '../Plugins/PluginMessage';
9
+ import Inspector from '../Plugins/Inspector';
10
+ import PluginRender from '../Plugins/Render';
10
11
 
11
- export const FunctionMessage: RenderMessage = memo(
12
- ({ id, content, plugin, function_call, name }) => {
13
- const genFunctionCallProps = useSessionStore(chatSelectors.getFunctionMessageParams);
14
- const fcProps = genFunctionCallProps({ content, function_call, id, plugin });
12
+ export const FunctionMessage: RenderMessage = memo(({ id, content, plugin, name }) => {
13
+ const fcProps = useSessionStore(
14
+ chatSelectors.getFunctionMessageProps({ content, id, plugin }),
15
+ isEqual,
16
+ );
17
+ const [showRender, setShow] = useState(true);
15
18
 
16
- return (
17
- <Flexbox gap={12} id={id}>
18
- <FunctionCall {...fcProps} />
19
- <PluginMessage content={content} loading={fcProps.loading} name={name} />
20
- </Flexbox>
21
- );
22
- },
23
- );
19
+ return (
20
+ <Flexbox gap={12} id={id}>
21
+ <Inspector showRender={showRender} {...fcProps} setShow={setShow} />
22
+ {showRender && (
23
+ <PluginRender
24
+ content={content}
25
+ id={id}
26
+ loading={fcProps.loading}
27
+ name={name}
28
+ payload={fcProps.command}
29
+ type={fcProps.type}
30
+ />
31
+ )}
32
+ </Flexbox>
33
+ );
34
+ });
@@ -0,0 +1,36 @@
1
+ import { ActionIcon } from '@lobehub/ui';
2
+ import { LucideSettings } from 'lucide-react';
3
+ import { memo, useState } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import PluginSettingsModal from '@/features/PluginSettingsModal';
7
+ import { pluginSelectors, usePluginStore } from '@/store/plugin';
8
+
9
+ const Settings = memo<{ id: string }>(({ id }) => {
10
+ const item = usePluginStore(pluginSelectors.getPluginManifestById(id));
11
+ const [open, setOpen] = useState(false);
12
+ const { t } = useTranslation('plugin');
13
+ return (
14
+ item?.settings && (
15
+ <>
16
+ <ActionIcon
17
+ icon={LucideSettings}
18
+ onClick={() => {
19
+ setOpen(true);
20
+ }}
21
+ title={t('setting')}
22
+ />
23
+ <PluginSettingsModal
24
+ id={id}
25
+ onClose={() => {
26
+ setOpen(false);
27
+ }}
28
+ open={open}
29
+ schema={item.settings}
30
+ />
31
+ </>
32
+ )
33
+ );
34
+ });
35
+
36
+ export default Settings;
@@ -1,26 +1,40 @@
1
1
  import { LoadingOutlined } from '@ant-design/icons';
2
- import { Avatar, Highlighter, Icon } from '@lobehub/ui';
2
+ import { LobePluginType } from '@lobehub/chat-plugin-sdk';
3
+ import { ActionIcon, Avatar, Highlighter, Icon } from '@lobehub/ui';
3
4
  import { Tabs } from 'antd';
4
- import { LucideChevronDown, LucideChevronUp, LucideToyBrick } from 'lucide-react';
5
+ import { LucideChevronDown, LucideChevronUp, LucideOrbit, LucideToyBrick } from 'lucide-react';
5
6
  import { memo, useState } from 'react';
6
7
  import { useTranslation } from 'react-i18next';
7
8
  import { Flexbox } from 'react-layout-kit';
8
9
 
9
10
  import { pluginHelpers, pluginSelectors, usePluginStore } from '@/store/plugin';
10
11
 
11
- import PluginResult from './PluginResultRender';
12
+ import PluginResult from './PluginResultJSON';
13
+ import Settings from './Settings';
12
14
  import { useStyles } from './style';
13
15
 
14
- export interface FunctionCallProps {
16
+ export interface InspectorProps {
15
17
  arguments?: string;
16
18
  command?: any;
17
19
  content: string;
18
20
  id?: string;
19
21
  loading?: boolean;
22
+ setShow?: (showRender: boolean) => void;
23
+ showRender?: boolean;
24
+ type?: LobePluginType;
20
25
  }
21
26
 
22
- const FunctionCall = memo<FunctionCallProps>(
23
- ({ arguments: requestArgs = '{}', command, loading, content, id = 'unknown' }) => {
27
+ const Inspector = memo<InspectorProps>(
28
+ ({
29
+ arguments: requestArgs = '{}',
30
+ command,
31
+ showRender,
32
+ loading,
33
+ setShow,
34
+ content,
35
+ id = 'unknown',
36
+ type,
37
+ }) => {
24
38
  const { t } = useTranslation('plugin');
25
39
  const { styles } = useStyles();
26
40
  const [open, setOpen] = useState(false);
@@ -41,24 +55,38 @@ const FunctionCall = memo<FunctionCallProps>(
41
55
 
42
56
  return (
43
57
  <Flexbox gap={8}>
44
- <Flexbox
45
- align={'center'}
46
- className={styles.container}
47
- gap={8}
48
- horizontal
49
- onClick={() => {
50
- setOpen(!open);
51
- }}
52
- >
53
- {loading ? (
54
- <div>
55
- <LoadingOutlined />
56
- </div>
57
- ) : (
58
- avatar
59
- )}
60
- {pluginTitle ?? t('plugins.unknown')}
61
- <Icon icon={open ? LucideChevronUp : LucideChevronDown} />
58
+ <Flexbox align={'center'} distribution={'space-between'} gap={24} horizontal>
59
+ <Flexbox
60
+ align={'center'}
61
+ className={styles.container}
62
+ gap={8}
63
+ horizontal
64
+ onClick={() => {
65
+ setOpen(!open);
66
+ }}
67
+ >
68
+ {loading ? (
69
+ <div>
70
+ <LoadingOutlined />
71
+ </div>
72
+ ) : (
73
+ avatar
74
+ )}
75
+ {pluginTitle ?? t('plugins.unknown')}
76
+ <Icon icon={open ? LucideChevronUp : LucideChevronDown} />
77
+ </Flexbox>
78
+ <Flexbox horizontal>
79
+ {type === 'standalone' && <ActionIcon icon={LucideOrbit} />}
80
+ <Settings id={id} />
81
+ {setShow && (
82
+ <ActionIcon
83
+ icon={showRender ? LucideChevronUp : LucideChevronDown}
84
+ onClick={() => {
85
+ setShow(!showRender);
86
+ }}
87
+ />
88
+ )}
89
+ </Flexbox>
62
90
  </Flexbox>
63
91
  {open && (
64
92
  <Tabs
@@ -87,4 +115,4 @@ const FunctionCall = memo<FunctionCallProps>(
87
115
  },
88
116
  );
89
117
 
90
- export default FunctionCall;
118
+ export default Inspector;
@@ -1,9 +1,9 @@
1
1
  import { PluginRenderProps } from '@lobehub/chat-plugin-sdk/client';
2
2
  import { Skeleton } from 'antd';
3
- import { memo, useEffect, useRef, useState } from 'react';
3
+ import { memo, useRef, useState } from 'react';
4
4
 
5
- import { useOnPluginFetchMessage, useOnPluginReady } from './hooks';
6
- import { sendMessageToPlugin } from './utils';
5
+ import { useOnPluginReadyForInteraction } from '../../utils/iframeOnReady';
6
+ import { sendMessageToPlugin } from '../../utils/postMessage';
7
7
 
8
8
  interface IFrameRenderProps extends PluginRenderProps {
9
9
  height?: number;
@@ -12,24 +12,13 @@ interface IFrameRenderProps extends PluginRenderProps {
12
12
  }
13
13
 
14
14
  const IFrameRender = memo<IFrameRenderProps>(({ url, width = 800, height = 300, ...props }) => {
15
- const [loading, setLoading] = useState(true);
16
- const [readyForRender, setReady] = useState(false);
17
15
  const iframeRef = useRef<HTMLIFrameElement>(null);
18
-
19
- useOnPluginReady(() => setReady(true));
16
+ const [loading, setLoading] = useState(true);
20
17
 
21
18
  // 当 props 发生变化时,主动向 iframe 发送数据
22
- useEffect(() => {
19
+ useOnPluginReadyForInteraction(() => {
23
20
  const iframeWin = iframeRef.current?.contentWindow;
24
21
 
25
- if (iframeWin && readyForRender) {
26
- sendMessageToPlugin(iframeWin, props);
27
- }
28
- }, [readyForRender, props]);
29
-
30
- // 当接收到来自 iframe 的请求时,触发发送数据
31
- useOnPluginFetchMessage(() => {
32
- const iframeWin = iframeRef.current?.contentWindow;
33
22
  if (iframeWin) {
34
23
  sendMessageToPlugin(iframeWin, props);
35
24
  }
@@ -11,13 +11,13 @@ import IFrameRender from './IFrameRender';
11
11
 
12
12
  const SystemJsRender = dynamic(() => import('./SystemJsRender'), { ssr: false });
13
13
 
14
- export interface FunctionMessageProps {
14
+ export interface PluginDefaultTypeProps {
15
15
  content: string;
16
16
  loading?: boolean;
17
17
  name?: string;
18
18
  }
19
19
 
20
- const PluginMessage = memo<FunctionMessageProps>(({ content, name }) => {
20
+ const PluginDefaultType = memo<PluginDefaultTypeProps>(({ content, name }) => {
21
21
  const { t } = useTranslation('plugin');
22
22
 
23
23
  const manifest = usePluginStore((s) => s.pluginManifestMap[name || '']);
@@ -65,4 +65,4 @@ const PluginMessage = memo<FunctionMessageProps>(({ content, name }) => {
65
65
  );
66
66
  });
67
67
 
68
- export default PluginMessage;
68
+ export default PluginDefaultType;
@@ -0,0 +1,136 @@
1
+ import { PluginRequestPayload } from '@lobehub/chat-plugin-sdk';
2
+ import { Skeleton } from 'antd';
3
+ import { memo, useRef, useState } from 'react';
4
+
5
+ import { useOnPluginSettingsUpdate } from '@/app/chat/features/Conversation/ChatList/Plugins/Render/utils/pluginSettings';
6
+ import { useOnPluginStateUpdate } from '@/app/chat/features/Conversation/ChatList/Plugins/Render/utils/pluginState';
7
+ import { pluginSelectors, usePluginStore } from '@/store/plugin';
8
+ import { useSessionStore } from '@/store/session';
9
+ import { chatSelectors } from '@/store/session/selectors';
10
+
11
+ import { useOnPluginReadyForInteraction } from '../utils/iframeOnReady';
12
+ import {
13
+ useOnPluginFetchMessage,
14
+ useOnPluginFetchPluginSettings,
15
+ useOnPluginFetchPluginState,
16
+ useOnPluginFillContent,
17
+ } from '../utils/listenToPlugin';
18
+ import {
19
+ sendMessageToPlugin,
20
+ sendPayloadToPlugin,
21
+ sendPluginSettingsToPlugin,
22
+ sendPluginStateToPlugin,
23
+ } from '../utils/postMessage';
24
+
25
+ interface IFrameRenderProps {
26
+ height?: number;
27
+ id: string;
28
+ payload?: PluginRequestPayload;
29
+ url: string;
30
+ width?: number;
31
+ }
32
+
33
+ const IFrameRender = memo<IFrameRenderProps>(({ url, id, payload, width = 600, height = 300 }) => {
34
+ const [loading, setLoading] = useState(true);
35
+
36
+ const iframeRef = useRef<HTMLIFrameElement>(null);
37
+
38
+ // when payload change,send content to plugin
39
+ useOnPluginReadyForInteraction(() => {
40
+ const iframeWin = iframeRef.current?.contentWindow;
41
+ if (iframeWin && payload) {
42
+ sendPayloadToPlugin(iframeWin, payload);
43
+ }
44
+ }, [payload]);
45
+
46
+ // when plugin wants to get message content, send it to plugin
47
+ useOnPluginFetchMessage(() => {
48
+ const iframeWin = iframeRef.current?.contentWindow;
49
+
50
+ if (iframeWin) {
51
+ const message = chatSelectors.getMessageById(id)(useSessionStore.getState());
52
+ if (!message) return;
53
+ const props = { content: '' };
54
+
55
+ try {
56
+ props.content = JSON.parse(message.content || '{}');
57
+ } catch {
58
+ props.content = message.content || '';
59
+ }
60
+
61
+ sendMessageToPlugin(iframeWin, props);
62
+ }
63
+ }, []);
64
+
65
+ // when plugin try to send back message, we should fill it to the message content
66
+ const fillPluginContent = useSessionStore((s) => s.fillPluginMessageContent);
67
+ useOnPluginFillContent((content) => {
68
+ fillPluginContent(id, content);
69
+ });
70
+
71
+ // when plugin wants to get plugin state, send it to plugin
72
+ useOnPluginFetchPluginState((key) => {
73
+ const iframeWin = iframeRef.current?.contentWindow;
74
+
75
+ if (iframeWin) {
76
+ const message = chatSelectors.getMessageById(id)(useSessionStore.getState());
77
+ if (!message) return;
78
+
79
+ sendPluginStateToPlugin(iframeWin, key, message.pluginState?.[key]);
80
+ }
81
+ });
82
+
83
+ // when plugin update state, we should update it to the message pluginState key
84
+ const updatePluginState = useSessionStore((s) => s.updatePluginState);
85
+ useOnPluginStateUpdate((key, value) => {
86
+ updatePluginState(id, key, value);
87
+ });
88
+
89
+ // when plugin wants to get plugin settings, send it to plugin
90
+ useOnPluginFetchPluginSettings(() => {
91
+ const iframeWin = iframeRef.current?.contentWindow;
92
+
93
+ if (iframeWin) {
94
+ if (!payload?.identifier) return;
95
+
96
+ const settings = pluginSelectors.getPluginSettingsById(payload?.identifier)(
97
+ usePluginStore.getState(),
98
+ );
99
+
100
+ sendPluginSettingsToPlugin(iframeWin, settings);
101
+ }
102
+ });
103
+
104
+ // when plugin update settings, we should update it to the plugin settings
105
+ const updatePluginSettings = usePluginStore((s) => s.updatePluginSettings);
106
+ useOnPluginSettingsUpdate((value) => {
107
+ if (!payload?.identifier) return;
108
+
109
+ updatePluginSettings(payload?.identifier, value);
110
+ });
111
+
112
+ return (
113
+ <>
114
+ {loading && <Skeleton active style={{ width }} />}
115
+ <iframe
116
+ // @ts-ignore
117
+ allowtransparency="true"
118
+ height={height}
119
+ hidden={loading}
120
+ onLoad={() => {
121
+ setLoading(false);
122
+ }}
123
+ ref={iframeRef}
124
+ src={url}
125
+ style={{
126
+ border: 0,
127
+ // iframe 在 color-scheme:dark 模式下无法透明
128
+ // refs: https://www.jianshu.com/p/bc5a37bb6a7b
129
+ colorScheme: 'light',
130
+ }}
131
+ width={width}
132
+ />
133
+ </>
134
+ );
135
+ });
136
+ export default IFrameRender;