Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@

## [Unreleased]

### Changed

- 配置面板改为左右分区布局:左侧分区导航(常规 / 连接 / AI / 关于)、右侧按分区归类展示配置项,替代此前单列平铺;分区结构为后续扩展(主题、编辑器风格、上下文窗口等)预留。

## [0.6.0] - 2026-06-23

> 首个 0.6 正式版。本版重点:
Expand Down
3 changes: 2 additions & 1 deletion apps/desktop/src/renderer/src/components/common/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { useTranslation } from 'react-i18next';
import type { CSSProperties, ReactNode } from 'react';
import { CloseIcon } from './icons';

type ModalSize = 'md' | 'sm' | 'confirm';
type ModalSize = 'lg' | 'md' | 'sm' | 'confirm';

const SIZE_CLASS: Record<ModalSize, string> = {
lg: 'modal modal-lg',
md: 'modal',
sm: 'modal modal-sm',
confirm: 'modal modal-confirm',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import type { AppInfo, AppPaths, Config, SupportedLanguage } from '@meebox/shared';
import { ConfirmModal, Modal } from '../../common';
import {
ConfirmModal,
GlobeIcon,
Modal,
QuestionIcon,
RobotIcon,
SettingsIcon,
} from '../../common';
import { useSettingsDraft } from './hooks/useSettingsDraft';
import { ConnectionEditorModal } from './editors/ConnectionEditorModal';
import { LlmEditorModal } from './editors/LlmEditorModal';
Expand All @@ -15,6 +23,23 @@ import { WorkDirSection } from './sections/WorkDirSection';
import { CacheDirSection } from './sections/CacheDirSection';
import { RuntimeSection } from './sections/RuntimeSection';

type SettingsCategory = 'general' | 'connection' | 'ai' | 'about';

/**
* 配置分区导航元数据(左侧栏)。新增配置分区在此登记一项,并在右侧面板的 switch
* 中渲染对应 section —— 分区结构为后续扩展(主题 / 编辑器 / 上下文窗口等)预留。
*/
const SETTINGS_CATEGORIES: ReadonlyArray<{
id: SettingsCategory;
labelKey: string;
Icon: typeof SettingsIcon;
}> = [
{ id: 'general', labelKey: 'settings.catGeneral', Icon: SettingsIcon },
{ id: 'connection', labelKey: 'settings.catConnection', Icon: GlobeIcon },
{ id: 'ai', labelKey: 'settings.catAi', Icon: RobotIcon },
{ id: 'about', labelKey: 'settings.catAbout', Icon: QuestionIcon },
];

interface SettingsModalProps {
info: AppInfo;
paths: AppPaths;
Expand Down Expand Up @@ -48,6 +73,7 @@ export function SettingsModal({
onClose,
}: SettingsModalProps) {
const { t } = useTranslation();
const [category, setCategory] = useState<SettingsCategory>('general');
const s = useSettingsDraft({
config,
paths,
Expand All @@ -61,10 +87,11 @@ export function SettingsModal({
return (
<>
<Modal
size="md"
size="lg"
onClose={onClose}
title={t('settings.title')}
headerClose="icon"
bodyClassName="settings-modal-body"
footer={
<>
<div className="modal-footer-left">
Expand Down Expand Up @@ -94,38 +121,70 @@ export function SettingsModal({
</>
}
>
<LanguageSection language={s.language} onChange={s.handleLanguageChange} />
<ConnectionsSection
connections={s.connections}
activeConnId={s.activeConnId}
onAdd={s.openAddConn}
onEdit={s.openEditConn}
onSetActive={s.setActiveConn}
onRequestDelete={s.setConnDeleteId}
/>
<PollerSection value={s.pollerInput} onChange={s.setPoller} />
<LlmSection
llm={s.llm}
onAdd={s.openAddProfile}
onEdit={s.openEditProfile}
onSetActive={s.setActiveLlm}
onDelete={s.deleteProfile}
/>
<ProxySection proxy={s.proxy} onConfigure={() => s.setProxyEditor(s.proxy)} />
<AgentDirSection
value={s.agentDirInput}
onChange={s.setAgentDir}
onPick={() => void s.pickAgentDir()}
/>
<WorkDirSection paths={paths} />
<CacheDirSection
paths={paths}
value={s.reposDirInput}
onChange={s.setReposDir}
onPick={() => void s.pickReposDir()}
totalBytes={s.totalBytes}
/>
<RuntimeSection info={info} updateEnabled={config.update.check_enabled} />
<div className="settings-layout">
<nav className="settings-nav" aria-label={t('settings.title')}>
{SETTINGS_CATEGORIES.map(({ id, labelKey, Icon }) => (
<button
key={id}
type="button"
className={`settings-nav-item${category === id ? ' active' : ''}`}
aria-current={category === id ? 'page' : undefined}
onClick={() => setCategory(id)}
>
<Icon size={16} />
<span>{t(labelKey)}</span>
</button>
))}
</nav>
<div className="settings-panel">
{category === 'general' && (
<LanguageSection language={s.language} onChange={s.handleLanguageChange} />
)}
{category === 'connection' && (
<>
<ConnectionsSection
connections={s.connections}
activeConnId={s.activeConnId}
onAdd={s.openAddConn}
onEdit={s.openEditConn}
onSetActive={s.setActiveConn}
onRequestDelete={s.setConnDeleteId}
/>
<PollerSection value={s.pollerInput} onChange={s.setPoller} />
<ProxySection proxy={s.proxy} onConfigure={() => s.setProxyEditor(s.proxy)} />
<CacheDirSection
paths={paths}
value={s.reposDirInput}
onChange={s.setReposDir}
onPick={() => void s.pickReposDir()}
totalBytes={s.totalBytes}
/>
</>
)}
{category === 'ai' && (
<>
<LlmSection
llm={s.llm}
onAdd={s.openAddProfile}
onEdit={s.openEditProfile}
onSetActive={s.setActiveLlm}
onDelete={s.deleteProfile}
/>
<AgentDirSection
value={s.agentDirInput}
onChange={s.setAgentDir}
onPick={() => void s.pickAgentDir()}
/>
</>
)}
{category === 'about' && (
<>
<WorkDirSection paths={paths} />
<RuntimeSection info={info} updateEnabled={config.update.check_enabled} />
</>
)}
</div>
</div>
</Modal>

{s.llmEditor && (
Expand Down
4 changes: 4 additions & 0 deletions apps/desktop/src/renderer/src/i18n/locales/de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,10 @@
"cacheDirTitle": "Cache-Verzeichnis",
"cacheUsage": "Cache-Nutzung",
"calculating": "Berechne…",
"catAbout": "Info",
"catAi": "KI",
"catConnection": "Verbindung",
"catGeneral": "Allgemein",
"checkFailed": "Prüfung fehlgeschlagen: {{error}}",
"checkUpdate": "Nach Updates suchen",
"checkUpdateTitle": "GitHub auf die neueste Version prüfen (nur Erkennung, keine automatische Installation)",
Expand Down
4 changes: 4 additions & 0 deletions apps/desktop/src/renderer/src/i18n/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,10 @@
"cacheDirTitle": "Cache Directory",
"cacheUsage": "Cache Usage",
"calculating": "Calculating…",
"catAbout": "About",
"catAi": "AI",
"catConnection": "Connection",
"catGeneral": "General",
"checkFailed": "Check failed: {{error}}",
"checkUpdate": "Check for Updates",
"checkUpdateTitle": "Check GitHub for the latest version (detection only, no auto-install)",
Expand Down
4 changes: 4 additions & 0 deletions apps/desktop/src/renderer/src/i18n/locales/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,10 @@
"cacheDirTitle": "キャッシュディレクトリ",
"cacheUsage": "キャッシュ使用量",
"calculating": "計算中…",
"catAbout": "情報",
"catAi": "AI",
"catConnection": "接続",
"catGeneral": "一般",
"checkFailed": "チェックに失敗しました:{{error}}",
"checkUpdate": "アップデートを確認",
"checkUpdateTitle": "GitHub で最新バージョンを確認(検出のみ、自動インストールなし)",
Expand Down
4 changes: 4 additions & 0 deletions apps/desktop/src/renderer/src/i18n/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,10 @@
"cacheDirTitle": "缓存目录",
"cacheUsage": "缓存占用",
"calculating": "计算中…",
"catAbout": "关于",
"catAi": "AI",
"catConnection": "连接",
"catGeneral": "常规",
"checkFailed": "检查失败:{{error}}",
"checkUpdate": "检查更新",
"checkUpdateTitle": "查 GitHub 最新版本(仅检测,不自动安装)",
Expand Down
8 changes: 8 additions & 0 deletions apps/desktop/src/renderer/src/styles/common/modal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@
overflow-y: auto;
}

// 宽尺寸:左右分区类模态(如 SettingsModal 分区导航 + 内容)需要更宽横向空间,
// 让左导航与右侧内容各得其所。定高(受 .modal 的 max-height: 84vh 上限约束),
// 使切换分区时模态尺寸恒定、右侧内容区独立滚动而非整体撑高。
.modal-lg {
max-width: 880px;
height: 720px;
}

// ConfirmModal: 跟 SettingsModal 共用 .modal-backdrop/.modal/.modal-header,加几条
// 小尺寸 + 底部操作行特化
.modal-confirm {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,76 @@
@use '../../tokens' as *;
@use '../../mixins' as *;

// 配置面板左右分区:模态 body 去内边距、由本布局接管;左侧分区导航固定不滚,
// 右侧分区内容独立滚动。分区结构为后续扩展(主题 / 编辑器 / 上下文窗口等)预留。
.modal-body.settings-modal-body {
padding: 0;
overflow: hidden;
display: flex;
flex: 1;
min-height: 0;
}
.settings-layout {
display: flex;
flex: 1;
min-width: 0;
// 撑满定高模态(.modal-lg):左导航固定、右面板独立滚动,不随分区内容增减高度
min-height: 0;
}
.settings-nav {
flex: 0 0 168px;
display: flex;
flex-direction: column;
gap: $space-1;
padding: $space-5 $space-4;
// 与模态主体同色($bg-elev),仅以右侧分隔线区分导航区,避免色块割裂
border-right: 1px solid $border-default;
overflow-y: auto;
}
.settings-nav-item {
display: flex;
align-items: center;
gap: $space-3;
padding: $space-3 $space-4;
border: none;
border-radius: $radius-md;
background: transparent;
color: $text-body;
font-size: $fs-lg;
text-align: left;
cursor: pointer;

svg {
flex-shrink: 0;
color: $text-muted;
}

&:hover {
background: $bg-hover;
}

&.active {
background: $color-accent-bg-fade;
color: $text-primary;
font-weight: 500;

svg {
color: $color-accent;
}
}
}
.settings-panel {
flex: 1;
min-width: 0;
overflow-y: auto;
padding: $space-8;

// 分区内末个 section 去掉多余底部留白
.modal-section:last-child {
margin-bottom: 0;
}
}

// 语言下拉:固定宽度、靠右(块定位),文本左对齐;不被 .settings-input 的 flex:1 撑满(复合选择器提权)
.settings-input.settings-language-select {
flex: 0 0 auto;
Expand Down
Loading