Skip to content
Open
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
/apps/desktop/runtime/target/
/apps/desktop/runtime/Cargo.lock
/plan/
/.codux/
/.cursor/
/.omo/

# Monorepo app build outputs
/apps/mobile/.dart_tool/
Expand Down
70 changes: 60 additions & 10 deletions apps/desktop/runtime-assets/i18n/locales/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,12 @@
"settings.ai.tool.reasoning_effort.low": "Niedrig",
"settings.ai.tool.reasoning_effort.medium": "Mittel",
"settings.ai.tool.reasoning_effort.high": "Hoch",
"settings.ai.tool.reasoning_effort.xhigh": "Sehr hoch"
"settings.ai.tool.reasoning_effort.xhigh": "Sehr hoch",
"settings.git.file_panel": "Dateipanel",
"settings.git.file_view_mode": "Layout",
"settings.git.file_view_mode.help": "Baum gruppiert geänderte Dateien nach Verzeichnis. Flach listet sie direkt unter jedem Abschnitt auf.",
"settings.git.review_compare_mode": "Review-Vergleich",
"settings.git.review_compare_mode.help": "Arbeitsbaum prüft nur nicht committete Änderungen. Origin zu Arbeitsbaum zeigt alles, was nicht hochgeladen wurde."
},
"en": {
"settings.ai_auto_refresh": "AI Auto Refresh",
Expand Down Expand Up @@ -754,7 +759,12 @@
"settings.ai.tool.reasoning_effort.low": "Low",
"settings.ai.tool.reasoning_effort.medium": "Medium",
"settings.ai.tool.reasoning_effort.high": "High",
"settings.ai.tool.reasoning_effort.xhigh": "XHigh"
"settings.ai.tool.reasoning_effort.xhigh": "XHigh",
"settings.git.file_panel": "File Panel",
"settings.git.file_view_mode": "Layout",
"settings.git.file_view_mode.help": "Tree groups changed files by directory. Flatten lists them directly under each section.",
"settings.git.review_compare_mode": "Review Compare",
"settings.git.review_compare_mode.help": "Working Tree audits only uncommitted changes. Origin to Working Tree shows everything not pushed upstream."
},
"es": {
"settings.ai_auto_refresh": "Actualización automática de IA",
Expand Down Expand Up @@ -1131,7 +1141,12 @@
"settings.ai.tool.reasoning_effort.low": "Bajo",
"settings.ai.tool.reasoning_effort.medium": "Medio",
"settings.ai.tool.reasoning_effort.high": "Alto",
"settings.ai.tool.reasoning_effort.xhigh": "Muy alto"
"settings.ai.tool.reasoning_effort.xhigh": "Muy alto",
"settings.git.file_panel": "Panel de archivos",
"settings.git.file_view_mode": "Diseño",
"settings.git.file_view_mode.help": "Árbol agrupa los archivos modificados por directorio. Plano los lista directamente bajo cada sección.",
"settings.git.review_compare_mode": "Comparación de revisión",
"settings.git.review_compare_mode.help": "Árbol de trabajo audita solo cambios no confirmados. Origin a árbol de trabajo muestra todo lo no empujado."
},
"fr": {
"settings.ai_auto_refresh": "Actualisation auto IA",
Expand Down Expand Up @@ -1508,7 +1523,12 @@
"settings.ai.tool.reasoning_effort.low": "Faible",
"settings.ai.tool.reasoning_effort.medium": "Moyen",
"settings.ai.tool.reasoning_effort.high": "Élevé",
"settings.ai.tool.reasoning_effort.xhigh": "Très élevé"
"settings.ai.tool.reasoning_effort.xhigh": "Très élevé",
"settings.git.file_panel": "Panneau de fichiers",
"settings.git.file_view_mode": "Disposition",
"settings.git.file_view_mode.help": "Arborescence regroupe les fichiers modifiés par répertoire. Liste les affiche directement sous chaque section.",
"settings.git.review_compare_mode": "Comparaison de révision",
"settings.git.review_compare_mode.help": "Arbre de travail audite uniquement les modifications non validées. Origin vers arbre de travail montre tout ce qui n'est pas poussé."
},
"ja": {
"settings.ai_auto_refresh": "AI 自動更新",
Expand Down Expand Up @@ -1885,7 +1905,12 @@
"settings.ai.tool.reasoning_effort.low": "低",
"settings.ai.tool.reasoning_effort.medium": "中",
"settings.ai.tool.reasoning_effort.high": "高",
"settings.ai.tool.reasoning_effort.xhigh": "最高"
"settings.ai.tool.reasoning_effort.xhigh": "最高",
"settings.git.file_panel": "ファイルパネル",
"settings.git.file_view_mode": "レイアウト",
"settings.git.file_view_mode.help": "ツリーは変更されたファイルをディレクトリ別にグループ化します。フラットは各セクションの下に直接表示します。",
"settings.git.review_compare_mode": "レビュー比較",
"settings.git.review_compare_mode.help": "ワーキングツリーはコミットされていない変更のみ監査します。Origin to Working Treeはプッシュされていないすべてを表示します。"
},
"ko": {
"settings.ai_auto_refresh": "AI 자동 새로고침",
Expand Down Expand Up @@ -2262,7 +2287,12 @@
"settings.ai.tool.reasoning_effort.low": "낮음",
"settings.ai.tool.reasoning_effort.medium": "중간",
"settings.ai.tool.reasoning_effort.high": "높음",
"settings.ai.tool.reasoning_effort.xhigh": "매우 높음"
"settings.ai.tool.reasoning_effort.xhigh": "매우 높음",
"settings.git.file_panel": "파일 패널",
"settings.git.file_view_mode": "레이아웃",
"settings.git.file_view_mode.help": "트리는 변경된 파일을 디렉토리별로 그룹화합니다. 플랫은 각 섹션 아래에 직접 나열합니다.",
"settings.git.review_compare_mode": "리뷰 비교",
"settings.git.review_compare_mode.help": "워킹 트리는 커밋되지 않은 변경만 감사합니다. Origin to Working Tree는 푸시되지 않은 모든 것을 표시합니다."
},
"pt-BR": {
"settings.ai_auto_refresh": "Atualização automática de IA",
Expand Down Expand Up @@ -2639,7 +2669,12 @@
"settings.ai.tool.reasoning_effort.low": "Baixo",
"settings.ai.tool.reasoning_effort.medium": "Médio",
"settings.ai.tool.reasoning_effort.high": "Alto",
"settings.ai.tool.reasoning_effort.xhigh": "Muito alto"
"settings.ai.tool.reasoning_effort.xhigh": "Muito alto",
"settings.git.file_panel": "Painel de arquivos",
"settings.git.file_view_mode": "Layout",
"settings.git.file_view_mode.help": "Árvore agrupa arquivos alterados por diretório. Plano os lista diretamente sob cada seção.",
"settings.git.review_compare_mode": "Comparação de revisão",
"settings.git.review_compare_mode.help": "Working Tree audita apenas alterações não commitadas. Origin to Working Tree mostra tudo não enviado."
},
"ru": {
"settings.ai_auto_refresh": "Автообновление ИИ",
Expand Down Expand Up @@ -3016,7 +3051,12 @@
"settings.ai.tool.reasoning_effort.low": "Низкий",
"settings.ai.tool.reasoning_effort.medium": "Средний",
"settings.ai.tool.reasoning_effort.high": "Высокий",
"settings.ai.tool.reasoning_effort.xhigh": "Очень высокий"
"settings.ai.tool.reasoning_effort.xhigh": "Очень высокий",
"settings.git.file_panel": "Панель файлов",
"settings.git.file_view_mode": "Макет",
"settings.git.file_view_mode.help": "Дерево группирует изменённые файлы по каталогам. Плоский список показывает их напрямую под каждой секцией.",
"settings.git.review_compare_mode": "Сравнение ревью",
"settings.git.review_compare_mode.help": "Рабочее дерево проверяет только незафиксированные изменения. Origin to Working Tree показывает всё, что не отправлено."
},
"zh-Hans": {
"settings.ai_auto_refresh": "AI 自动刷新",
Expand Down Expand Up @@ -3395,7 +3435,12 @@
"settings.ai.tool.reasoning_effort.low": "低",
"settings.ai.tool.reasoning_effort.medium": "中",
"settings.ai.tool.reasoning_effort.high": "高",
"settings.ai.tool.reasoning_effort.xhigh": "极高"
"settings.ai.tool.reasoning_effort.xhigh": "极高",
"settings.git.file_panel": "文件面板",
"settings.git.file_view_mode": "布局",
"settings.git.file_view_mode.help": "树形按目录分组已更改的文件。扁平在每个部分下直接列出它们。",
"settings.git.review_compare_mode": "审查比较",
"settings.git.review_compare_mode.help": "工作树仅审查未提交的更改。Origin to Working Tree 显示所有未推送到上游的内容。"
},
"zh-Hant": {
"settings.ai_auto_refresh": "AI 自動重新整理",
Expand Down Expand Up @@ -3774,7 +3819,12 @@
"settings.ai.tool.reasoning_effort.low": "低",
"settings.ai.tool.reasoning_effort.medium": "中",
"settings.ai.tool.reasoning_effort.high": "高",
"settings.ai.tool.reasoning_effort.xhigh": "極高"
"settings.ai.tool.reasoning_effort.xhigh": "極高",
"settings.git.file_panel": "檔案面板",
"settings.git.file_view_mode": "佈局",
"settings.git.file_view_mode.help": "樹狀按目錄分組已變更的檔案。扁平在每個區段下直接列出它們。",
"settings.git.review_compare_mode": "審查比較",
"settings.git.review_compare_mode.help": "工作樹僅審查未提交的變更。Origin to Working Tree 顯示所有未推送到上游的內容。"
}
}
}
12 changes: 12 additions & 0 deletions apps/desktop/runtime/src/remote/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,11 @@ fn remote_git_status_payload_matches_domain_shape() {
index_status: "modified".to_string(),
worktree_status: "modified".to_string(),
}],
flat_changed_files: vec![crate::git::GitFileStatus {
path: "src/main.rs".to_string(),
index_status: "modified".to_string(),
worktree_status: "modified".to_string(),
}],
branches: vec![crate::git::GitBranchSummary {
name: "main".to_string(),
is_current: true,
Expand Down Expand Up @@ -490,6 +495,13 @@ fn remote_git_status_payload_matches_domain_shape() {
.map(Vec::len),
Some(1)
);
assert_eq!(
payload
.get("flatChangedFiles")
.and_then(serde_json::Value::as_array)
.map(Vec::len),
Some(1)
);
}

#[test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,20 @@ impl RuntimeService {
self.update_settings_with_side_effects(|settings| settings.set_git_refresh(seconds))
}

