@lobehub/lobehub 2.0.0-next.94 → 2.0.0-next.96

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 (63) hide show
  1. package/.github/workflows/issue-auto-comments.yml +0 -19
  2. package/CHANGELOG.md +50 -0
  3. package/changelog/v1.json +18 -0
  4. package/locales/ar/common.json +21 -0
  5. package/locales/ar/hotkey.json +4 -0
  6. package/locales/bg-BG/common.json +21 -0
  7. package/locales/bg-BG/hotkey.json +4 -0
  8. package/locales/de-DE/common.json +21 -0
  9. package/locales/de-DE/hotkey.json +4 -0
  10. package/locales/en-US/common.json +21 -0
  11. package/locales/en-US/hotkey.json +4 -0
  12. package/locales/es-ES/common.json +21 -0
  13. package/locales/es-ES/hotkey.json +4 -0
  14. package/locales/fa-IR/common.json +21 -0
  15. package/locales/fa-IR/hotkey.json +4 -0
  16. package/locales/fr-FR/common.json +21 -0
  17. package/locales/fr-FR/hotkey.json +4 -0
  18. package/locales/it-IT/common.json +21 -0
  19. package/locales/it-IT/hotkey.json +4 -0
  20. package/locales/ja-JP/common.json +21 -0
  21. package/locales/ja-JP/hotkey.json +4 -0
  22. package/locales/ko-KR/common.json +21 -0
  23. package/locales/ko-KR/hotkey.json +4 -0
  24. package/locales/nl-NL/common.json +21 -0
  25. package/locales/nl-NL/hotkey.json +4 -0
  26. package/locales/pl-PL/common.json +21 -0
  27. package/locales/pl-PL/hotkey.json +4 -0
  28. package/locales/pt-BR/common.json +21 -0
  29. package/locales/pt-BR/hotkey.json +4 -0
  30. package/locales/ru-RU/common.json +21 -0
  31. package/locales/ru-RU/hotkey.json +4 -0
  32. package/locales/tr-TR/common.json +21 -0
  33. package/locales/tr-TR/hotkey.json +4 -0
  34. package/locales/vi-VN/common.json +21 -0
  35. package/locales/vi-VN/hotkey.json +4 -0
  36. package/locales/zh-CN/common.json +21 -0
  37. package/locales/zh-CN/hotkey.json +4 -0
  38. package/locales/zh-TW/common.json +21 -0
  39. package/locales/zh-TW/hotkey.json +4 -0
  40. package/package.json +3 -1
  41. package/packages/agent-runtime/src/core/InterventionChecker.ts +85 -0
  42. package/packages/agent-runtime/src/core/__tests__/InterventionChecker.test.ts +492 -22
  43. package/packages/agent-runtime/src/core/defaultSecurityBlacklist.ts +335 -0
  44. package/packages/agent-runtime/src/core/index.ts +1 -0
  45. package/packages/agent-runtime/src/types/state.ts +10 -1
  46. package/packages/const/src/hotkeys.ts +6 -0
  47. package/packages/conversation-flow/src/__tests__/indexing.test.ts +513 -0
  48. package/packages/conversation-flow/src/__tests__/structuring.test.ts +600 -0
  49. package/packages/types/src/hotkey.ts +1 -0
  50. package/packages/types/src/tool/intervention.ts +38 -0
  51. package/src/app/[variants]/(main)/settings/_layout/Desktop/index.tsx +41 -8
  52. package/src/app/[variants]/(main)/settings/provider/(list)/ProviderGrid/Card.tsx +6 -4
  53. package/src/app/[variants]/(main)/settings/provider/(list)/ProviderGrid/index.tsx +16 -4
  54. package/src/app/[variants]/(main)/settings/provider/(list)/index.tsx +15 -3
  55. package/src/app/[variants]/(main)/settings/provider/detail/index.tsx +23 -15
  56. package/src/features/Conversation/MarkdownElements/remarkPlugins/createRemarkSelfClosingTagPlugin.test.ts +25 -0
  57. package/src/features/Conversation/MarkdownElements/remarkPlugins/createRemarkSelfClosingTagPlugin.ts +28 -0
  58. package/src/layout/GlobalProvider/Cmdk.tsx +470 -0
  59. package/src/layout/GlobalProvider/CmdkLazy.tsx +17 -0
  60. package/src/layout/GlobalProvider/index.tsx +2 -0
  61. package/src/locales/default/common.ts +21 -0
  62. package/src/locales/default/hotkey.ts +4 -0
  63. package/src/store/chat/agents/GeneralChatAgent.ts +22 -8
