diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d754a7452f..6f1d97d75d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -75,6 +75,9 @@ jobs:
- name: Run verification-code countdown lifecycle contract
run: yarn run test:verification-code-countdown
+ - name: Run settings layout contract
+ run: yarn run test:settings-layout
+
- name: Build Community web client
run: yarn run build:web:community --app_version=5.3.0
diff --git a/chat2db-community-client/package.json b/chat2db-community-client/package.json
index ad599ee9f6..b79cc0bfa6 100644
--- a/chat2db-community-client/package.json
+++ b/chat2db-community-client/package.json
@@ -41,6 +41,7 @@
"test:search-result-tab-selection": "tsx src/blocks/SearchResult/tabSelection.test.ts",
"test:saved-console-tree-refresh": "tsx src/store/tree/savedConsoleTreeRefresh.test.ts",
"test:saved-console-lifecycle": "tsx src/store/workspace/utils/savedConsoleLifecycle.test.ts",
+ "test:settings-layout": "tsx src/blocks/Setting/navigation.test.ts && tsx src/blocks/Setting/search.test.ts && tsx src/blocks/Setting/BaseSetting/model.test.ts && tsx src/blocks/Setting/settingsLayout.test.ts",
"test:sql-execution-log": "tsx src/service/sqlExecutionLog.test.ts",
"test:sql-in-clipboard": "tsx src/utils/sqlInClipboard.test.ts && tsx src/components/SQLEditor/helper/sqlInsertValueDefaults.test.ts",
"test:tree-title-highlight": "tsx src/blocks/NewTree/components/TitleRender/highlightSearchText.test.ts",
diff --git a/chat2db-community-client/scripts/i18n-source-hashes.json b/chat2db-community-client/scripts/i18n-source-hashes.json
index 70e67c8641..cfa1f1fb35 100644
--- a/chat2db-community-client/scripts/i18n-source-hashes.json
+++ b/chat2db-community-client/scripts/i18n-source-hashes.json
@@ -16,12 +16,12 @@
"license.ts": "6aff8c38b1a7475264aa1e16e88d9b9731ecef112af70fe53edab449788ea788",
"login.ts": "d6b15f2b4db713711c093108372027ad65899aec6dd963d3f954231a9ce5b890",
"menu.ts": "1f9adabb7465ed545bdaa57fd2a0c10913e04979e710f65c6822f295c4d3e6ed",
- "monaco.ts": "0c7f14a176a65e1e534d68bead392907430b943b80edd1ce173a1ddfcc50ab4f",
+ "monaco.ts": "da4d9ea1a29de505f94ca9c49d125805fbef1c3132c7aa0f49f1c278b8cb1e93",
"notification.ts": "500751da2f02391622f074448eb46611fd41da2e5530d6c7d65f17a1382bd236",
"plugin.ts": "01f15b6342cc5b29899ee4144d67b04bb4bc02c6bb10779806be322dc5e9a2bc",
"price.ts": "26e35cb96368585f83a29adbb14e300da9b7f5278a4320063e81a1d1dd0adee9",
"redis.ts": "a73e4fca6d783031370060e20bb1bbba13f663a0773cd031e1744be1cb4e93af",
- "setting.ts": "0cbee33ef6464e82b35af4fc22bbe15fceb34a5b395fc582bac50ee77424f38c",
+ "setting.ts": "447e759e9842d790e80e6d5680032bf150ec259c6d48daa87458277a78a9ba1f",
"spaceSetting.ts": "defdf3138e5e1782f0282760bcef4ce1b8550cc80b03052fea468d97165cb980",
"sqlEditor.ts": "5c41e5f24d5ba78909795bcc681cabb8460cca075233b665dc4940afe82ce0c9",
"stream.ts": "8418dd25b0fd54b2ef2b15a2573edf0a4876efb4e69936ff82a157ea5cf42be4",
@@ -43,12 +43,12 @@
"license.ts": "6aff8c38b1a7475264aa1e16e88d9b9731ecef112af70fe53edab449788ea788",
"login.ts": "d6b15f2b4db713711c093108372027ad65899aec6dd963d3f954231a9ce5b890",
"menu.ts": "1f9adabb7465ed545bdaa57fd2a0c10913e04979e710f65c6822f295c4d3e6ed",
- "monaco.ts": "0c7f14a176a65e1e534d68bead392907430b943b80edd1ce173a1ddfcc50ab4f",
+ "monaco.ts": "da4d9ea1a29de505f94ca9c49d125805fbef1c3132c7aa0f49f1c278b8cb1e93",
"notification.ts": "500751da2f02391622f074448eb46611fd41da2e5530d6c7d65f17a1382bd236",
"plugin.ts": "01f15b6342cc5b29899ee4144d67b04bb4bc02c6bb10779806be322dc5e9a2bc",
"price.ts": "26e35cb96368585f83a29adbb14e300da9b7f5278a4320063e81a1d1dd0adee9",
"redis.ts": "a73e4fca6d783031370060e20bb1bbba13f663a0773cd031e1744be1cb4e93af",
- "setting.ts": "0cbee33ef6464e82b35af4fc22bbe15fceb34a5b395fc582bac50ee77424f38c",
+ "setting.ts": "447e759e9842d790e80e6d5680032bf150ec259c6d48daa87458277a78a9ba1f",
"spaceSetting.ts": "defdf3138e5e1782f0282760bcef4ce1b8550cc80b03052fea468d97165cb980",
"sqlEditor.ts": "5c41e5f24d5ba78909795bcc681cabb8460cca075233b665dc4940afe82ce0c9",
"stream.ts": "8418dd25b0fd54b2ef2b15a2573edf0a4876efb4e69936ff82a157ea5cf42be4",
diff --git a/chat2db-community-client/src/blocks/Setting/BaseSetting/index.tsx b/chat2db-community-client/src/blocks/Setting/BaseSetting/index.tsx
index 9abd7d7c82..c0e1581307 100644
--- a/chat2db-community-client/src/blocks/Setting/BaseSetting/index.tsx
+++ b/chat2db-community-client/src/blocks/Setting/BaseSetting/index.tsx
@@ -3,26 +3,18 @@ import themeDarkDimmedImg from '@/assets/img/theme-dark-dimmed.png';
import themeDarkImg from '@/assets/img/theme-dark.png';
import themeLightImg from '@/assets/img/theme-light.png';
import { runtimeEditionConfig } from '@/constants/runtimeEdition';
-import { LangType } from '@/constants/settings';
+import type { LangType } from '@/constants/settings';
import i18n from '@/i18n';
import { useGlobalStore } from '@/store/global';
import { settingSelectors } from '@/store/global/selectors';
import { refreshPage } from '@/utils';
-import { PrimaryColors, primaryColorsScales, PrimaryGradient, Swatches, ThemeAppearance } from '@chat2db/ui';
-import { Form, Input, Select } from 'antd';
-import { ChevronDown } from 'lucide-react';
+import { PrimaryColors, primaryColorsScales, PrimaryGradient, ThemeAppearance } from '@chat2db/ui';
+import { Input, Select } from 'antd';
+import { Check, ChevronDown, Globe, Palette, Type } from 'lucide-react';
import { useMemo } from 'react';
-import SettingSubsection from '../SettingSubsection';
+import { getAvailableLanguageOptions, resolveCurrentLanguage } from './model';
import { useStyles } from './style';
-const languageOptions = [
- { value: LangType.ZH_CN, label: '简体中文' },
- { value: LangType.EN_US, label: 'English' },
- { value: LangType.JA_JP, label: '日本語' },
- { value: LangType.ES_ES, label: 'Español' },
- { value: LangType.KO_KR, label: '한국어' },
-];
-
const customFontSizeOptions = [
{ value: 12, label: '12px' },
{ value: 13, label: '13px' },
@@ -74,7 +66,6 @@ export default function BaseSetting() {
setAppearance: state.setAppearance,
isCN: state.appConfig.isCN,
setPrimaryColor: state.setPrimaryColor,
- setNeutralColor: state.setNeutralColor,
setLanguage: state.setLanguage,
setCustomFont: state.setCustomFont,
setCustomFontSize: state.setCustomFontSize,
@@ -83,18 +74,12 @@ export default function BaseSetting() {
// If it is not a domestic version, Chinese will not be displayed.
const curLanguageOptions = useMemo(() => {
- if (runtimeEditionConfig.languageRegionRestricted && !isCN) {
- return languageOptions.filter((item) => item.value !== LangType.ZH_CN);
- }
- return languageOptions;
+ return getAvailableLanguageOptions(runtimeEditionConfig.languageRegionRestricted, isCN);
}, [isCN]);
// If it is not a domestic version, Chinese will not be displayed.
const curLanguage = useMemo(() => {
- if (runtimeEditionConfig.languageRegionRestricted && !isCN && language === LangType.ZH_CN) {
- return LangType.EN_US;
- }
- return language;
+ return resolveCurrentLanguage(language, runtimeEditionConfig.languageRegionRestricted, isCN);
}, [language, isCN]);
const isDark = appearance.includes('dark');
@@ -113,108 +98,138 @@ export default function BaseSetting() {
window.setTimeout(refreshPage, 0);
}
- function changeThemeMode(item: any) {
- setAppearance(item.code);
+ function changeThemeMode(code: ThemeAppearance) {
+ setAppearance(code);
}
function changePrimaryColor(item: any) {
setPrimaryColor(item);
}
- // function changeNeutralColor(item: any) {
- // setNeutralColor(item);
- // }
-
return (
-
-
-
-
- {themeList.map((t) => {
- return (
-
- );
- })}
+
+
+
+
+
+
+ {i18n('setting.title.appearance')}
+
+
{i18n('setting.text.appearanceDescribe')}
+
+
+
+
+
{i18n('setting.title.backgroundColor')}
+
+ {themeList.map((t) => {
+ const isActive = appearance === t.code;
+ return (
+
+ );
+ })}
+
+
+
+
{i18n('setting.title.themeColor')}
+
+ {primaryColorsSwatches.map((color) => {
+ const isActive = primaryColor?.label === color.label;
+ return (
+
+ );
+ })}
+
+
-
-
- }
- />
-
-
-
-
*/}
- {
- setCustomFont(e.target.value);
- }}
- placeholder={i18n('setting.placeholder.customFont')}
- />
- {/*
-
*/}
+
+
+
+
+
+ {i18n('setting.title.language')}
+
+
{i18n('setting.title.languageDescribe')}
+
+
+
-
-
-
+
- {/*
-
-
-
+
+
+
+
+
+ {i18n('setting.title.interfaceFont')}
+
+
{i18n('setting.text.interfaceFontDescribe')}
+
+
+
+
+
-
*/}
+
);
}
diff --git a/chat2db-community-client/src/blocks/Setting/BaseSetting/model.test.ts b/chat2db-community-client/src/blocks/Setting/BaseSetting/model.test.ts
new file mode 100644
index 0000000000..f26c897f8d
--- /dev/null
+++ b/chat2db-community-client/src/blocks/Setting/BaseSetting/model.test.ts
@@ -0,0 +1,31 @@
+import assert from 'node:assert/strict';
+import type { LangType } from '@/constants/settings';
+import { getAvailableLanguageOptions, languageOptions, resolveCurrentLanguage } from './model';
+
+assert.equal(
+ getAvailableLanguageOptions(false, false).length,
+ languageOptions.length,
+ 'unrestricted editions expose every supported language',
+);
+assert.equal(
+ getAvailableLanguageOptions(true, false).some((item) => item.value === 'zh-CN'),
+ false,
+ 'region-restricted non-CN editions hide Simplified Chinese',
+);
+assert.equal(
+ getAvailableLanguageOptions(true, true).some((item) => item.value === 'zh-CN'),
+ true,
+ 'region-restricted CN editions keep Simplified Chinese available',
+);
+assert.equal(
+ resolveCurrentLanguage('zh-CN' as LangType, true, false),
+ 'en-US',
+ 'a persisted Simplified Chinese preference falls back to English when it is unavailable',
+);
+assert.equal(
+ resolveCurrentLanguage('ja-JP' as LangType, true, false),
+ 'ja-JP',
+ 'available persisted languages remain unchanged',
+);
+
+console.log('Base settings model tests passed.');
diff --git a/chat2db-community-client/src/blocks/Setting/BaseSetting/model.ts b/chat2db-community-client/src/blocks/Setting/BaseSetting/model.ts
new file mode 100644
index 0000000000..67b5afbe05
--- /dev/null
+++ b/chat2db-community-client/src/blocks/Setting/BaseSetting/model.ts
@@ -0,0 +1,29 @@
+import type { LangType } from '@/constants/settings';
+
+const EN_US = 'en-US' as LangType;
+const ZH_CN = 'zh-CN' as LangType;
+const JA_JP = 'ja-JP' as LangType;
+const ES_ES = 'es-ES' as LangType;
+const KO_KR = 'ko-KR' as LangType;
+
+export const languageOptions = [
+ { value: ZH_CN, label: '简体中文' },
+ { value: EN_US, label: 'English' },
+ { value: JA_JP, label: '日本語' },
+ { value: ES_ES, label: 'Español' },
+ { value: KO_KR, label: '한국어' },
+];
+
+export function getAvailableLanguageOptions(languageRegionRestricted: boolean, isCN: boolean) {
+ if (languageRegionRestricted && !isCN) {
+ return languageOptions.filter((item) => item.value !== ZH_CN);
+ }
+ return languageOptions;
+}
+
+export function resolveCurrentLanguage(language: LangType, languageRegionRestricted: boolean, isCN: boolean) {
+ if (languageRegionRestricted && !isCN && language === ZH_CN) {
+ return EN_US;
+ }
+ return language;
+}
diff --git a/chat2db-community-client/src/blocks/Setting/BaseSetting/style.ts b/chat2db-community-client/src/blocks/Setting/BaseSetting/style.ts
index 6b2eb5d689..1632b79f7d 100644
--- a/chat2db-community-client/src/blocks/Setting/BaseSetting/style.ts
+++ b/chat2db-community-client/src/blocks/Setting/BaseSetting/style.ts
@@ -1,55 +1,205 @@
import { createStyles } from 'antd-style';
-export const useStyles = createStyles(({ css, token }) => {
- return {
- title: css`
- font-size: 14px;
- margin-bottom: 10px;
- i {
- margin-left: 10px;
- color: ${token.colorPrimary};
- }
- `,
- baseSettingBox: css`
- display: flex;
- flex-direction: column;
- gap: 30px;
- `,
- backgroundList: css`
- display: flex;
- gap: 20px;
- `,
- themeItemBox: css`
- display: flex;
- flex-direction: column;
- align-items: center;
- `,
- themeBox: css`
- width: 80px;
- height: 52px;
- margin-bottom: 6px;
- overflow: hidden;
- border-radius: 4px;
- background-size: cover;
- background-repeat: no-repeat;
- cursor: pointer;
- `,
- activeThemeBox: css`
- box-shadow: inset 0 0 0 2px ${token.colorPrimary};
- border-radius: 4px;
- `,
- themeName: css`
- text-align: center;
- font-size: 12px;
- `,
- primaryColorList: css`
- display: flex;
- flex-wrap: wrap;
- margin-bottom: 20px;
- `,
- customFontBox: css`
- display: flex;
- gap: 20px;
- `,
- };
-});
+export const useStyles = createStyles(({ css, token }) => ({
+ baseSettingBox: css`
+ width: 100%;
+ padding-top: 24px;
+ display: flex;
+ flex-direction: column;
+ gap: 30px;
+ `,
+ customFontBox: css`
+ display: flex;
+ flex-wrap: wrap;
+ gap: 20px;
+ `,
+ settingsList: css`
+ width: 100%;
+ container-type: inline-size;
+ `,
+ settingRow: css`
+ padding: 24px 0;
+ border-bottom: 1px solid ${token.colorSplit};
+ display: grid;
+ grid-template-columns: minmax(180px, 240px) minmax(0, 1fr);
+ align-items: start;
+ gap: 32px;
+
+ @container (max-width: 720px) {
+ grid-template-columns: minmax(0, 1fr);
+ gap: 16px;
+ }
+ `,
+ settingMeta: css`
+ min-width: 0;
+ display: grid;
+ grid-template-columns: 20px minmax(0, 1fr);
+ align-items: start;
+ gap: 10px;
+ `,
+ settingMetaContent: css`
+ min-width: 0;
+ `,
+ settingGroupIcon: css`
+ margin-top: 2px;
+ color: ${token.colorPrimary};
+ `,
+ settingTitle: css`
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 22px;
+ `,
+ settingDescription: css`
+ max-width: 280px;
+ margin-top: 4px;
+ color: ${token.colorTextSecondary};
+ font-size: 13px;
+ line-height: 20px;
+ `,
+ settingControl: css`
+ min-width: 0;
+ `,
+ settingStack: css`
+ width: 100%;
+ max-width: 640px;
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+ `,
+ controlBlock: css`
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ `,
+ backgroundList: css`
+ width: 100%;
+ max-width: 640px;
+ display: grid;
+ grid-template-columns: repeat(4, minmax(96px, 1fr));
+ gap: 14px;
+
+ @container (max-width: 480px) {
+ grid-template-columns: repeat(2, minmax(96px, 1fr));
+ }
+ `,
+ themeItemBox: css`
+ min-width: 0;
+ padding: 0;
+ border: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ background: transparent;
+ color: ${token.colorTextSecondary};
+ font: inherit;
+ cursor: pointer;
+
+ &:hover {
+ color: ${token.colorText};
+ }
+
+ &:focus-visible {
+ border-radius: 6px;
+ outline: 2px solid ${token.colorPrimary};
+ outline-offset: 3px;
+ }
+ `,
+ activeThemeItemBox: css`
+ color: ${token.colorText};
+ font-weight: 500;
+ `,
+ themeBox: css`
+ position: relative;
+ box-sizing: border-box;
+ width: 100%;
+ aspect-ratio: 8 / 5;
+ overflow: hidden;
+ border: 1px solid ${token.colorBorderSecondary};
+ border-radius: 6px;
+ display: block;
+ background-position: center;
+ background-repeat: no-repeat;
+ background-size: cover;
+ `,
+ activeThemeBox: css`
+ border-color: ${token.colorPrimary};
+ box-shadow: 0 0 0 1px ${token.colorPrimary};
+ `,
+ themeCheck: css`
+ position: absolute;
+ top: 7px;
+ right: 7px;
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ background-color: ${token.colorPrimary};
+ color: ${token.colorTextLightSolid};
+ `,
+ themeName: css`
+ min-height: 20px;
+ font-size: 12px;
+ line-height: 20px;
+ text-align: center;
+ `,
+ swatchesBox: css`
+ min-height: 32px;
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 12px;
+ `,
+ colorSwatch: css`
+ box-sizing: border-box;
+ width: 28px;
+ height: 28px;
+ padding: 0;
+ border: 1px solid ${token.colorBorderSecondary};
+ border-radius: 50%;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ color: ${token.colorTextLightSolid};
+ cursor: pointer;
+
+ &:focus-visible {
+ outline: 2px solid ${token.colorPrimary};
+ outline-offset: 3px;
+ }
+ `,
+ activeColorSwatch: css`
+ box-shadow: 0 0 0 2px ${token.colorBgBase}, 0 0 0 4px ${token.colorPrimary};
+ `,
+ languageSelect: css`
+ width: min(220px, 100%);
+ `,
+ fontControls: css`
+ width: 100%;
+ max-width: 640px;
+ display: grid;
+ grid-template-columns: minmax(180px, 1fr) 160px;
+ gap: 16px;
+
+ @container (max-width: 480px) {
+ grid-template-columns: minmax(0, 1fr);
+ }
+ `,
+ fieldControl: css`
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 7px;
+
+ .ant-select {
+ width: 100%;
+ }
+ `,
+ fieldLabel: css`
+ color: ${token.colorTextSecondary};
+ font-size: 12px;
+ line-height: 18px;
+ `,
+}));
diff --git a/chat2db-community-client/src/blocks/Setting/CommunitySetting.tsx b/chat2db-community-client/src/blocks/Setting/CommunitySetting.tsx
index 6eb8c9366a..4d9a758f77 100644
--- a/chat2db-community-client/src/blocks/Setting/CommunitySetting.tsx
+++ b/chat2db-community-client/src/blocks/Setting/CommunitySetting.tsx
@@ -1,16 +1,14 @@
import i18n from '@/i18n';
+import { ClipboardPen, Info, Keyboard, SlidersHorizontal } from 'lucide-react';
import { useEffect, useMemo } from 'react';
-import { history } from 'umi';
import About from './About';
import BaseSetting from './BaseSetting';
import EditorSetting from './EditorSetting';
import McpSetting from './McpSetting';
import NetworkProxySetting from './NetworkProxySetting';
+import SettingLayout, { type SettingMenuItem } from './SettingLayout';
import ShortcutSetting from './ShortcutSetting';
-import { IconButton, ListItem } from '@chat2db/ui';
-import { useStyles } from './style';
-
import { runtimeEditionConfig } from '@/constants/runtimeEdition';
import { useGlobalStore } from '@/store/global';
@@ -25,65 +23,67 @@ function CommunitySetting() {
language: state.baseSetting.language,
}));
- const { styles } = useStyles();
-
- function changeMenu(tab: string) {
- setSettingPageActiveTab(tab);
- }
-
const menusList = useMemo(
- () => [
- {
- title: i18n('setting.nav.basic'),
- describe: i18n('setting.nav.basicDescribe'),
- iconCode: 'icon-setting',
- body: ,
- code: 'basic',
- },
- {
- title: i18n('setting.nav.editSetting'),
- describe: i18n('setting.nav.editSettingDescribe'),
- iconCode: 'icon-clipboard1',
- body: ,
- code: 'editSetting',
- },
- ...(runtimeEditionConfig.mcpSetting
- ? [
- {
- title: i18n('setting.nav.mcp'),
- describe: i18n('setting.nav.mcpDescribe'),
- iconCode: 'icon-mcp',
- body: ,
- code: 'mcp',
- },
- ]
- : []),
- ...(runtimeEditionConfig.networkProxySetting
- ? [
- {
- title: i18n('setting.nav.networkProxy'),
- describe: i18n('setting.nav.networkProxyDescribe'),
- iconCode: 'icon-wangluo',
- body: ,
- code: 'networkProxy',
- },
- ]
- : []),
- {
- title: i18n('setting.nav.shortcut'),
- describe: '',
- iconCode: 'icon-a-kuaijiejian1',
- body: ,
- code: 'shortcut',
- },
- {
- title: i18n('setting.nav.aboutUs'),
- describe: i18n('setting.nav.aboutUsDescribe'),
- iconCode: 'icon-exclamation-circle',
- body: ,
- code: 'about',
- },
- ],
+ () =>
+ [
+ {
+ title: i18n('setting.nav.basic'),
+ describe: i18n('setting.nav.basicDescribe'),
+ group: 'general' as const,
+ icon: SlidersHorizontal,
+ body: ,
+ code: 'basic',
+ },
+ {
+ title: i18n('setting.nav.editSetting'),
+ describe: i18n('setting.nav.editSettingDescribe'),
+ group: 'general' as const,
+ icon: ClipboardPen,
+ body: ,
+ code: 'editSetting',
+ },
+ ...(runtimeEditionConfig.mcpSetting
+ ? [
+ {
+ title: i18n('setting.nav.mcp'),
+ describe: i18n('setting.text.mcpDescribe'),
+ group: 'services' as const,
+ iconCode: 'icon-mcp',
+ body: ,
+ code: 'mcp',
+ },
+ ]
+ : []),
+ ...(runtimeEditionConfig.networkProxySetting
+ ? [
+ {
+ title: i18n('setting.nav.networkProxy'),
+ describe: i18n('setting.text.networkProxyDescribe'),
+ group: 'services' as const,
+ iconCode: 'icon-wangluo',
+ body: ,
+ code: 'networkProxy',
+ },
+ ]
+ : []),
+ {
+ title: i18n('setting.nav.shortcut'),
+ describe: i18n('setting.nav.shortcutDescribe'),
+ group: 'general' as const,
+ icon: Keyboard,
+ body: ,
+ code: 'shortcut',
+ },
+ {
+ title: i18n('setting.nav.aboutUs'),
+ describe: i18n('setting.nav.aboutUsDescribe'),
+ group: 'information' as const,
+ hidePageHeader: true,
+ icon: Info,
+ body: ,
+ code: 'about',
+ },
+ ] satisfies SettingMenuItem[],
[language],
);
@@ -94,39 +94,7 @@ function CommunitySetting() {
}, [menusList, settingPageActiveTab, setSettingPageActiveTab]);
return (
-
-
-
{i18n('setting.title.setting')}
-
{
- const pathName = window.location.pathname.split('/')[1];
- setSettingPageActiveTab(false);
- if (pathName === 'settings') {
- history.push('/');
- }
- }}
- size="lg"
- code="icon-close"
- />
-
-
-
- {menusList.map((item) => (
-
- ))}
-
-
- {menusList.map((item) => (settingPageActiveTab === item.code ? item.body : null))}
-
-
-
+
);
}
diff --git a/chat2db-community-client/src/blocks/Setting/DeviceCer/index.tsx b/chat2db-community-client/src/blocks/Setting/DeviceCer/index.tsx
index 5c6d68f00d..e5c109fac2 100644
--- a/chat2db-community-client/src/blocks/Setting/DeviceCer/index.tsx
+++ b/chat2db-community-client/src/blocks/Setting/DeviceCer/index.tsx
@@ -77,8 +77,6 @@ const DeviceCer = () => {
return (
-
{i18n('license.deviceCertificateTitle')}
-
{
return {
wrapper: css`
- min-width: 480px;
- `,
- mainTitle: css`
- font-size: 28px;
- font-weight: 700;
- margin-bottom: 48px;
+ width: 100%;
+ min-width: 0;
+ padding-top: 24px;
`,
title: css``,
diff --git a/chat2db-community-client/src/blocks/Setting/EditorSetting/index.tsx b/chat2db-community-client/src/blocks/Setting/EditorSetting/index.tsx
index f4a99fe20a..bfee576530 100644
--- a/chat2db-community-client/src/blocks/Setting/EditorSetting/index.tsx
+++ b/chat2db-community-client/src/blocks/Setting/EditorSetting/index.tsx
@@ -1,9 +1,7 @@
import { useMemo } from 'react';
import i18n from '@/i18n';
import { useStyles } from './style';
-import SettingSubsection from '../SettingSubsection';
-import { useCommonStyle } from '../commonStyle';
-import { Form, InputNumber, Radio, Col, Row, Input, Select, Switch } from 'antd';
+import { Form, Input, InputNumber, Radio, Select, Switch } from 'antd';
import { useGlobalStore } from '@/store/global';
import { DEFAULT_EDITOR_SETTINGS, MonacoEditor, editorFontFamily, editorThemes } from '@/components/SQLEditor';
@@ -13,7 +11,8 @@ import { osNow } from '@/utils';
import { v4 as uuid } from 'uuid';
import { databaseMap } from '@/constants';
import { useUpdateEffect } from 'ahooks';
-import { ChevronDown } from 'lucide-react';
+import { Braces, ChevronDown, Monitor, Palette, Play } from 'lucide-react';
+import SearchTargetLabel from '../SearchTargetLabel';
const THEMES = Object.entries(editorThemes).map(([key]) => ({ label: key, value: key }));
const FONT_FAMILIES = Object.entries(editorFontFamily).map(([key, value]) => ({ label: key, value }));
@@ -25,7 +24,6 @@ const { isMac, isWin } = osNow();
function EditorSettings() {
const { styles, theme } = useStyles();
const { appearance } = theme;
- const { styles: commonStyles } = useCommonStyle();
const [form] = Form.useForm();
const { updateEditorSettings, _editorSettings, getEditorTheme } = useGlobalStore((s) => ({
_editorSettings: s.editorSettings,
@@ -73,120 +71,192 @@ function EditorSettings() {
const monacoEditorId = useMemo(() => uuid(), []);
return (
-
-
-
-
-
- {
- form.setFieldsValue({ theme: value });
+
+
-
- {i18n('monaco.minimap.on')}
- {i18n('monaco.minimap.off')}
-
-
-
-
- {i18n('monaco.wordWrap.on')}
- {i18n('monaco.wordWrap.off')}
-
-
-
-
- {i18n('monaco.minimap.on')}
- {i18n('monaco.minimap.off')}
-
-
-
-
+
+
+
+ {i18n('monaco.group.display')}
+
+
+
{i18n('monaco.lineNumbers')}}
+ >
+
+ {i18n('monaco.lineNumbers.on')}
+ {i18n('monaco.lineNumbers.off')}
+
+
+ {i18n('monaco.minimap')}}
+ tooltip={i18n('monaco.minimap.tooltip')}
+ >
+
+ {i18n('monaco.minimap.on')}
+ {i18n('monaco.minimap.off')}
+
+
+ {i18n('monaco.wordWrap')}}
+ tooltip={i18n('monaco.wordWrap.tooltip')}
+ >
+
+ {i18n('monaco.wordWrap.on')}
+ {i18n('monaco.wordWrap.off')}
+
+
+ {i18n('monaco.folding')}}
+ tooltip={i18n('monaco.folding.tooltip')}
+ >
+
+ {i18n('monaco.minimap.on')}
+ {i18n('monaco.minimap.off')}
+
+
+
+ {i18n('monaco.renderLineHighlight')}
+
+ }
+ tooltip={i18n('monaco.renderLineHighlight.tooltip')}
+ >
}
- options={['line', 'none', 'gutter', 'all'].map((value) => ({
- label: value.toUpperCase(),
- value,
- }))}
+ options={[
+ { label: i18n('monaco.renderLineHighlight.line'), value: 'line' },
+ { label: i18n('monaco.renderLineHighlight.none'), value: 'none' },
+ { label: i18n('monaco.renderLineHighlight.gutter'), value: 'gutter' },
+ { label: i18n('monaco.renderLineHighlight.all'), value: 'all' },
+ ]}
/>
-
-
-
- {i18n('monaco.keywordCase.upper')}
- {i18n('monaco.keywordCase.lower')}
-
-
-
-
+ {i18n('monaco.stickyScroll')}
+ }
+ tooltip={i18n('monaco.stickyScroll.tooltip')}
+ valuePropName="checked"
+ >
+
+
+
+
+
+
+
+
+ {i18n('monaco.group.completion')}
+
+
+
{i18n('monaco.keywordCase')}}
+ tooltip={i18n('monaco.keywordCase.tooltip')}
+ >
+
+ {i18n('monaco.keywordCase.upper')}
+ {i18n('monaco.keywordCase.lower')}
+
+
+
+ {i18n('monaco.completionAcceptKey')}
+
+ }
+ tooltip={i18n('monaco.completionAcceptKey.tooltip')}
+ >
+
+ {i18n('monaco.completionAcceptKey.enter')}
+ {i18n('monaco.completionAcceptKey.tab')}
+
+
+
{i18n('monaco.completion.all')}
+ }
+ tooltip={i18n('monaco.completion.all.tooltip')}
+ >
}
@@ -196,40 +266,55 @@ function EditorSettings() {
}))}
/>
-
-
-
- {i18n('monaco.errorContinue.true')}
- {i18n('monaco.errorContinue.false')}
-
-
-
-
- {ddlTriggerOptions.map((option) => (
-
- {option.label}
-
- ))}
-
-
-
-
- {i18n('monaco.completionAcceptKey.enter')}
- {i18n('monaco.completionAcceptKey.tab')}
-
-
-
-
-
-
-
-
-
+
+ {i18n('monaco.tableDDLTriggerMode')}
+
+ }
+ tooltip={i18n('monaco.tableDDLTriggerMode.tooltip', ddlClickModifierKey)}
+ >
+
+ {ddlTriggerOptions.map((option) => (
+
+ {option.label}
+
+ ))}
+
+
+
+
+
+
+
+
+ {i18n('monaco.group.execution')}
+
+
+
{i18n('monaco.errorContinue')}
+ }
+ tooltip={i18n('monaco.errorContinue.tooltip')}
+ >
+
+ {i18n('monaco.errorContinue.true')}
+ {i18n('monaco.errorContinue.false')}
+
+
+
+
+
+
+
);
diff --git a/chat2db-community-client/src/blocks/Setting/EditorSetting/style.ts b/chat2db-community-client/src/blocks/Setting/EditorSetting/style.ts
index 519da8c409..179f60eb13 100644
--- a/chat2db-community-client/src/blocks/Setting/EditorSetting/style.ts
+++ b/chat2db-community-client/src/blocks/Setting/EditorSetting/style.ts
@@ -3,20 +3,88 @@ import { createStyles } from 'antd-style';
export const useStyles = createStyles(({ css, token }) => {
return {
container: css`
- display: flex;
- gap: 20px;
width: 100%;
+ padding-top: 24px;
+ display: flex;
+ gap: 32px;
+
+ @container (max-width: 960px) {
+ flex-direction: column;
+ }
`,
formWrapper: css`
flex: 1;
- min-width: 240px;
+ min-width: 0;
+ `,
+ settingSection: css`
+ width: 100%;
+ padding-bottom: 24px;
+
+ & + & {
+ padding-top: 24px;
+ border-top: 1px solid ${token.colorSplit};
+ }
+
+ &:last-child {
+ padding-bottom: 0;
+ }
+ `,
+ sectionTitle: css`
+ margin: 0 0 18px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 22px;
+ `,
+ sectionIcon: css`
+ flex: 0 0 auto;
+ color: ${token.colorPrimary};
+ `,
+ fieldGrid: css`
+ width: 100%;
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ column-gap: 18px;
+
+ :global(.ant-form-item) {
+ min-width: 0;
+ margin-bottom: 18px;
+ }
+
+ :global(.ant-select),
+ :global(.ant-input-number-group-wrapper),
+ :global(.ant-input-number) {
+ width: 100%;
+ }
+
+ @container (max-width: 520px) {
+ grid-template-columns: minmax(0, 1fr);
+ }
+ `,
+ fullWidthField: css`
+ grid-column: 1 / -1;
`,
editorWrapper: css`
/* max-height: calc(100vh - 120px); */
/* max-height: calc(100vh - 60px); */
/* border: 1px solid ${token.colorBorderSecondary}; */
/* padding-top: 8px; */
+ position: sticky;
+ top: 24px;
flex: 1;
+ min-width: 0;
+ height: clamp(420px, calc(100vh - 190px), 720px);
+ overflow: hidden;
+
+ @container (max-width: 960px) {
+ position: static;
+ width: 100%;
+ height: 420px;
+ min-height: 320px;
+ flex: none;
+ }
`,
};
});
diff --git a/chat2db-community-client/src/blocks/Setting/License/index.tsx b/chat2db-community-client/src/blocks/Setting/License/index.tsx
index de12a59fbe..d9c62f145e 100644
--- a/chat2db-community-client/src/blocks/Setting/License/index.tsx
+++ b/chat2db-community-client/src/blocks/Setting/License/index.tsx
@@ -1,7 +1,6 @@
import { useMemo } from 'react';
import { useStyles } from './style';
import i18n from '@/i18n';
-import SettingSubsection from '../SettingSubsection';
import { Flex, Button } from 'antd';
import { useUserStore } from '@/store/user';
import { useGlobalStore } from '@/store/global';
@@ -150,7 +149,6 @@ const License = () => {
return (
-
{i18n('setting.license.title')}
diff --git a/chat2db-community-client/src/blocks/Setting/License/style.ts b/chat2db-community-client/src/blocks/Setting/License/style.ts
index dd7c7594b7..81b29cf5be 100644
--- a/chat2db-community-client/src/blocks/Setting/License/style.ts
+++ b/chat2db-community-client/src/blocks/Setting/License/style.ts
@@ -3,6 +3,7 @@ import { createStyles } from 'antd-style';
export const useStyles = createStyles(({ css, token }) => {
return {
wrapper: css`
+ padding-top: 24px;
display: flex;
flex-direction: column;
gap: 16px;
diff --git a/chat2db-community-client/src/blocks/Setting/McpSetting/index.tsx b/chat2db-community-client/src/blocks/Setting/McpSetting/index.tsx
index a1cdba0231..2c33bcb5b1 100644
--- a/chat2db-community-client/src/blocks/Setting/McpSetting/index.tsx
+++ b/chat2db-community-client/src/blocks/Setting/McpSetting/index.tsx
@@ -55,17 +55,19 @@ export default function McpSetting() {
return (
-
-
+
-
-
+
+
{i18n('setting.title.mcpToken')}}
+ describe={i18n('setting.text.mcpTokenDescribe')}
+ />