pub fn cycle_git_file_view_mode(&self) -> Result<SettingsSummary, String> {
self.update_settings_with_side_effects(|settings| settings.cycle_git_file_view_mode())
}

pub fn set_git_file_view_mode(&self, mode: &str) -> Result<SettingsSummary, String> {
self.update_settings_with_side_effects(|settings| settings.set_git_file_view_mode(mode))
}

pub fn set_git_review_compare_mode(&self, mode: &str) -> Result<SettingsSummary, String> {
self.update_settings_with_side_effects(|settings| {
settings.set_git_review_compare_mode(mode)
})
}

pub fn cycle_ai_refresh(&self) -> Result<SettingsSummary, String> {
self.update_settings_with_side_effects(|settings| settings.cycle_ai_refresh())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ fn git_summary_from_payload(value: &serde_json::Value) -> crate::git::GitSummary
.and_then(Value::as_str)
.map(str::to_string),
changed_files: parse_typed(value, "changedFiles"),
flat_changed_files: parse_typed(value, "flatChangedFiles"),
branches: parse_typed(value, "branches"),
remote_branches: parse_typed(value, "remoteBranches"),
remotes: parse_typed(value, "remotes"),
Expand Down
8 changes: 8 additions & 0 deletions apps/desktop/runtime/src/settings/app_settings/defaults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,11 @@ pub(super) fn is_managed_update_endpoint(endpoint: &str) -> bool {
| "https://raw.githubusercontent.com/duxweb/codux/main/updates/beta/latest.json"
)
}