@@ -1,11 +1,11 @@
1
1
  'use client';
2
2
 
3
3
  import { useResponsive, useTheme } from 'antd-style';
4
- import { memo, useRef } from 'react';
4
+ import { memo, useEffect, useRef } from 'react';
5
5
  import { Flexbox } from 'react-layout-kit';
6
+ import { useSearchParams } from 'react-router-dom';
6
7
 
7
8
  import SettingContainer from '@/features/Setting/SettingContainer';
8
- import { parseAsStringEnum, useQueryParam } from '@/hooks/useQueryParam';
9
9
  import { SettingsTabs } from '@/store/global/initialState';
10
10
 
11
11
  import CategoryContent from '../CategoryContent';
@@ -13,18 +13,51 @@ import SettingsContent from '../SettingsContent';
13
13
  import { LayoutProps } from '../type';
14
14
  import Header from './Header';
15
15
  import SideBar from './SideBar';
16
+ import useMergedState from 'rc-util/lib/hooks/useMergedState';
16
17
 
17
18
  const Layout = memo<LayoutProps>(() => {
18
19
  const ref = useRef<HTMLDivElement | null>(null);
19
20
  const { md = true } = useResponsive();
20
21
  const theme = useTheme();
21
22
 
22
- const [activeTab, setActiveTab] = useQueryParam(
23
- 'active',
24
- parseAsStringEnum(Object.values(SettingsTabs)).withDefault(SettingsTabs.Common),
25
- );
23
+ const [searchParams, setSearchParams] = useSearchParams();
24
+
25
+ const [activeTabState, setActiveTabState] = useMergedState({
26
+ active: searchParams.get('active') as SettingsTabs ? searchParams.get('active') as SettingsTabs : SettingsTabs.Common,
27
+ }, {
28
+ onChange: (obj: {
29
+ active: SettingsTabs;
30
+ provider?: string;
31
+ }) => {
32
+ if (obj.provider) {
33
+ setSearchParams({ active: obj.active, provider: obj.provider });
34
+ } else {
35
+ searchParams.delete('provider');
36
+ setSearchParams({ active: obj.active });
37
+ }
38
+ },
39
+ });
40
+
41
+ const setActiveTab = (tab: SettingsTabs) => {
42
+ if (tab === SettingsTabs.Provider) {
43
+ setActiveTabState({ active: tab, provider: 'all' });
44
+ } else {
45
+ setActiveTabState({
46
+ active: tab,
47
+ });
48
+ }
49
+ };
50
+
51
+ useEffect(() => {
52
+ return () => {
53
+ setSearchParams((prevParams) => {
54
+ prevParams.delete('active');
55
+ return prevParams;
56
+ });
57
+ };
58
+ }, []);
26
59
 
27
- const category = <CategoryContent activeTab={activeTab} onMenuSelect={setActiveTab} />;
60
+ const category = <CategoryContent activeTab={activeTabState.active} onMenuSelect={setActiveTab} />;
28
61
 
29
62
  return (
30
63
  <Flexbox
@@ -39,7 +72,7 @@ const Layout = memo<LayoutProps>(() => {
39
72
  <Header getContainer={() => ref.current!}>{category}</Header>
40
73
  )}
41
74
  <SettingContainer maxWidth={'none'}>
42
- <SettingsContent activeTab={activeTab} mobile={false} />
75
+ <SettingsContent activeTab={activeTabState.active} mobile={false} />
43
76
  </SettingContainer>
44
77
  </Flexbox>
45
78
  );
@@ -5,7 +5,6 @@ import { memo } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
  import { Flexbox } from 'react-layout-kit';
7
7
 
8
- import Link from '@/components/Link';
9
8
  import { AiProviderListItem } from '@/types/aiProvider';
10
9
 
11
10
  import EnableSwitch from './EnableSwitch';
@@ -13,9 +12,10 @@ import { useStyles } from './style';
13
12
 
14
13
  interface ProviderCardProps extends AiProviderListItem {
15
14
  loading?: boolean;
15
+ onProviderSelect: (provider: string) => void;
16
16
  }
17
17
  const ProviderCard = memo<ProviderCardProps>(
18
- ({ id, description, name, enabled, source, logo, loading }) => {
18
+ ({ id, description, name, enabled, source, logo, loading, onProviderSelect }) => {
19
19
  const { t } = useTranslation('providers');
20
20
  const { cx, styles, theme } = useStyles();
21
21
 
@@ -33,7 +33,9 @@ const ProviderCard = memo<ProviderCardProps>(
33
33
  return (
34
34
  <Flexbox className={cx(styles.container)} gap={24}>
35
35
  <Flexbox gap={12} padding={16} width={'100%'}>
36
- <Link href={`/settings?active=provider&provider=${id}`}>
36
+ <div onClick={() => {
37
+ onProviderSelect(id);
38
+ }} style={{ cursor: 'pointer' }}>
37
39
  <Flexbox gap={12} width={'100%'}>
38
40
  <Flexbox align={'center'} horizontal justify={'space-between'}>
39
41
  {source === 'builtin' ? (
@@ -68,7 +70,7 @@ const ProviderCard = memo<ProviderCardProps>(
68
70
  {source === 'custom' ? description : t(`${id}.description`)}
69
71
  </Text>
70
72
  </Flexbox>
71
- </Link>
73
+ </div>
72
74
  <Divider style={{ margin: '4px 0' }} />
73
75
  <Flexbox horizontal justify={'space-between'}>
74
76
  <div />
@@ -14,7 +14,12 @@ const loadingArr = Array.from({ length: 12 })
14
14
  .fill('-')
15
15
  .map((item, index) => `${index}x${item}`);
16
16
 
17
- const List = memo(() => {
17
+ type ListProps = {
18
+ onProviderSelect: (provider: string) => void;
19
+ };
20
+
21
+ const List = memo((props: ListProps) => {
22
+ const { onProviderSelect } = props;
18
23
  const { t } = useTranslation('modelProvider');
19
24
  const enabledList = useAiInfraStore(aiProviderSelectors.enabledAiProviderList, isEqual);
20
25
  const disabledList = useAiInfraStore(aiProviderSelectors.disabledAiProviderList, isEqual);
@@ -30,7 +35,14 @@ const List = memo(() => {
30
35
  </Flexbox>
31
36
  <Grid gap={16} rows={3}>
32
37
  {loadingArr.map((item) => (
33
- <Card enabled={false} id={item} key={item} loading source={'builtin'} />
38
+ <Card
39
+ enabled={false}
40
+ id={item}
41
+ key={item}
42
+ loading
43
+ onProviderSelect={onProviderSelect}
44
+ source={'builtin'}
45
+ />
34
46
  ))}
35
47
  </Grid>
36
48
  </Flexbox>
@@ -47,7 +59,7 @@ const List = memo(() => {
47
59
  </Flexbox>
48
60
  <Grid gap={16} rows={3}>
49
61
  {enabledList.map((item) => (
50
- <Card {...item} key={item.id} />
62
+ <Card {...item} key={item.id} onProviderSelect={onProviderSelect} />
51
63
  ))}
52
64
  </Grid>
53
65
  </Flexbox>
@@ -60,7 +72,7 @@ const List = memo(() => {
60
72
  </Flexbox>
61
73
  <Grid gap={16} rows={3}>
62
74
  {disabledList.map((item) => (
63
- <Card {...item} key={item.id} />
75
+ <Card {...item} key={item.id} onProviderSelect={onProviderSelect} />
64
76
  ))}
65
77
  </Grid>
66
78
  </Flexbox>
@@ -1,20 +1,32 @@
1
1
  'use client';
2
2
 
3
3
  import { isCustomBranding } from '@/const/version';
4
- import { parseAsString, useQueryParam } from '@/hooks/useQueryParam';
5
4
 
6
5
  import DesktopLayout from '../_layout/Desktop';
7
6
  import MobileLayout from '../_layout/Mobile';
8
7
  import ProviderDetailPage from '../detail';
9
8
  import Footer from './Footer';
9
+ import { useMemo, useState } from 'react';
10
+ import { useSearchParams } from 'react-router-dom';
10
11
 
11
12
  const Page = (props: { mobile?: boolean }) => {
12
- const [Provider, setProvider] = useQueryParam('provider', parseAsString.withDefault('all'));
13
+ const [SearchParams, setSearchParams] = useSearchParams();
14
+ const [provider, setProviderState] = useState(SearchParams.get('provider') || 'all');
15
+ const setProvider = (provider: string) => {
16
+ setSearchParams({ active: 'provider', provider });
17
+ setProviderState(provider);
18
+ };
19
+
13
20
  const { mobile } = props;
14
21
  const ProviderLayout = mobile ? MobileLayout : DesktopLayout;
22
+
23
+ const ProviderListPage = useMemo(() => {
24
+ return <ProviderDetailPage id={provider} onProviderSelect={setProvider} />;
25
+ }, [provider]);
26
+
15
27
  return (
16
28
  <ProviderLayout onProviderSelect={setProvider}>
17
- <ProviderDetailPage id={Provider} />
29
+ {ProviderListPage}
18
30
  {!isCustomBranding && <Footer />}
19
31
  </ProviderLayout>
20
32
  );
@@ -1,22 +1,30 @@
1
- import NewAPI from './newapi';
2
- import ProviderGrid from '../(list)/ProviderGrid';
3
- import Azure from './azure';
4
- import AzureAI from './azureai';
5
- import Bedrock from './bedrock';
6
- import Cloudflare from './cloudflare';
7
- import ComfyUI from './comfyui';
8
- import DefaultPage from './default/ProviderDetialPage';
9
- import GitHub from './github';
10
- import Ollama from './ollama';
11
- import OpenAI from './openai';
12
- import VertexAI from './vertexai';
1
+ import dynamic from 'next/dynamic';
2
+ import Loading from '@/components/Loading/BrandTextLoading';
13
3
 
14
- const ProviderDetailPage = (props: { id?: string | null }) => {
15
- const { id } = props;
4
+ const NewAPI = dynamic(() => import('./newapi'), { loading: () => <Loading />, ssr: false });
5
+ const OpenAI = dynamic(() => import('./openai'), { loading: () => <Loading />, ssr: false });
6
+ const VertexAI = dynamic(() => import('./vertexai'), { loading: () => <Loading />, ssr: false });
7
+ const GitHub = dynamic(() => import('./github'), { loading: () => <Loading />, ssr: false });
8
+ const Ollama = dynamic(() => import('./ollama'), { loading: () => <Loading />, ssr: false });
9
+ const ComfyUI = dynamic(() => import('./comfyui'), { loading: () => <Loading />, ssr: false });
10
+ const Cloudflare = dynamic(() => import('./cloudflare'), { loading: () => <Loading />, ssr: false });
11
+ const Bedrock = dynamic(() => import('./bedrock'), { loading: () => <Loading />, ssr: false });
12
+ const AzureAI = dynamic(() => import('./azureai'), { loading: () => <Loading />, ssr: false });
13
+ const Azure = dynamic(() => import('./azure'), { loading: () => <Loading />, ssr: false });
14
+ const ProviderGrid = dynamic(() => import('../(list)/ProviderGrid'), { loading: () => <Loading />, ssr: false });
15
+ const DefaultPage = dynamic(() => import('./default/ProviderDetialPage'), { loading: () => <Loading />, ssr: false });
16
+
17
+ type ProviderDetailPageProps = {
18
+ id?: string | null;
19
+ onProviderSelect: (provider: string) => void;
20
+ }
21
+
22
+ const ProviderDetailPage = (props: ProviderDetailPageProps) => {
23
+ const { id, onProviderSelect } = props;
16
24
 
17
25
  switch (id) {
18
26
  case 'all': {
19
- return <ProviderGrid />;
27
+ return <ProviderGrid onProviderSelect={onProviderSelect} />;
20
28
  }
21
29
  case 'azure': {
22
30
  return <Azure />;
@@ -249,4 +249,29 @@ describe('createRemarkSelfClosingTagPlugin', () => {
249
249
 
250
250
  expect(tree).toMatchSnapshot();
251
251
  });
252
+
253
+ it('should handle tags wrapped in backticks (code)', () => {
254
+ const markdown = `Use this file: \`<${tagName} name="config.json" path="/app/config.json" />\` in your code.`;
255
+ const tree = processMarkdown(markdown, tagName);
256
+
257
+ expect(tree.children).toHaveLength(1);
258
+ expect(tree.children[0].type).toBe('paragraph');
259
+
260
+ const paragraphChildren = tree.children[0].children;
261
+ expect(paragraphChildren).toHaveLength(3);
262
+
263
+ expect(paragraphChildren[0].type).toBe('text');
264
+ expect(paragraphChildren[0].value).toBe('Use this file: ');
265
+
266
+ // The tag should be parsed even inside backticks
267
+ const tagNode = paragraphChildren[1];
268
+ expect(tagNode.type).toBe(tagName);
269
+ expect(tagNode.data?.hProperties).toEqual({
270
+ name: 'config.json',
271
+ path: '/app/config.json',
272
+ });
273
+
274
+ expect(paragraphChildren[2].type).toBe('text');
275
+ expect(paragraphChildren[2].value).toBe(' in your code.');
276
+ });
252
277
  });
@@ -130,5 +130,33 @@ export const createRemarkSelfClosingTagPlugin =
130
130
  return [SKIP, index + newChildren.length]; // Skip new nodes
131
131
  }
132
132
  });
133
+
134
+ // 3. Visit inlineCode nodes (backtick-wrapped tags like `<localFile ... />`)
135
+ // @ts-ignore
136
+ visit(tree, 'inlineCode', (node: any, index: number, parent) => {
137
+ log('>>> Visiting inlineCode node: "%s"', node.value);
138
+
139
+ if (!parent || typeof index !== 'number' || !node.value?.includes(`<${tagName}`)) {
140
+ return;
141
+ }
142
+
143
+ const match = node.value.match(exactTagRegex);
144
+ if (match) {
145
+ const [, attributesString] = match;
146
+ const properties = attributesString ? parseAttributes(attributesString.trim()) : {};
147
+
148
+ const newNode = {
149
+ data: {
150
+ hName: tagName,
151
+ hProperties: properties,
152
+ },
153
+ type: tagName,
154
+ };
155
+
156
+ log('Replacing inlineCode node at index %d with %s node: %o', index, tagName, newNode);
157
+ parent.children.splice(index, 1, newNode);
158
+ return [SKIP, index + 1];
159
+ }
160
+ });
133
161
  };
134
162
  };