mirror of
https://github.com/lobehub/lobe-chat.git
synced 2025-12-20 01:12:52 +08:00
🐛 fix: disable rich text in markdown editor (#9637)
* clean * update * add labs * fix * improve * update * fix * hide lab * improve workflow
This commit is contained in:
6
.github/workflows/desktop-pr-build.yml
vendored
6
.github/workflows/desktop-pr-build.yml
vendored
@@ -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
|
||||
|
||||
6
.github/workflows/release-desktop-beta.yml
vendored
6
.github/workflows/release-desktop-beta.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -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
|
||||
|
||||
6
.github/workflows/test.yml
vendored
6
.github/workflows/test.yml
vendored
@@ -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
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { TopicDisplayMode, UserPreference } from '@lobechat/types';
|
||||
|
||||
export const DEFAULT_PREFERENCE: UserPreference = {
|
||||
disableInputMarkdownRender: false,
|
||||
enableGroupChat: false,
|
||||
guide: {
|
||||
moveSettingsToAvatar: true,
|
||||
topic: true,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -35,6 +35,14 @@ export const UserGuideSchema = z.object({
|
||||
export type UserGuide = z.infer<typeof UserGuideSchema>;
|
||||
|
||||
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;
|
||||
|
||||
@@ -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 (
|
||||
<Flexbox gap={8}>
|
||||
@@ -30,16 +29,14 @@ const BottomActions = memo(() => {
|
||||
/>
|
||||
</Link>
|
||||
)}
|
||||
{!hideDocs && (
|
||||
<Link aria-label={t('document')} href={DOCUMENTS_REFER_URL} target={'_blank'}>
|
||||
<ActionIcon
|
||||
icon={Book}
|
||||
size={ICON_SIZE}
|
||||
title={t('document')}
|
||||
tooltipProps={{ placement: 'right' }}
|
||||
/>
|
||||
</Link>
|
||||
)}
|
||||
{/*<Link aria-label={t('labs')} href={'/labs'}>*/}
|
||||
{/* <ActionIcon*/}
|
||||
{/* icon={FlaskConical}*/}
|
||||
{/* size={ICON_SIZE}*/}
|
||||
{/* title={t('labs')}*/}
|
||||
{/* tooltipProps={{ placement: 'right' }}*/}
|
||||
{/* />*/}
|
||||
{/*</Link>*/}
|
||||
</Flexbox>
|
||||
);
|
||||
});
|
||||
|
||||
43
src/app/[variants]/(main)/labs/components/Hero.tsx
Normal file
43
src/app/[variants]/(main)/labs/components/Hero.tsx
Normal file
@@ -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 (
|
||||
<div className={styles.container}>
|
||||
<Flexbox gap={8}>
|
||||
<div className={styles.title}>🪄 {t('title')}</div>
|
||||
<div className={styles.desc}>{t('desc')}</div>
|
||||
</Flexbox>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
Hero.displayName = 'LabsHero';
|
||||
|
||||
export default Hero;
|
||||
85
src/app/[variants]/(main)/labs/components/LabCard.tsx
Normal file
85
src/app/[variants]/(main)/labs/components/LabCard.tsx
Normal file
@@ -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<PropsWithChildren<LabCardProps>>(
|
||||
({ title, desc, checked, onChange, meta, loading }) => {
|
||||
const { styles } = useStyles();
|
||||
|
||||
return (
|
||||
<div className={styles.wrap}>
|
||||
<div className={styles.card}>
|
||||
<div className={styles.row}>
|
||||
<div className={styles.thumb} />
|
||||
<Flexbox gap={6}>
|
||||
<div className={styles.title}>{title}</div>
|
||||
<div className={styles.desc}>{desc}</div>
|
||||
{meta ? <div className={styles.meta}>{meta}</div> : null}
|
||||
</Flexbox>
|
||||
{!loading && (
|
||||
<Flexbox align={'flex-end'} height={'100%'} justify={'center'} paddingInline={8}>
|
||||
<Switch checked={checked} onChange={onChange} />
|
||||
</Flexbox>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
LabCard.displayName = 'LabCard';
|
||||
|
||||
export default LabCard;
|
||||
59
src/app/[variants]/(main)/labs/page.tsx
Normal file
59
src/app/[variants]/(main)/labs/page.tsx
Normal file
@@ -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 (
|
||||
<Flexbox gap={16} padding={8} style={{ alignItems: 'center', width: '100%' }}>
|
||||
<Hero />
|
||||
|
||||
<LabCard
|
||||
checked={inputMarkdownRender}
|
||||
desc={t('features.inputMarkdown.desc')}
|
||||
loading={!isPreferenceInit}
|
||||
onChange={onToggleMarkdown}
|
||||
title={t('features.inputMarkdown.title')}
|
||||
/>
|
||||
|
||||
<LabCard
|
||||
checked={enableGroupChat}
|
||||
desc={t('features.groupChat.desc')}
|
||||
loading={!isPreferenceInit}
|
||||
onChange={onToggleGroupChat}
|
||||
title={t('features.groupChat.title')}
|
||||
/>
|
||||
</Flexbox>
|
||||
);
|
||||
});
|
||||
|
||||
LabsPage.displayName = 'LabsPage';
|
||||
|
||||
export default LabsPage;
|
||||
@@ -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) => (
|
||||
<FloatMenu
|
||||
{...props}
|
||||
getPopupContainer={() => (slashMenuRef as any)?.current}
|
||||
/>
|
||||
),
|
||||
}),
|
||||
],
|
||||
[enableMarkdown],
|
||||
);
|
||||
|
||||
return (
|
||||
<Editor
|
||||
autoFocus
|
||||
className={className}
|
||||
content={''}
|
||||
editor={editor}
|
||||
enablePasteMarkdown={enableMarkdown}
|
||||
markdownOption={enableMarkdown}
|
||||
onBlur={() => {
|
||||
disableScope(HotkeyEnum.AddUserMessage);
|
||||
}}
|
||||
@@ -109,21 +145,7 @@ const InputEditor = memo<{ defaultRows?: number }>(() => {
|
||||
}
|
||||
}}
|
||||
placeholder={<Placeholder />}
|
||||
plugins={[
|
||||
ReactListPlugin,
|
||||
ReactLinkPlugin,
|
||||
ReactCodePlugin,
|
||||
ReactCodeblockPlugin,
|
||||
ReactHRPlugin,
|
||||
ReactTablePlugin,
|
||||
Editor.withProps(ReactMathPlugin, {
|
||||
renderComp: expand
|
||||
? undefined
|
||||
: (props) => (
|
||||
<FloatMenu {...props} getPopupContainer={() => (slashMenuRef as any)?.current} />
|
||||
),
|
||||
}),
|
||||
]}
|
||||
plugins={plugins}
|
||||
slashOption={{
|
||||
items: slashItems,
|
||||
renderComp: expand
|
||||
|
||||
@@ -239,6 +239,7 @@ export default {
|
||||
},
|
||||
information: '社区与资讯',
|
||||
installPWA: '安装浏览器应用 (PWA)',
|
||||
labs: '实验室',
|
||||
lang: {
|
||||
'ar': '阿拉伯语',
|
||||
'bg-BG': '保加利亚语',
|
||||
|
||||
@@ -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,
|
||||
|
||||
14
src/locales/default/labs.ts
Normal file
14
src/locales/default/labs.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export default {
|
||||
desc: '这里会不定期更新我们正在探索的新功能,欢迎试用!',
|
||||
features: {
|
||||
groupChat: {
|
||||
desc: '启用多智能体群聊编排能力。',
|
||||
title: '群聊(多智能体)',
|
||||
},
|
||||
inputMarkdown: {
|
||||
desc: '在输入区域实时渲染 Markdown(粗体、代码块、表格等)。',
|
||||
title: '输入框 Markdown 渲染',
|
||||
},
|
||||
},
|
||||
title: '实验室',
|
||||
};
|
||||
@@ -33,6 +33,7 @@ export const config = {
|
||||
'/',
|
||||
'/discover',
|
||||
'/discover(.*)',
|
||||
'/labs',
|
||||
'/chat',
|
||||
'/chat(.*)',
|
||||
'/changelog(.*)',
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user