pub(super) fn default_git_file_view_mode() -> String {
"tree".to_string()
}

pub(super) fn default_git_review_compare_mode() -> String {
"workingTree".to_string()
}
8 changes: 8 additions & 0 deletions apps/desktop/runtime/src/settings/app_settings/sanitize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ pub(super) fn sanitize_settings(mut settings: AppSettings) -> AppSettings {
} else if settings.update.enabled && is_managed_update_endpoint(&settings.update.endpoint) {
settings.update.endpoint = update_endpoint_for_channel(&settings.update.channel);
}
settings.git.file_view_mode = match settings.git.file_view_mode.trim() {
"flatten" => "flatten".to_string(),
_ => "tree".to_string(),
};
settings.git.review_compare_mode = match settings.git.review_compare_mode.trim() {
"originToWorkingTree" => "originToWorkingTree".to_string(),
_ => "workingTree".to_string(),
};
settings
}

Expand Down
11 changes: 11 additions & 0 deletions apps/desktop/runtime/src/settings/app_settings/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,22 @@ pub struct AppSettings {
#[serde(default)]
pub remote: RemoteSettings,
#[serde(default)]
pub git: GitSettings,
#[serde(default)]
pub developer_hud: bool,
#[serde(default = "default_developer_refresh")]
pub developer_refresh: String,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GitSettings {
#[serde(default = "default_git_file_view_mode")]
pub file_view_mode: String,
#[serde(default = "default_git_review_compare_mode")]
pub review_compare_mode: String,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateSettings {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
impl Default for GitSettings {
fn default() -> Self {
Self {
file_view_mode: default_git_file_view_mode(),
review_compare_mode: default_git_review_compare_mode(),
}
}
}

impl Default for RemoteSettings {
fn default() -> Self {
Self {
Expand Down Expand Up @@ -152,6 +161,7 @@ impl Default for AppSettings {
shortcuts: HashMap::new(),
update: UpdateSettings::default(),
remote: RemoteSettings::default(),
git: GitSettings::default(),
developer_hud: false,
developer_refresh: default_developer_refresh(),
}
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/runtime/src/settings/default_summary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ impl Default for SettingsSummary {
pet_speech_temporary_muted: false,
developer_hud: false,
developer_refresh: "3".to_string(),
git_file_view_mode: "tree".to_string(),
git_review_compare_mode: "workingTree".to_string(),
shortcuts: HashMap::new(),
}
}
Expand Down
21 changes: 21 additions & 0 deletions apps/desktop/runtime/src/settings/sanitize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,24 @@ fn pet_mut(raw: &mut Map<String, Value>) -> Result<&mut Map<String, Value>, Stri
.as_object_mut()
.ok_or_else(|| "Pet settings are invalid.".to_string())
}

pub(super) fn git_mut(raw: &mut Map<String, Value>) -> Result<&mut Map<String, Value>, String> {
raw.entry("git".to_string())
.or_insert_with(|| Value::Object(Map::new()))
.as_object_mut()
.ok_or_else(|| "Git settings are invalid.".to_string())
}

pub(super) fn sanitize_git_file_view_mode(value: &str) -> String {
match value.trim() {
"flatten" => "flatten".to_string(),
_ => "tree".to_string(),
}
}

pub(super) fn sanitize_git_review_compare_mode(value: &str) -> String {
match value.trim() {
"originToWorkingTree" => "originToWorkingTree".to_string(),
_ => "workingTree".to_string(),
}
}
8 changes: 8 additions & 0 deletions apps/desktop/runtime/src/settings/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ impl SettingsService {
Ok(summary_from_raw(&raw))
}

pub fn update_git_string(&self, key: &str, value: String) -> Result<SettingsSummary, String> {
let mut raw = self.raw_settings();
let git = git_mut(&mut raw)?;
git.insert(key.to_string(), Value::String(value));
self.save_raw_settings(&raw)?;
Ok(summary_from_raw(&raw))
}

fn toggle_pet_bool(&self, key: &str, default: bool) -> Result<SettingsSummary, String> {
let mut raw = self.raw_settings();
let pet = pet_mut(&mut raw)?;
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/runtime/src/settings/service_preferences.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
include!("service_preferences/appearance.rs");
include!("service_preferences/general.rs");
include!("service_preferences/git_commit.rs");
include!("service_preferences/git_view.rs");
include!("service_preferences/refresh.rs");
include!("service_preferences/terminal.rs");
30 changes: 30 additions & 0 deletions apps/desktop/runtime/src/settings/service_preferences/git_view.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
impl SettingsService {
pub fn set_git_file_view_mode(&self, mode: &str) -> Result<SettingsSummary, String> {
let mode = sanitize_git_file_view_mode(mode);
self.update_git_string("fileViewMode", mode)
}

pub fn cycle_git_file_view_mode(&self) -> Result<SettingsSummary, String> {
let current = self.summary().git_file_view_mode;
let next = if current == "flatten" {
"tree"
} else {
"flatten"
};
self.update_git_string("fileViewMode", next.to_string())
}
pub fn set_git_review_compare_mode(&self, mode: &str) -> Result<SettingsSummary, String> {
let mode = sanitize_git_review_compare_mode(mode);
self.update_git_string("reviewCompareMode", mode)
}

pub fn cycle_git_review_compare_mode(&self) -> Result<SettingsSummary, String> {
let current = self.summary().git_review_compare_mode;
let next = if current == "originToWorkingTree" {
"workingTree"
} else {
"originToWorkingTree"
};
self.update_git_string("reviewCompareMode", next.to_string())
}
}
11 changes: 11 additions & 0 deletions apps/desktop/runtime/src/settings/summary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ fn summary_from_raw(raw: &Map<String, Value>) -> SettingsSummary {
let remote = raw.get("remote").and_then(Value::as_object);
let update = raw.get("update").and_then(Value::as_object);
let pet = raw.get("pet").and_then(Value::as_object);
let git = raw.get("git").and_then(Value::as_object);

SettingsSummary {
language: string_value(raw, "language", defaults.language),
Expand Down Expand Up @@ -370,6 +371,16 @@ fn summary_from_raw(raw: &Map<String, Value>) -> SettingsSummary {
.and_then(Value::as_str)
.map(|value| numeric_string(value, 3, 1, 86_400).to_string())
.unwrap_or(defaults.developer_refresh),
git_file_view_mode: git
.and_then(|git| git.get("fileViewMode"))
.and_then(Value::as_str)
.map(sanitize_git_file_view_mode)
.unwrap_or(defaults.git_file_view_mode),
git_review_compare_mode: git
.and_then(|git| git.get("reviewCompareMode"))
.and_then(Value::as_str)
.map(sanitize_git_review_compare_mode)
.unwrap_or(defaults.git_review_compare_mode),
shortcuts: raw
.get("shortcuts")
.and_then(Value::as_object)
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/runtime/src/settings/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ pub struct SettingsSummary {
pub pet_speech_temporary_muted: bool,
pub developer_hud: bool,
pub developer_refresh: String,
pub git_file_view_mode: String,
pub git_review_compare_mode: String,
pub shortcuts: HashMap<String, String>,
}

Expand Down
Loading
Loading