diff --git a/.changelogrc.js b/.changelogrc.cjs similarity index 100% rename from .changelogrc.js rename to .changelogrc.cjs diff --git a/.commitlintrc.js b/.commitlintrc.cjs similarity index 100% rename from .commitlintrc.js rename to .commitlintrc.cjs diff --git a/.github/workflows/desktop-pr-build.yml b/.github/workflows/desktop-pr-build.yml index afc132d75e..cf83b846a3 100644 --- a/.github/workflows/desktop-pr-build.yml +++ b/.github/workflows/desktop-pr-build.yml @@ -36,7 +36,7 @@ jobs: - name: Install bun uses: oven-sh/setup-bun@v2 with: - bun-version: ${{ secrets.BUN_VERSION }} + bun-version: 1.2.23 - name: Install deps run: bun i @@ -188,7 +188,7 @@ jobs: else ARCH_SUFFIX="x64" fi - + mv latest-mac.yml "latest-mac-${ARCH_SUFFIX}.yml" echo "✅ Renamed latest-mac.yml to latest-mac-${ARCH_SUFFIX}.yml (detected: $SYSTEM_ARCH)" ls -la latest-mac-*.yml @@ -234,7 +234,7 @@ jobs: - name: Install bun uses: oven-sh/setup-bun@v2 with: - bun-version: ${{ secrets.BUN_VERSION }} + bun-version: 1.2.23 # 下载所有平台的构建产物 - name: Download artifacts diff --git a/.github/workflows/release-desktop-beta.yml b/.github/workflows/release-desktop-beta.yml index d7e5306cd4..06bf6fd693 100644 --- a/.github/workflows/release-desktop-beta.yml +++ b/.github/workflows/release-desktop-beta.yml @@ -32,7 +32,7 @@ jobs: - name: Install bun uses: oven-sh/setup-bun@v2 with: - bun-version: ${{ secrets.BUN_VERSION }} + bun-version: 1.2.23 - name: Install deps run: bun i @@ -170,7 +170,7 @@ jobs: else ARCH_SUFFIX="x64" fi - + mv latest-mac.yml "latest-mac-${ARCH_SUFFIX}.yml" echo "✅ Renamed latest-mac.yml to latest-mac-${ARCH_SUFFIX}.yml (detected: $SYSTEM_ARCH)" ls -la latest-mac-*.yml @@ -216,7 +216,7 @@ jobs: - name: Install bun uses: oven-sh/setup-bun@v2 with: - bun-version: ${{ secrets.BUN_VERSION }} + bun-version: 1.2.23 # 下载所有平台的构建产物 - name: Download artifacts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 10dad80ba8..27fcfef85c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,7 +41,7 @@ jobs: - name: Install bun uses: oven-sh/setup-bun@v2 with: - bun-version: ${{ secrets.BUN_VERSION }} + bun-version: 1.2.23 - name: Install deps run: bun i diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 61f840be23..97027e83a1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -71,7 +71,7 @@ jobs: - name: Install bun uses: oven-sh/setup-bun@v2 with: - bun-version: ${{ secrets.BUN_VERSION }} + bun-version: 1.2.23 - name: Install deps run: bun i @@ -104,7 +104,7 @@ jobs: - name: Install bun uses: oven-sh/setup-bun@v2 with: - bun-version: ${{ secrets.BUN_VERSION }} + bun-version: 1.2.23 - name: Install deps run: bun i @@ -148,7 +148,7 @@ jobs: - name: Install bun uses: oven-sh/setup-bun@v2 with: - bun-version: ${{ secrets.BUN_VERSION }} + bun-version: 1.2.23 - name: Install deps run: bun i diff --git a/.prettierrc.js b/.prettierrc.cjs similarity index 100% rename from .prettierrc.js rename to .prettierrc.cjs diff --git a/.releaserc.js b/.releaserc.cjs similarity index 100% rename from .releaserc.js rename to .releaserc.cjs diff --git a/.remarkrc.js b/.remarkrc.cjs similarity index 100% rename from .remarkrc.js rename to .remarkrc.cjs diff --git a/.stylelintrc.js b/.stylelintrc.cjs similarity index 100% rename from .stylelintrc.js rename to .stylelintrc.cjs diff --git a/packages/const/src/user.ts b/packages/const/src/user.ts index 12ec37f531..bb6a64be24 100644 --- a/packages/const/src/user.ts +++ b/packages/const/src/user.ts @@ -1,6 +1,8 @@ import { TopicDisplayMode, UserPreference } from '@lobechat/types'; export const DEFAULT_PREFERENCE: UserPreference = { + disableInputMarkdownRender: false, + enableGroupChat: false, guide: { moveSettingsToAvatar: true, topic: true, diff --git a/packages/types/src/sync.ts b/packages/types/src/sync.ts deleted file mode 100644 index dc83beccb6..0000000000 --- a/packages/types/src/sync.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { LobeDBSchemaMap } from '@/database/_deprecated/core/db'; - -export type OnSyncEvent = (tableKey: keyof LobeDBSchemaMap) => void; -export type OnSyncStatusChange = (status: PeerSyncStatus) => void; -export type OnAwarenessChange = (state: SyncAwarenessState[]) => void; - -// export type PeerSyncStatus = 'syncing' | 'synced' | 'ready' | 'unconnected'; - -export enum PeerSyncStatus { - Connecting = 'connecting', - Disabled = 'disabled', - Ready = 'ready', - Synced = 'synced', - Syncing = 'syncing', - Unconnected = 'unconnected', -} - -export interface StartDataSyncParams { - channel: { - name: string; - password?: string; - }; - onAwarenessChange: OnAwarenessChange; - onSyncEvent: OnSyncEvent; - onSyncStatusChange: OnSyncStatusChange; - signaling: string; - user: SyncUserInfo; -} - -export interface SyncUserInfo { - browser?: string; - id: string; - isMobile: boolean; - name?: string; - os?: string; -} - -export interface SyncAwarenessState extends SyncUserInfo { - clientID: number; - current: boolean; -} diff --git a/packages/types/src/user/index.ts b/packages/types/src/user/index.ts index 640745baba..bc3a21fc25 100644 --- a/packages/types/src/user/index.ts +++ b/packages/types/src/user/index.ts @@ -35,6 +35,14 @@ export const UserGuideSchema = z.object({ export type UserGuide = z.infer; export interface UserPreference { + /** + * disable markdown rendering in chat input editor + */ + disableInputMarkdownRender?: boolean; + /** + * enable multi-agent group chat mode + */ + enableGroupChat?: boolean; guide?: UserGuide; hideSyncAlert?: boolean; telemetry: boolean | null; diff --git a/src/app/[variants]/(main)/_layout/Desktop/SideBar/BottomActions.tsx b/src/app/[variants]/(main)/_layout/Desktop/SideBar/BottomActions.tsx index 31ef0af363..c7f7f8608c 100644 --- a/src/app/[variants]/(main)/_layout/Desktop/SideBar/BottomActions.tsx +++ b/src/app/[variants]/(main)/_layout/Desktop/SideBar/BottomActions.tsx @@ -1,11 +1,10 @@ import { ActionIcon, ActionIconProps } from '@lobehub/ui'; -import { Book, Github } from 'lucide-react'; +import { Github } from 'lucide-react'; import Link from 'next/link'; import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; -import { DOCUMENTS_REFER_URL, GITHUB } from '@/const/url'; +import { GITHUB } from '@/const/url'; import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig'; const ICON_SIZE: ActionIconProps['size'] = { @@ -15,8 +14,8 @@ const ICON_SIZE: ActionIconProps['size'] = { }; const BottomActions = memo(() => { - const { t } = useTranslation('common'); - const { hideGitHub, hideDocs } = useServerConfigStore(featureFlagsSelectors); + // const { t } = useTranslation('common'); + const { hideGitHub } = useServerConfigStore(featureFlagsSelectors); return ( @@ -30,16 +29,14 @@ const BottomActions = memo(() => { /> )} - {!hideDocs && ( - - - - )} + {/**/} + {/* */} + {/**/} ); }); diff --git a/src/app/[variants]/(main)/labs/components/Hero.tsx b/src/app/[variants]/(main)/labs/components/Hero.tsx new file mode 100644 index 0000000000..681d2d1234 --- /dev/null +++ b/src/app/[variants]/(main)/labs/components/Hero.tsx @@ -0,0 +1,43 @@ +'use client'; + +import { createStyles } from 'antd-style'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Flexbox } from 'react-layout-kit'; + +const useStyles = createStyles(({ css, token }) => ({ + container: css` + width: 100%; + max-width: 800px; + margin-block: 0; + margin-inline: auto; + padding-block: 24px 8px; + padding-inline: 16px; + `, + desc: css` + color: ${token.colorTextSecondary}; + `, + title: css` + font-size: 22px; + font-weight: 600; + color: ${token.colorText}; + `, +})); + +const Hero = memo(() => { + const { styles } = useStyles(); + const { t } = useTranslation('labs'); + + return ( +
+ +
🪄 {t('title')}
+
{t('desc')}
+
+
+ ); +}); + +Hero.displayName = 'LabsHero'; + +export default Hero; diff --git a/src/app/[variants]/(main)/labs/components/LabCard.tsx b/src/app/[variants]/(main)/labs/components/LabCard.tsx new file mode 100644 index 0000000000..a3d8577011 --- /dev/null +++ b/src/app/[variants]/(main)/labs/components/LabCard.tsx @@ -0,0 +1,85 @@ +'use client'; + +import { Switch } from 'antd'; +import { createStyles } from 'antd-style'; +import { PropsWithChildren, memo } from 'react'; +import { Flexbox } from 'react-layout-kit'; + +interface LabCardProps { + checked: boolean; + desc: string; + loading: boolean; + meta?: string; + onChange: (v: boolean) => void; + title: string; +} + +const useStyles = createStyles(({ css, token }) => ({ + card: css` + width: 100%; + max-width: 800px; + margin-block: 0; + margin-inline: auto; + padding: 16px; + border: 1px solid ${token.colorBorderSecondary}; + border-radius: 12px; + + background: ${token.colorBgContainer}; + `, + desc: css` + color: ${token.colorTextSecondary}; + `, + meta: css` + font-size: 12px; + color: ${token.colorTextTertiary}; + `, + row: css` + display: grid; + grid-template-columns: 240px 1fr 80px; + gap: 16px; + align-items: center; + `, + thumb: css` + height: 128px; + border-radius: ${token.borderRadiusLG}px; + background: linear-gradient(135deg, ${token.colorFillTertiary}, ${token.colorFillQuaternary}); + `, + title: css` + font-size: 16px; + font-weight: 600; + color: ${token.colorText}; + `, + wrap: css` + width: 100%; + `, +})); + +const LabCard = memo>( + ({ title, desc, checked, onChange, meta, loading }) => { + const { styles } = useStyles(); + + return ( +
+
+
+
+ +
{title}
+
{desc}
+ {meta ?
{meta}
: null} +
+ {!loading && ( + + + + )} +
+
+
+ ); + }, +); + +LabCard.displayName = 'LabCard'; + +export default LabCard; diff --git a/src/app/[variants]/(main)/labs/page.tsx b/src/app/[variants]/(main)/labs/page.tsx new file mode 100644 index 0000000000..0d6ee7f2f9 --- /dev/null +++ b/src/app/[variants]/(main)/labs/page.tsx @@ -0,0 +1,59 @@ +'use client'; + +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Flexbox } from 'react-layout-kit'; + +import { useUserStore } from '@/store/user'; +import { preferenceSelectors } from '@/store/user/selectors'; + +import Hero from './components/Hero'; +import LabCard from './components/LabCard'; + +const LabsPage = memo(() => { + const { t } = useTranslation('labs'); + + const [isPreferenceInit, inputMarkdownRender, enableGroupChat, updatePreference] = useUserStore( + (s) => [ + preferenceSelectors.isPreferenceInit(s), + preferenceSelectors.inputMarkdownRender(s), + preferenceSelectors.enableGroupChat(s), + s.updatePreference, + ], + ); + + const onToggleMarkdown = useCallback( + (checked: boolean) => updatePreference({ disableInputMarkdownRender: !checked }), + [updatePreference], + ); + const onToggleGroupChat = useCallback( + (checked: boolean) => updatePreference({ enableGroupChat: checked }), + [updatePreference], + ); + + return ( + + + + + + + + ); +}); + +LabsPage.displayName = 'LabsPage'; + +export default LabsPage; diff --git a/src/features/ChatInput/InputEditor/index.tsx b/src/features/ChatInput/InputEditor/index.tsx index f2c5c6017a..d810063294 100644 --- a/src/features/ChatInput/InputEditor/index.tsx +++ b/src/features/ChatInput/InputEditor/index.tsx @@ -12,7 +12,8 @@ import { } from '@lobehub/editor'; import { Editor, FloatMenu, SlashMenu, useEditorState } from '@lobehub/editor/react'; import { combineKeys } from '@lobehub/ui'; -import { memo, useEffect, useRef } from 'react'; +import { css, cx } from 'antd-style'; +import { memo, useEffect, useMemo, useRef } from 'react'; import { useHotkeysContext } from 'react-hotkeys-hook'; import { useUserStore } from '@/store/user'; @@ -22,6 +23,12 @@ import { useChatInputStore, useStoreApi } from '../store'; import Placeholder from './Placeholder'; import { useSlashItems } from './useSlashItems'; +const className = cx(css` + p { + margin-block-end: 0; + } +`); + const InputEditor = memo<{ defaultRows?: number }>(() => { const [editor, slashMenuRef, send, updateMarkdownContent, expand] = useChatInputStore((s) => [ s.editor, @@ -55,11 +62,40 @@ const InputEditor = memo<{ defaultRows?: number }>(() => { }; }, [state.isEmpty]); + const enableMarkdown = useUserStore(preferenceSelectors.inputMarkdownRender); + const plugins = useMemo( + () => + !enableMarkdown + ? undefined + : [ + ReactListPlugin, + ReactLinkPlugin, + ReactCodePlugin, + ReactCodeblockPlugin, + ReactHRPlugin, + ReactTablePlugin, + Editor.withProps(ReactMathPlugin, { + renderComp: expand + ? undefined + : (props) => ( + (slashMenuRef as any)?.current} + /> + ), + }), + ], + [enableMarkdown], + ); + return ( { disableScope(HotkeyEnum.AddUserMessage); }} @@ -109,21 +145,7 @@ const InputEditor = memo<{ defaultRows?: number }>(() => { } }} placeholder={} - plugins={[ - ReactListPlugin, - ReactLinkPlugin, - ReactCodePlugin, - ReactCodeblockPlugin, - ReactHRPlugin, - ReactTablePlugin, - Editor.withProps(ReactMathPlugin, { - renderComp: expand - ? undefined - : (props) => ( - (slashMenuRef as any)?.current} /> - ), - }), - ]} + plugins={plugins} slashOption={{ items: slashItems, renderComp: expand diff --git a/src/locales/default/common.ts b/src/locales/default/common.ts index 4b49f6f2ae..c4e5fca556 100644 --- a/src/locales/default/common.ts +++ b/src/locales/default/common.ts @@ -239,6 +239,7 @@ export default { }, information: '社区与资讯', installPWA: '安装浏览器应用 (PWA)', + labs: '实验室', lang: { 'ar': '阿拉伯语', 'bg-BG': '保加利亚语', diff --git a/src/locales/default/index.ts b/src/locales/default/index.ts index 203c0fa7cb..5a18a883af 100644 --- a/src/locales/default/index.ts +++ b/src/locales/default/index.ts @@ -13,6 +13,7 @@ import file from './file'; import hotkey from './hotkey'; import image from './image'; import knowledgeBase from './knowledgeBase'; +import labs from './labs'; import metadata from './metadata'; import migration from './migration'; import modelProvider from './modelProvider'; @@ -45,6 +46,7 @@ const resources = { hotkey, image, knowledgeBase, + labs, metadata, migration, modelProvider, diff --git a/src/locales/default/labs.ts b/src/locales/default/labs.ts new file mode 100644 index 0000000000..df3d20294f --- /dev/null +++ b/src/locales/default/labs.ts @@ -0,0 +1,14 @@ +export default { + desc: '这里会不定期更新我们正在探索的新功能,欢迎试用!', + features: { + groupChat: { + desc: '启用多智能体群聊编排能力。', + title: '群聊(多智能体)', + }, + inputMarkdown: { + desc: '在输入区域实时渲染 Markdown(粗体、代码块、表格等)。', + title: '输入框 Markdown 渲染', + }, + }, + title: '实验室', +}; diff --git a/src/middleware.ts b/src/middleware.ts index 15640dc6f4..cec4dbd5a2 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -33,6 +33,7 @@ export const config = { '/', '/discover', '/discover(.*)', + '/labs', '/chat', '/chat(.*)', '/changelog(.*)', diff --git a/src/store/user/slices/preference/selectors.ts b/src/store/user/slices/preference/selectors.ts index 699e35d2d3..5c41a4ba4b 100644 --- a/src/store/user/slices/preference/selectors.ts +++ b/src/store/user/slices/preference/selectors.ts @@ -21,8 +21,11 @@ const shouldTriggerFileInKnowledgeBaseTip = (s: UserStore) => const isPreferenceInit = (s: UserStore) => s.isUserStateInit; export const preferenceSelectors = { + enableGroupChat: (s: UserStore) => s.preference.enableGroupChat || false, hideSettingsMoveGuide, hideSyncAlert, + // TODO: 等到 lab 样式搞完再开启 + inputMarkdownRender: (s: UserStore) => false && !s.preference.disableInputMarkdownRender, isPreferenceInit, shouldTriggerFileInKnowledgeBaseTip, showUploadFileInKnowledgeBaseTip,