🐛 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:
Arvin Xu
2025-10-11 09:59:56 +02:00
committed by GitHub
parent 3985c13488
commit 9349ce2d2f
23 changed files with 278 additions and 82 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,6 +1,8 @@
import { TopicDisplayMode, UserPreference } from '@lobechat/types';
export const DEFAULT_PREFERENCE: UserPreference = {
disableInputMarkdownRender: false,
enableGroupChat: false,
guide: {
moveSettingsToAvatar: true,
topic: true,

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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>
);
});

View 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;

View 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;

View 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;

View File

@@ -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

View File

@@ -239,6 +239,7 @@ export default {
},
information: '社区与资讯',
installPWA: '安装浏览器应用 (PWA)',
labs: '实验室',
lang: {
'ar': '阿拉伯语',
'bg-BG': '保加利亚语',

View File

@@ -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,

View File

@@ -0,0 +1,14 @@
export default {
desc: '这里会不定期更新我们正在探索的新功能,欢迎试用!',
features: {
groupChat: {
desc: '启用多智能体群聊编排能力。',
title: '群聊(多智能体)',
},
inputMarkdown: {
desc: '在输入区域实时渲染 Markdown粗体、代码块、表格等。',
title: '输入框 Markdown 渲染',
},
},
title: '实验室',
};

View File

@@ -33,6 +33,7 @@ export const config = {
'/',
'/discover',
'/discover(.*)',
'/labs',
'/chat',
'/chat(.*)',
'/changelog(.*)',

View File

@@ -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,