mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-22 13:17:10 +08:00
✨ feat(settings): 统一设置中心内页与代理交互
- 将主题设置、AI 设置和关于页纳入设置中心详情页,补齐返回和取消交互 - 重构全局代理设置为草稿应用模式,新增常用预设、URL 测试入口和测试结果展示 - 按参考样式重做关于页版本与项目入口布局,并收紧顶部品牌卡尺寸 - 提取 AISettingsContent 供设置中心内嵌复用,控制详情页内部滚动边界 - 补齐多语言文案和设置中心静态回归断言
This commit is contained in:
@@ -7,10 +7,15 @@ const appSource = readFileSync(
|
||||
'utf8',
|
||||
);
|
||||
|
||||
const aiSettingsModalSource = readFileSync(
|
||||
fileURLToPath(new globalThis.URL('./components/AISettingsModal.tsx', import.meta.url)),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
describe('settings center layout', () => {
|
||||
it('uses the same split navigation shell as the tool center', () => {
|
||||
expect(appSource).toContain("type SettingsCenterGroupKey = 'preferences' | 'services' | 'about';");
|
||||
expect(appSource).toContain("type SettingsCenterPaneKey = 'language' | 'sidebar-metadata' | 'proxy' | 'web-auth';");
|
||||
expect(appSource).toContain("type SettingsCenterPaneKey = 'language' | 'theme' | 'sidebar-metadata' | 'proxy' | 'web-auth' | 'ai' | 'about-go-navi';");
|
||||
expect(appSource).toContain("const [activeSettingsCenterGroupKey, setActiveSettingsCenterGroupKey] = useState<SettingsCenterGroupKey>('preferences');");
|
||||
expect(appSource).toContain("const [activeSettingsCenterPane, setActiveSettingsCenterPane] = useState<SettingsCenterPaneState | null>(null);");
|
||||
expect(appSource).toContain('style={toolCenterModalWorkspaceStyle}');
|
||||
@@ -49,4 +54,64 @@ describe('settings center layout', () => {
|
||||
expect(appSource).toContain("handleOpenSettingsCenterPane('services', 'web-auth')");
|
||||
expect(appSource).toContain("<WebAuthSettingsPanel");
|
||||
});
|
||||
|
||||
it('adds global proxy connection testing controls', () => {
|
||||
expect(appSource).toContain("const DEFAULT_GLOBAL_PROXY_TEST_URL = 'https://api.github.com/';");
|
||||
expect(appSource).toContain('const [proxyTestUrl, setProxyTestUrl]');
|
||||
expect(appSource).toContain('const [proxyTesting, setProxyTesting]');
|
||||
expect(appSource).toContain('const [proxyTestResult, setProxyTestResult]');
|
||||
expect(appSource).toContain('handleTestGlobalProxyDraft');
|
||||
expect(appSource).toContain('TestGlobalProxyConnection');
|
||||
expect(appSource).toContain('https://github.com/Syngnat/GoNavi/releases/latest');
|
||||
expect(appSource).toContain("t('app.proxy.test.action')");
|
||||
expect(appSource).toContain("t('app.proxy.test.target_placeholder')");
|
||||
});
|
||||
|
||||
it('adds cancel and back actions to settings center detail panes', () => {
|
||||
expect(appSource).toContain('const handleBackFromSettingsCenterPane = useCallback(() => {');
|
||||
expect(appSource).toContain('const handleCancelSettingsCenterPane = useCallback(() => {');
|
||||
expect(appSource).toContain('onClick={handleCancelSettingsCenterPane}');
|
||||
expect(appSource).toContain("t('common.cancel')");
|
||||
expect(appSource).toContain('onClick={handleBackFromSettingsCenterPane}');
|
||||
expect(appSource).toContain("t('common.back_to_previous')");
|
||||
});
|
||||
|
||||
it('opens theme, AI, and about entries inside settings center detail panes', () => {
|
||||
expect(appSource).toContain("handleOpenSettingsCenterPane('preferences', 'theme')");
|
||||
expect(appSource).toContain("handleOpenSettingsCenterPane('services', 'ai')");
|
||||
expect(appSource).toContain("handleOpenSettingsCenterPane('about', 'about-go-navi')");
|
||||
expect(appSource).toContain("if (activeSettingsCenterPane.key === 'theme')");
|
||||
expect(appSource).toContain("if (activeSettingsCenterPane.key === 'ai')");
|
||||
expect(appSource).toContain('<AISettingsContent');
|
||||
expect(appSource).toContain("if (activeSettingsCenterPane.key === 'about-go-navi')");
|
||||
expect(appSource).toContain('renderSettingsCenterAboutPane()');
|
||||
});
|
||||
|
||||
it('renders the settings center about page with the reference card layout', () => {
|
||||
expect(appSource).toContain('const renderSettingsCenterAboutPane = () => {');
|
||||
expect(appSource).toContain('const renderSettingsCenterAboutProjectEntry = ({');
|
||||
expect(appSource).toContain("padding: '18px 22px'");
|
||||
expect(appSource).toContain('width: 64');
|
||||
expect(appSource).toContain('height: 64');
|
||||
expect(appSource).toContain('minWidth: 260');
|
||||
expect(appSource).toContain("t('app.about.version_update.title')");
|
||||
expect(appSource).toContain("t('app.about.project.github.title')");
|
||||
expect(appSource).toContain("t('app.about.project.issues.title')");
|
||||
expect(appSource).toContain("t('app.about.project.releases.title')");
|
||||
expect(appSource).toContain('const renderSettingsCenterAboutFooter = () => (');
|
||||
expect(appSource).toContain("t('app.about.last_checked_at', { time: aboutLastCheckedAt })");
|
||||
expect(appSource).toContain('renderAboutUpdateActions()');
|
||||
});
|
||||
|
||||
it('keeps embedded split-pane settings stable at scroll boundaries', () => {
|
||||
expect(appSource).toContain('const isSettingsCenterContainedScrollPane =');
|
||||
expect(appSource).toContain("activeSettingsCenterPane?.key === 'theme' || activeSettingsCenterPane?.key === 'ai'");
|
||||
expect(appSource).toContain('const settingsCenterDetailBodyStyle: React.CSSProperties = isSettingsCenterContainedScrollPane');
|
||||
expect(appSource).toContain("overflowY: 'hidden'");
|
||||
expect(appSource).toContain('style={settingsCenterDetailBodyStyle}');
|
||||
expect(appSource).toContain("boxSizing: 'border-box'");
|
||||
expect(appSource).toContain("overscrollBehavior: 'contain'");
|
||||
expect(aiSettingsModalSource).toContain("boxSizing: 'border-box'");
|
||||
expect(aiSettingsModalSource).toContain("overscrollBehavior: 'contain'");
|
||||
});
|
||||
});
|
||||
|
||||
2797
frontend/src/App.tsx
2797
frontend/src/App.tsx
File diff suppressed because it is too large
Load Diff
@@ -44,6 +44,14 @@ interface AISettingsModalProps {
|
||||
onBeforeExternalMCPUse?: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface AISettingsContentProps {
|
||||
active: boolean;
|
||||
darkMode: boolean;
|
||||
overlayTheme: OverlayWorkbenchTheme;
|
||||
focusProviderId?: string;
|
||||
onBeforeExternalMCPUse?: () => Promise<void>;
|
||||
}
|
||||
|
||||
const DEFAULT_MCP_HTTP_SERVER_STATUS: AIMCPHTTPServerStatus = {
|
||||
running: false,
|
||||
addr: '127.0.0.1:8765',
|
||||
@@ -80,7 +88,7 @@ const normalizeMCPHTTPAuthorizationToken = (value: string): string => {
|
||||
return withoutHeaderName.replace(/^Bearer\s+/i, '').trim();
|
||||
};
|
||||
|
||||
const AISettingsModal: React.FC<AISettingsModalProps> = ({ open, onClose, darkMode, overlayTheme, focusProviderId, onBeforeExternalMCPUse }) => {
|
||||
export const AISettingsContent: React.FC<AISettingsContentProps> = ({ active, darkMode, overlayTheme, focusProviderId, onBeforeExternalMCPUse }) => {
|
||||
const { t } = useI18n();
|
||||
const defaultMCPHTTPServerStatus = useMemo<AIMCPHTTPServerStatus>(() => ({
|
||||
...DEFAULT_MCP_HTTP_SERVER_STATUS,
|
||||
@@ -251,16 +259,16 @@ const AISettingsModal: React.FC<AISettingsModalProps> = ({ open, onClose, darkMo
|
||||
} catch (e) { console.warn('Failed to load AI config', e); }
|
||||
}, [defaultMCPHTTPServerStatus, resolveAIService, syncMCPClientStatuses]);
|
||||
|
||||
useEffect(() => { if (open) void loadConfig(); }, [open, loadConfig]);
|
||||
useEffect(() => { if (active) void loadConfig(); }, [active, loadConfig]);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
if (active) {
|
||||
resetMCPClientSelectionTouched();
|
||||
}
|
||||
}, [open, resetMCPClientSelectionTouched]);
|
||||
}, [active, resetMCPClientSelectionTouched]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open || !focusProviderId) {
|
||||
if (!active || !focusProviderId) {
|
||||
return;
|
||||
}
|
||||
if (!providers.some((provider) => provider.id === focusProviderId)) {
|
||||
@@ -268,7 +276,7 @@ const AISettingsModal: React.FC<AISettingsModalProps> = ({ open, onClose, darkMo
|
||||
}
|
||||
setActiveSection('providers');
|
||||
setActiveProviderId(focusProviderId);
|
||||
}, [focusProviderId, open, providers]);
|
||||
}, [active, focusProviderId, providers]);
|
||||
|
||||
const applyProviderEditorSession = useCallback((session: ProviderEditorSession) => {
|
||||
setEditingProvider(session.editingProvider as AIProviderConfig | null);
|
||||
@@ -285,16 +293,11 @@ const AISettingsModal: React.FC<AISettingsModalProps> = ({ open, onClose, darkMo
|
||||
applyProviderEditorSession(buildClosedProviderEditorSession());
|
||||
}, [applyProviderEditorSession]);
|
||||
|
||||
const handleModalClose = useCallback(() => {
|
||||
resetProviderEditorSession();
|
||||
onClose();
|
||||
}, [onClose, resetProviderEditorSession]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
if (!active) {
|
||||
resetProviderEditorSession();
|
||||
}
|
||||
}, [open, resetProviderEditorSession]);
|
||||
}, [active, resetProviderEditorSession]);
|
||||
const handleAddProvider = () => {
|
||||
const preset = findPreset('openai');
|
||||
applyProviderEditorSession(buildAddProviderEditorSession({
|
||||
@@ -707,6 +710,148 @@ const AISettingsModal: React.FC<AISettingsModalProps> = ({ open, onClose, darkMo
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={modalBodyRef} className="ai-settings-body" style={{ display: 'grid', gridTemplateColumns: '180px minmax(0, 1fr)', gap: 16, padding: '12px 0', height: '100%', minHeight: 0, overflow: 'hidden', alignItems: 'stretch', position: 'relative', boxSizing: 'border-box' }}>
|
||||
{messageContextHolder}
|
||||
<AISettingsSidebar
|
||||
activeSection={activeSection}
|
||||
darkMode={darkMode}
|
||||
overlayTheme={overlayTheme}
|
||||
onSelectSection={setActiveSection}
|
||||
/>
|
||||
<div style={{ minWidth: 0, minHeight: 0, height: '100%', overflowY: 'auto', overflowX: 'hidden', overscrollBehavior: 'contain', paddingRight: 8, paddingBottom: 28 }}>
|
||||
{activeSection === 'providers' && (
|
||||
<AISettingsProvidersSection
|
||||
providers={providers}
|
||||
activeProviderId={activeProviderId}
|
||||
editingProvider={editingProvider}
|
||||
isEditing={isEditing}
|
||||
form={form}
|
||||
providerPresets={localizedProviderPresets}
|
||||
watchedPresetKey={watchedPresetKey}
|
||||
watchedApiFormat={watchedApiFormat}
|
||||
loading={loading}
|
||||
testStatus={testStatus}
|
||||
primaryPasswordVisible={primaryPasswordVisible}
|
||||
darkMode={darkMode}
|
||||
overlayTheme={overlayTheme}
|
||||
cardBg={cardBg}
|
||||
cardBorder={cardBorder}
|
||||
inputBg={inputBg}
|
||||
onPrimaryPasswordVisibleChange={setPrimaryPasswordVisible}
|
||||
resolveProviderPreset={matchLocalizedProviderPreset}
|
||||
resolvePresetByKey={findLocalizedPreset}
|
||||
onAddProvider={handleAddProvider}
|
||||
onEditProvider={handleEditProvider}
|
||||
onDeleteProvider={handleDeleteProvider}
|
||||
onSetActiveProvider={handleSetActive}
|
||||
onCancelEdit={resetProviderEditorSession}
|
||||
onPresetChange={handlePresetChange}
|
||||
onTestProvider={handleTestProvider}
|
||||
onSaveProvider={handleSaveProvider}
|
||||
/>
|
||||
)}
|
||||
{activeSection === 'safety' && (
|
||||
<AISettingsSafetySection
|
||||
safetyLevel={safetyLevel}
|
||||
darkMode={darkMode}
|
||||
overlayTheme={overlayTheme}
|
||||
cardBg={cardBg}
|
||||
cardBorder={cardBorder}
|
||||
onChange={handleSafetyChange}
|
||||
/>
|
||||
)}
|
||||
{activeSection === 'context' && (
|
||||
<AISettingsContextSection
|
||||
contextLevel={contextLevel}
|
||||
darkMode={darkMode}
|
||||
overlayTheme={overlayTheme}
|
||||
cardBg={cardBg}
|
||||
cardBorder={cardBorder}
|
||||
onChange={handleContextChange}
|
||||
/>
|
||||
)}
|
||||
{activeSection === 'mcp' && (
|
||||
<AISettingsMCPSection
|
||||
mcpClientStatuses={mcpClientStatuses}
|
||||
selectedMCPClient={selectedMCPClient}
|
||||
selectedMCPClientStatus={selectedMCPClientStatus}
|
||||
selectedMCPClientCommandText={selectedMCPClientCommandText}
|
||||
mcpHTTPServerStatus={mcpHTTPServerStatus}
|
||||
mcpHTTPServerDraft={mcpHTTPServerDraft}
|
||||
mcpServers={mcpServers}
|
||||
mcpTools={mcpTools}
|
||||
darkMode={darkMode}
|
||||
overlayTheme={overlayTheme}
|
||||
cardBg={cardBg}
|
||||
cardBorder={cardBorder}
|
||||
inputBg={inputBg}
|
||||
loading={loading}
|
||||
mcpClientStatusLoading={mcpClientStatusLoading}
|
||||
mcpHTTPServerLoading={mcpHTTPServerLoading}
|
||||
onUpdateHTTPServerDraft={handleUpdateMCPHTTPServerDraft}
|
||||
onToggleHTTPServer={handleToggleMCPHTTPServer}
|
||||
onCopyHTTPServerURL={() => void handleCopyMCPHTTPServerURL()}
|
||||
onCopyHTTPServerAuthorization={() => void handleCopyMCPHTTPServerAuthorization()}
|
||||
onSelectClient={handleSelectMCPClient}
|
||||
onRefreshStatus={() => void loadMCPClientStatuses()}
|
||||
onCopyConfigPath={() => void handleCopySelectedMCPConfigPath()}
|
||||
onCopyLaunchCommand={() => void handleCopySelectedMCPLaunchCommand()}
|
||||
onInstallSelectedClient={handleInstallSelectedMCPClient}
|
||||
onAddServer={handleAddMCPServer}
|
||||
onUpdateServerDraft={updateMCPServerDraft}
|
||||
onTestServer={handleTestMCPServer}
|
||||
onSaveServer={handleSaveMCPServer}
|
||||
onDeleteServer={handleDeleteMCPServer}
|
||||
/>
|
||||
)}
|
||||
{activeSection === 'skills' && (
|
||||
<AISettingsSkillsSection
|
||||
skills={skills}
|
||||
skillRequiredToolOptions={skillRequiredToolOptions}
|
||||
overlayTheme={overlayTheme}
|
||||
cardBg={cardBg}
|
||||
cardBorder={cardBorder}
|
||||
inputBg={inputBg}
|
||||
loading={loading}
|
||||
onAddSkill={handleAddSkill}
|
||||
onUpdateSkillDraft={updateSkillDraft}
|
||||
onSaveSkill={handleSaveSkill}
|
||||
onDeleteSkill={handleDeleteSkill}
|
||||
/>
|
||||
)}
|
||||
{activeSection === 'tools' && (
|
||||
<AIBuiltinToolsCatalog
|
||||
darkMode={darkMode}
|
||||
overlayTheme={overlayTheme}
|
||||
cardBg={cardBg}
|
||||
cardBorder={cardBorder}
|
||||
/>
|
||||
)}
|
||||
{activeSection === 'prompts' && (
|
||||
<AISettingsPromptsSection
|
||||
builtinPrompts={builtinPrompts}
|
||||
userPromptSettings={userPromptSettings}
|
||||
overlayTheme={overlayTheme}
|
||||
cardBg={cardBg}
|
||||
cardBorder={cardBorder}
|
||||
inputBg={inputBg}
|
||||
darkMode={darkMode}
|
||||
loading={loading}
|
||||
onChangeUserPrompt={(key, value) => setUserPromptSettings((prev) => ({
|
||||
...prev,
|
||||
[key]: value,
|
||||
}))}
|
||||
onSave={handleSaveUserPromptSettings}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AISettingsModal: React.FC<AISettingsModalProps> = ({ open, onClose, darkMode, overlayTheme, focusProviderId, onBeforeExternalMCPUse }) => {
|
||||
const { t } = useI18n();
|
||||
const modalShellStyle = {
|
||||
background: overlayTheme.shellBg, border: overlayTheme.shellBorder,
|
||||
boxShadow: overlayTheme.shellShadow, backdropFilter: overlayTheme.shellBackdropFilter,
|
||||
@@ -731,7 +876,7 @@ const AISettingsModal: React.FC<AISettingsModalProps> = ({ open, onClose, darkMo
|
||||
</div>
|
||||
}
|
||||
open={open}
|
||||
onCancel={handleModalClose}
|
||||
onCancel={onClose}
|
||||
footer={null}
|
||||
width={820}
|
||||
styles={{
|
||||
@@ -740,142 +885,13 @@ const AISettingsModal: React.FC<AISettingsModalProps> = ({ open, onClose, darkMo
|
||||
body: { paddingTop: 8, height: 620, overflow: 'hidden' },
|
||||
}}
|
||||
>
|
||||
<div ref={modalBodyRef} className="ai-settings-body" style={{ display: 'grid', gridTemplateColumns: '180px minmax(0, 1fr)', gap: 16, padding: '12px 0', height: '100%', minHeight: 0, overflow: 'hidden', alignItems: 'stretch', position: 'relative' }}>
|
||||
{messageContextHolder}
|
||||
<AISettingsSidebar
|
||||
activeSection={activeSection}
|
||||
darkMode={darkMode}
|
||||
overlayTheme={overlayTheme}
|
||||
onSelectSection={setActiveSection}
|
||||
/>
|
||||
<div style={{ minWidth: 0, minHeight: 0, height: '100%', overflowY: 'auto', overflowX: 'hidden', paddingRight: 8, paddingBottom: 28 }}>
|
||||
{activeSection === 'providers' && (
|
||||
<AISettingsProvidersSection
|
||||
providers={providers}
|
||||
activeProviderId={activeProviderId}
|
||||
editingProvider={editingProvider}
|
||||
isEditing={isEditing}
|
||||
form={form}
|
||||
providerPresets={localizedProviderPresets}
|
||||
watchedPresetKey={watchedPresetKey}
|
||||
watchedApiFormat={watchedApiFormat}
|
||||
loading={loading}
|
||||
testStatus={testStatus}
|
||||
primaryPasswordVisible={primaryPasswordVisible}
|
||||
darkMode={darkMode}
|
||||
overlayTheme={overlayTheme}
|
||||
cardBg={cardBg}
|
||||
cardBorder={cardBorder}
|
||||
inputBg={inputBg}
|
||||
onPrimaryPasswordVisibleChange={setPrimaryPasswordVisible}
|
||||
resolveProviderPreset={matchLocalizedProviderPreset}
|
||||
resolvePresetByKey={findLocalizedPreset}
|
||||
onAddProvider={handleAddProvider}
|
||||
onEditProvider={handleEditProvider}
|
||||
onDeleteProvider={handleDeleteProvider}
|
||||
onSetActiveProvider={handleSetActive}
|
||||
onCancelEdit={resetProviderEditorSession}
|
||||
onPresetChange={handlePresetChange}
|
||||
onTestProvider={handleTestProvider}
|
||||
onSaveProvider={handleSaveProvider}
|
||||
/>
|
||||
)}
|
||||
{activeSection === 'safety' && (
|
||||
<AISettingsSafetySection
|
||||
safetyLevel={safetyLevel}
|
||||
darkMode={darkMode}
|
||||
overlayTheme={overlayTheme}
|
||||
cardBg={cardBg}
|
||||
cardBorder={cardBorder}
|
||||
onChange={handleSafetyChange}
|
||||
/>
|
||||
)}
|
||||
{activeSection === 'context' && (
|
||||
<AISettingsContextSection
|
||||
contextLevel={contextLevel}
|
||||
darkMode={darkMode}
|
||||
overlayTheme={overlayTheme}
|
||||
cardBg={cardBg}
|
||||
cardBorder={cardBorder}
|
||||
onChange={handleContextChange}
|
||||
/>
|
||||
)}
|
||||
{activeSection === 'mcp' && (
|
||||
<AISettingsMCPSection
|
||||
mcpClientStatuses={mcpClientStatuses}
|
||||
selectedMCPClient={selectedMCPClient}
|
||||
selectedMCPClientStatus={selectedMCPClientStatus}
|
||||
selectedMCPClientCommandText={selectedMCPClientCommandText}
|
||||
mcpHTTPServerStatus={mcpHTTPServerStatus}
|
||||
mcpHTTPServerDraft={mcpHTTPServerDraft}
|
||||
mcpServers={mcpServers}
|
||||
mcpTools={mcpTools}
|
||||
darkMode={darkMode}
|
||||
overlayTheme={overlayTheme}
|
||||
cardBg={cardBg}
|
||||
cardBorder={cardBorder}
|
||||
inputBg={inputBg}
|
||||
loading={loading}
|
||||
mcpClientStatusLoading={mcpClientStatusLoading}
|
||||
mcpHTTPServerLoading={mcpHTTPServerLoading}
|
||||
onUpdateHTTPServerDraft={handleUpdateMCPHTTPServerDraft}
|
||||
onToggleHTTPServer={handleToggleMCPHTTPServer}
|
||||
onCopyHTTPServerURL={() => void handleCopyMCPHTTPServerURL()}
|
||||
onCopyHTTPServerAuthorization={() => void handleCopyMCPHTTPServerAuthorization()}
|
||||
onSelectClient={handleSelectMCPClient}
|
||||
onRefreshStatus={() => void loadMCPClientStatuses()}
|
||||
onCopyConfigPath={() => void handleCopySelectedMCPConfigPath()}
|
||||
onCopyLaunchCommand={() => void handleCopySelectedMCPLaunchCommand()}
|
||||
onInstallSelectedClient={handleInstallSelectedMCPClient}
|
||||
onAddServer={handleAddMCPServer}
|
||||
onUpdateServerDraft={updateMCPServerDraft}
|
||||
onTestServer={handleTestMCPServer}
|
||||
onSaveServer={handleSaveMCPServer}
|
||||
onDeleteServer={handleDeleteMCPServer}
|
||||
/>
|
||||
)}
|
||||
{activeSection === 'skills' && (
|
||||
<AISettingsSkillsSection
|
||||
skills={skills}
|
||||
skillRequiredToolOptions={skillRequiredToolOptions}
|
||||
overlayTheme={overlayTheme}
|
||||
cardBg={cardBg}
|
||||
cardBorder={cardBorder}
|
||||
inputBg={inputBg}
|
||||
loading={loading}
|
||||
onAddSkill={handleAddSkill}
|
||||
onUpdateSkillDraft={updateSkillDraft}
|
||||
onSaveSkill={handleSaveSkill}
|
||||
onDeleteSkill={handleDeleteSkill}
|
||||
/>
|
||||
)}
|
||||
{activeSection === 'tools' && (
|
||||
<AIBuiltinToolsCatalog
|
||||
darkMode={darkMode}
|
||||
overlayTheme={overlayTheme}
|
||||
cardBg={cardBg}
|
||||
cardBorder={cardBorder}
|
||||
/>
|
||||
)}
|
||||
{activeSection === 'prompts' && (
|
||||
<AISettingsPromptsSection
|
||||
builtinPrompts={builtinPrompts}
|
||||
userPromptSettings={userPromptSettings}
|
||||
overlayTheme={overlayTheme}
|
||||
cardBg={cardBg}
|
||||
cardBorder={cardBorder}
|
||||
inputBg={inputBg}
|
||||
darkMode={darkMode}
|
||||
loading={loading}
|
||||
onChangeUserPrompt={(key, value) => setUserPromptSettings((prev) => ({
|
||||
...prev,
|
||||
[key]: value,
|
||||
}))}
|
||||
onSave={handleSaveUserPromptSettings}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<AISettingsContent
|
||||
active={open}
|
||||
darkMode={darkMode}
|
||||
overlayTheme={overlayTheme}
|
||||
focusProviderId={focusProviderId}
|
||||
onBeforeExternalMCPUse={onBeforeExternalMCPUse}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -344,6 +344,11 @@ describe("i18n catalog", () => {
|
||||
"data_grid.column_settings.show_all",
|
||||
"data_grid.column_settings.hide_all",
|
||||
"data_grid.column_settings.search_columns_placeholder",
|
||||
"data_grid.column_settings.global_hidden_columns",
|
||||
"data_grid.column_settings.global_hidden_columns_help",
|
||||
"data_grid.column_settings.global_hidden_columns_apply",
|
||||
"data_grid.column_settings.global_hidden_columns_add_current",
|
||||
"data_grid.column_settings.global_hidden_columns_clear",
|
||||
"data_grid.column_settings.remember_column_order",
|
||||
"data_grid.column_settings.remember_hidden_columns",
|
||||
"data_grid.column_settings.reset_order",
|
||||
|
||||
Reference in New Issue
Block a user