From 68fc51aa589cd77def666645ace63db1571bca69 Mon Sep 17 00:00:00 2001 From: CasLinden Date: Thu, 7 May 2026 19:19:20 +0900 Subject: [PATCH 01/13] refactor(commands): move install_version and clear_pinned_version stubs to commands/updater.rs --- apps/native/src-tauri/src/commands/updater.rs | 10 ++++++++++ apps/native/src-tauri/src/main.rs | 4 ++-- apps/native/src-tauri/src/updater_pin/mod.rs | 2 -- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/native/src-tauri/src/commands/updater.rs b/apps/native/src-tauri/src/commands/updater.rs index 1ca9aa89d..616b325b1 100644 --- a/apps/native/src-tauri/src/commands/updater.rs +++ b/apps/native/src-tauri/src/commands/updater.rs @@ -51,3 +51,13 @@ pub fn relaunch_after_update(app: AppHandle) -> Result<(), String> { // (it does not call std::process::exit directly), so we must return Ok here. Ok(()) } + +#[tauri::command] +pub async fn install_version(app: AppHandle, version: String) -> Result<(), String> { + crate::updater_pin::install_version(app, version).await +} + +#[tauri::command] +pub async fn clear_pinned_version(app: AppHandle) -> Result<(), String> { + crate::updater_pin::clear_pinned_version(app).await +} diff --git a/apps/native/src-tauri/src/main.rs b/apps/native/src-tauri/src/main.rs index dee567ebd..3f2cb6825 100644 --- a/apps/native/src-tauri/src/main.rs +++ b/apps/native/src-tauri/src/main.rs @@ -384,8 +384,8 @@ fn run_gui_mode( commands::cli_tool::list_cli_models, // Updater commands::updater::relaunch_after_update, - updater_pin::install_version, - updater_pin::clear_pinned_version, + commands::updater::install_version, + commands::updater::clear_pinned_version, // Editor commands::editor::editor_read_file, commands::editor::editor_write_file, diff --git a/apps/native/src-tauri/src/updater_pin/mod.rs b/apps/native/src-tauri/src/updater_pin/mod.rs index a271bc9ec..3f47c5899 100644 --- a/apps/native/src-tauri/src/updater_pin/mod.rs +++ b/apps/native/src-tauri/src/updater_pin/mod.rs @@ -177,13 +177,11 @@ async fn install_version_impl(_app: tauri::AppHandle, _version: String) -> Resul /// Install a specific past release of nixmac for bisecting. /// /// The frontend should call `relaunch_after_update` after this returns successfully. -#[tauri::command] pub async fn install_version(app: tauri::AppHandle, version: String) -> Result<(), String> { install_version_impl(app, version).await } /// Clear the pinned-version preference so the silent update check resumes. -#[tauri::command] pub async fn clear_pinned_version(app: tauri::AppHandle) -> Result<(), String> { crate::storage::store::delete_pref(&app, crate::storage::store::PINNED_VERSION_KEY) .map_err(|e| format!("failed to clear pinned version: {e}"))?; From 015ac53f216bc182b5d8d40db4d448953efaca7f Mon Sep 17 00:00:00 2001 From: CasLinden Date: Thu, 7 May 2026 19:22:07 +0900 Subject: [PATCH 02/13] chore(tauri-api): add updater namespace for installVersion, relaunch, clearPinnedVersion --- apps/native/src/tauri-api.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/native/src/tauri-api.ts b/apps/native/src/tauri-api.ts index 1e7801013..d7f6cd2a5 100644 --- a/apps/native/src/tauri-api.ts +++ b/apps/native/src/tauri-api.ts @@ -258,6 +258,12 @@ export const darwinAPI = { getStateDiff: () => invoke("homebrew_get_state_diff"), applyDiff: (diff: HomebrewState) => invoke("homebrew_apply_diff", { diff }), }, + + updater: { + installVersion: (version: string) => invoke("install_version", { version }), + relaunch: () => invoke("relaunch_after_update"), + clearPinnedVersion: () => invoke("clear_pinned_version"), + }, }; export const ipcRenderer = { From 15f2ffb07d63b4668d12a511f743db78736b476e Mon Sep 17 00:00:00 2001 From: CasLinden Date: Thu, 7 May 2026 19:28:52 +0900 Subject: [PATCH 03/13] refactor(settings): migrate invoke calls to darwinAPI.updater --- .../widget/settings/developer-tab.tsx | 10 +++++----- apps/native/src/hooks/use-updater.ts | 18 ++++++++++++++++-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/apps/native/src/components/widget/settings/developer-tab.tsx b/apps/native/src/components/widget/settings/developer-tab.tsx index e19808cfb..f9970b97d 100644 --- a/apps/native/src/components/widget/settings/developer-tab.tsx +++ b/apps/native/src/components/widget/settings/developer-tab.tsx @@ -2,7 +2,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { useWidgetStore } from "@/stores/widget-store"; import { darwinAPI } from "@/tauri-api"; -import { invoke } from "@tauri-apps/api/core"; +import { useUpdater } from "@/hooks/use-updater"; import { getVersion } from "@tauri-apps/api/app"; import { AlertTriangle, Download, History, Pin, RotateCcw } from "lucide-react"; import { useEffect, useState } from "react"; @@ -10,6 +10,7 @@ import { useEffect, useState } from "react"; const VERSION_PATTERN = /^[0-9]+(?:\.[0-9]+){0,2}(?:-[a-zA-Z0-9.-]+)?$/; export function DeveloperTab() { + const { installVersion, relaunch, clearPinnedVersion } = useUpdater(); const pinnedVersion = useWidgetStore((s) => s.pinnedVersion); const setPinnedVersion = useWidgetStore((s) => s.setPinnedVersion); const [currentVersion, setCurrentVersion] = useState(null); @@ -37,12 +38,11 @@ export function DeveloperTab() { setStatusMessage(`Fetching v${target}…`); setInstalling(true); try { - await invoke("install_version", { version: target }); + await installVersion(target); // Sync local store immediately — persistence already happened on the Rust side. setPinnedVersion(target); setStatusMessage(`Installed v${target}. Relaunching…`); - // Reuse the same relaunch path the production updater uses. - await invoke("relaunch_after_update"); + await relaunch(); } catch (err) { setStatusMessage(null); setErrorMessage(err instanceof Error ? err.message : String(err)); @@ -53,7 +53,7 @@ export function DeveloperTab() { const handleClearPin = async () => { setErrorMessage(null); try { - await invoke("clear_pinned_version"); + await clearPinnedVersion(); setPinnedVersion(null); setStatusMessage("Cleared pinned version. The auto-updater will check for the latest on next launch."); } catch (err) { diff --git a/apps/native/src/hooks/use-updater.ts b/apps/native/src/hooks/use-updater.ts index c52db9e3e..a3111bc32 100644 --- a/apps/native/src/hooks/use-updater.ts +++ b/apps/native/src/hooks/use-updater.ts @@ -1,6 +1,5 @@ import { useState, useEffect, useCallback, useRef } from "react"; import type { Update } from "@tauri-apps/plugin-updater"; -import { invoke } from "@tauri-apps/api/core"; import { darwinAPI } from "@/tauri-api"; import { useWidgetStore } from "@/stores/widget-store"; @@ -120,7 +119,7 @@ export function useUpdater() { // custom relaunch_after_update command opens the newly-installed // bundle via LaunchServices instead of re-exec-ing the cached // (potentially stale) binary path from the old bundle. - await invoke("relaunch_after_update"); + await darwinAPI.updater.relaunch(); } catch (err) { if (isDevMode) { setState((s) => ({ @@ -144,6 +143,18 @@ export function useUpdater() { } }, [isDevMode, state.available]); + const installVersion = useCallback(async (version: string): Promise => { + await darwinAPI.updater.installVersion(version); + }, []); + + const relaunch = useCallback(async (): Promise => { + await darwinAPI.updater.relaunch(); + }, []); + + const clearPinnedVersion = useCallback(async (): Promise => { + await darwinAPI.updater.clearPinnedVersion(); + }, []); + const dismiss = useCallback(() => { setState(initialState); }, []); @@ -189,6 +200,9 @@ export function useUpdater() { ...state, checkForUpdates, installUpdate, + installVersion, + relaunch, + clearPinnedVersion, dismiss, }; } From 7121bd6a9854bb201501ae1ca8b3051e9201a593 Mon Sep 17 00:00:00 2001 From: CasLinden Date: Thu, 7 May 2026 19:32:34 +0900 Subject: [PATCH 04/13] chore(tauri-api): add debug namespace for debug_sentry_event and use in feedback-dialog --- .../native/src/components/widget/feedback/feedback-dialog.tsx | 4 ++-- apps/native/src/tauri-api.ts | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/native/src/components/widget/feedback/feedback-dialog.tsx b/apps/native/src/components/widget/feedback/feedback-dialog.tsx index 639e947f9..b3a8ec743 100644 --- a/apps/native/src/components/widget/feedback/feedback-dialog.tsx +++ b/apps/native/src/components/widget/feedback/feedback-dialog.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; import * as Sentry from "@sentry/react"; -import { invoke } from "@tauri-apps/api/core"; + import { useCurrentStep, useWidgetStore } from "@/stores/widget-store"; import { Dialog, @@ -443,7 +443,7 @@ export function FeedbackDialog() { // Also send from Rust backend try { - await invoke("debug_sentry_event"); + await darwinAPI.debug.sentryEvent(); } catch (err) { // eslint-disable-next-line no-console console.warn("[debug_sentry_event] Failed to invoke Rust command:", err); diff --git a/apps/native/src/tauri-api.ts b/apps/native/src/tauri-api.ts index d7f6cd2a5..d4ccb9684 100644 --- a/apps/native/src/tauri-api.ts +++ b/apps/native/src/tauri-api.ts @@ -264,6 +264,10 @@ export const darwinAPI = { relaunch: () => invoke("relaunch_after_update"), clearPinnedVersion: () => invoke("clear_pinned_version"), }, + + debug: { + sentryEvent: () => invoke("debug_sentry_event"), + }, }; export const ipcRenderer = { From 48b308249c84c1f7f249aa2041bac67d7062cfa9 Mon Sep 17 00:00:00 2001 From: CasLinden Date: Thu, 7 May 2026 19:35:45 +0900 Subject: [PATCH 05/13] chore(lsp-client): use existing tauri-api invocations --- apps/native/src/lib/lsp-client.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/native/src/lib/lsp-client.ts b/apps/native/src/lib/lsp-client.ts index c4bd4c2fd..50555cba5 100644 --- a/apps/native/src/lib/lsp-client.ts +++ b/apps/native/src/lib/lsp-client.ts @@ -1,4 +1,4 @@ -import { invoke } from "@tauri-apps/api/core"; +import { darwinAPI } from "@/tauri-api"; import { listen, type UnlistenFn } from "@tauri-apps/api/event"; interface JsonRpcRequest { @@ -40,7 +40,7 @@ export class NixdLspClient { async start(configDir: string): Promise { if (this._running) return; - await invoke("lsp_start"); + await darwinAPI.lsp.start(); // Listen for messages from nixd via Tauri events this.unlisten = await listen("lsp:message", (event) => { @@ -82,7 +82,7 @@ export class NixdLspClient { } try { - await invoke("lsp_stop"); + await darwinAPI.lsp.stop(); } catch { // Best effort } @@ -101,13 +101,13 @@ export class NixdLspClient { this.pending.set(id, { resolve, reject }); }); - await invoke("lsp_send", { message: JSON.stringify(request) }); + await darwinAPI.lsp.send(JSON.stringify(request)); return promise; } sendNotification(method: string, params: unknown): void { const notification: JsonRpcNotification = { jsonrpc: "2.0", method, params }; - invoke("lsp_send", { message: JSON.stringify(notification) }).catch((e) => { + darwinAPI.lsp.send(JSON.stringify(notification)).catch((e: unknown) => { console.warn("[lsp-client] Failed to send notification:", e); }); } From 2bf6cd06554429732d2de77b7d8cef68d27bd7d2 Mon Sep 17 00:00:00 2001 From: CasLinden Date: Thu, 7 May 2026 19:42:32 +0900 Subject: [PATCH 06/13] chore(commands): remove six dead commands and their invocation wrappers --- apps/native/src-tauri/src/commands/apply.rs | 95 ------------------- apps/native/src-tauri/src/commands/editor.rs | 8 +- apps/native/src-tauri/src/commands/git.rs | 6 -- .../src-tauri/src/commands/permissions.rs | 5 - apps/native/src-tauri/src/editor/mod.rs | 46 --------- apps/native/src-tauri/src/git/exec.rs | 5 - apps/native/src-tauri/src/git/mod.rs | 2 +- apps/native/src-tauri/src/main.rs | 6 -- .../src-tauri/src/shared_types/system.rs | 11 --- apps/native/src-tauri/src/system/nix.rs | 27 +----- .../src-tauri/src/system/permissions.rs | 6 -- apps/native/src/tauri-api.ts | 8 -- 12 files changed, 3 insertions(+), 222 deletions(-) diff --git a/apps/native/src-tauri/src/commands/apply.rs b/apps/native/src-tauri/src/commands/apply.rs index 09049eda6..7ce159557 100644 --- a/apps/native/src-tauri/src/commands/apply.rs +++ b/apps/native/src-tauri/src/commands/apply.rs @@ -1,9 +1,7 @@ use super::helpers::capture_err; -use crate::bootstrap::default_config; use crate::storage::store; use crate::system::nix; use crate::{rebuild, shared_types}; -use std::process::Command; use tauri::AppHandle; /// Starts a streaming darwin-rebuild switch operation. @@ -53,72 +51,6 @@ pub async fn darwin_activate_store_path( .map_err(|e| capture_err("darwin_activate_store_path", e)) } -/// Cancels an in-progress apply by stashing changes on a new branch and returning to the previous branch. -/// Does not kill the running darwin-rebuild process; process cancellation is not yet implemented. -#[tauri::command] -pub async fn darwin_apply_stream_cancel(app: AppHandle) -> Result { - let dir = store::ensure_config_dir_exists(&app) - .map_err(|e| capture_err("darwin_apply_stream_cancel", e))?; - - let output = Command::new("git") - .args(["add", "."]) - .current_dir(&dir) - .env("PATH", nix::get_nix_path()) - .output() - .map_err(|e| capture_err("darwin_apply_stream_cancel", e))?; - if !output.status.success() { - return Err(format!( - "Failed to add files to git: {}", - String::from_utf8_lossy(&output.stderr) - )); - } - - let date = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(); - let output = Command::new("git") - .args(["checkout", "-b", &format!("canceled-{}", date)]) - .current_dir(&dir) - .env("PATH", nix::get_nix_path()) - .output() - .map_err(|e| capture_err("darwin_apply_stream_cancel", e))?; - if !output.status.success() { - return Err(format!( - "Failed to checkout canceled commit: {}", - String::from_utf8_lossy(&output.stderr) - )); - } - - let commit_hash = String::from_utf8_lossy(&output.stdout).trim().to_string(); - let output = Command::new("git") - .args(["commit", "-m", &format!("Canceled commit: {}", commit_hash)]) - .current_dir(&dir) - .env("PATH", nix::get_nix_path()) - .output() - .map_err(|e| capture_err("darwin_apply_stream_cancel", e))?; - if !output.status.success() { - return Err(format!( - "Failed to commit canceled commit: {}", - String::from_utf8_lossy(&output.stderr) - )); - } - - // check out prev branch - let output = Command::new("git") - .args(["checkout", "-"]) - .current_dir(&dir) - .env("PATH", nix::get_nix_path()) - .output() - .map_err(|e| capture_err("darwin_apply_stream_cancel", e))?; - if !output.status.success() { - return Err(format!( - "Failed to checkout previous branch: {}", - String::from_utf8_lossy(&output.stderr) - )); - } - - // TODO: Implement actual cancellation by tracking the child process - Ok(shared_types::OkResult::yes()) -} - /// Records build state and changeset after a successful darwin-rebuild switch. #[tauri::command] pub async fn finalize_apply(app: AppHandle) -> Result { @@ -139,27 +71,6 @@ pub async fn finalize_rollback( .map_err(|e| capture_err("finalize_rollback", e)) } -#[tauri::command] -pub async fn flake_installed_apps(app: AppHandle) -> Result, String> { - let dir = store::ensure_config_dir_exists(&app) - .map_err(|e| capture_err("flake_installed_apps", e))?; - - let host = nix::determine_host_attr(&app) - .or_else(|| { - let hosts = nix::list_darwin_hosts(&dir).ok()?; - if hosts.len() == 1 { - Some(hosts[0].clone()) - } else { - None - } - }) - .ok_or_else(|| "Host attribute not found".to_string())?; - - let apps = nix::evaluate_installed_apps(&dir, &host) - .map_err(|e| capture_err("flake_installed_apps", e))?; - Ok(apps) -} - #[tauri::command] pub async fn nix_check() -> Result { let installed = nix::is_nix_installed(); @@ -193,12 +104,6 @@ pub async fn nix_install_start(app: AppHandle) -> Result Result { - default_config::finalize_flake_lock(&app)?; - Ok(shared_types::OkResult::yes()) -} - /// Lists all darwinConfigurations defined in the flake. #[tauri::command] pub async fn flake_list_hosts(app: AppHandle) -> Result, String> { diff --git a/apps/native/src-tauri/src/commands/editor.rs b/apps/native/src-tauri/src/commands/editor.rs index 0732b80be..3ee8e805d 100644 --- a/apps/native/src-tauri/src/commands/editor.rs +++ b/apps/native/src-tauri/src/commands/editor.rs @@ -1,4 +1,4 @@ -use crate::{editor, shared_types}; +use crate::editor; use tauri::AppHandle; /// Read a file relative to the config directory. @@ -17,12 +17,6 @@ pub async fn editor_write_file( editor::write_file(&app, &rel_path, &content).await } -/// List files in the config directory. -#[tauri::command] -pub async fn editor_list_files(app: AppHandle) -> Result, String> { - editor::list_files(&app).await -} - /// Start the nixd LSP server. #[tauri::command] pub async fn lsp_start(app: AppHandle) -> Result<(), String> { diff --git a/apps/native/src-tauri/src/commands/git.rs b/apps/native/src-tauri/src/commands/git.rs index 34b2538a0..90f525fa3 100644 --- a/apps/native/src-tauri/src/commands/git.rs +++ b/apps/native/src-tauri/src/commands/git.rs @@ -22,12 +22,6 @@ pub async fn git_status_and_cache(app: AppHandle) -> Result Result, String> { - git::cached(&app).map_err(|e| capture_err("git_cached", e)) -} - /// Stages all changes and creates a commit with the given message. #[tauri::command] pub async fn git_commit( diff --git a/apps/native/src-tauri/src/commands/permissions.rs b/apps/native/src-tauri/src/commands/permissions.rs index 5fdeb0221..6cb6638df 100644 --- a/apps/native/src-tauri/src/commands/permissions.rs +++ b/apps/native/src-tauri/src/commands/permissions.rs @@ -18,8 +18,3 @@ pub async fn permissions_request( .map_err(|e| capture_err("permissions_request", e)) } -/// Check if all required permissions are granted. -#[tauri::command] -pub async fn permissions_all_required_granted() -> Result { - Ok(permissions::all_required_permissions_granted()) -} diff --git a/apps/native/src-tauri/src/editor/mod.rs b/apps/native/src-tauri/src/editor/mod.rs index 1857e4ad6..964794f96 100644 --- a/apps/native/src-tauri/src/editor/mod.rs +++ b/apps/native/src-tauri/src/editor/mod.rs @@ -7,7 +7,6 @@ pub mod lsp; use std::path::Path; -pub(crate) use crate::shared_types::FileEntry; use tauri::AppHandle; use crate::evolve::file_ops; @@ -46,48 +45,3 @@ pub async fn write_file(app: &AppHandle, rel_path: &str, content: &str) -> Resul std::fs::write(&full_path, content).map_err(|e| format!("Failed to write {}: {}", rel_path, e)) } -/// List files in the config directory recursively. -pub async fn list_files(app: &AppHandle) -> Result, String> { - let config_dir = store::get_config_dir(app).map_err(|e| e.to_string())?; - let base = Path::new(&config_dir); - - let mut entries = Vec::new(); - collect_entries(base, base, &mut entries).map_err(|e| e.to_string())?; - entries.sort_by(|a, b| a.path.cmp(&b.path)); - Ok(entries) -} - -fn collect_entries( - base: &Path, - dir: &Path, - entries: &mut Vec, -) -> Result<(), std::io::Error> { - for entry in std::fs::read_dir(dir)? { - let entry = entry?; - let path = entry.path(); - let name = entry.file_name().to_string_lossy().to_string(); - - // Skip hidden files/dirs and common noise - if name.starts_with('.') || name == "node_modules" || name == "result" { - continue; - } - - let rel = path - .strip_prefix(base) - .unwrap_or(&path) - .to_string_lossy() - .to_string(); - - let is_dir = path.is_dir(); - entries.push(FileEntry { - path: rel, - name, - is_dir, - }); - - if is_dir { - collect_entries(base, &path, entries)?; - } - } - Ok(()) -} diff --git a/apps/native/src-tauri/src/git/exec.rs b/apps/native/src-tauri/src/git/exec.rs index 410efa25f..c680beba1 100644 --- a/apps/native/src-tauri/src/git/exec.rs +++ b/apps/native/src-tauri/src/git/exec.rs @@ -315,11 +315,6 @@ pub fn cache_status(app: &AppHandle, status: &GitStatus) - crate::storage::store::set_cached_git_status(app, status) } -/// Returns cached -pub fn cached(app: &AppHandle) -> Result> { - crate::storage::store::get_cached_git_status(app) -} - /// Registers all untracked files as intent-to-add in the git index. /// Makes files visible to `git ls-files` (and therefore Nix flakes) pub fn intent_add_untracked(dir: &str) -> Result<()> { diff --git a/apps/native/src-tauri/src/git/mod.rs b/apps/native/src-tauri/src/git/mod.rs index b5bc769cd..cc80f8ce2 100644 --- a/apps/native/src-tauri/src/git/mod.rs +++ b/apps/native/src-tauri/src/git/mod.rs @@ -7,7 +7,7 @@ pub mod exec; // `crate::git::some_fn()` without change. #[allow(unused_imports)] pub use exec::{ - cache_status, cached, checkout_files_at_commit, commit_all, commit_diff, + cache_status, checkout_files_at_commit, commit_all, commit_diff, create_evolution_backup, current_branch, delete_backup_branch, get_full_diff, get_nix_diff, get_ref_sha, init_repo, intent_add_untracked, is_repo, log, read_tags, restore_all, restore_from_branch_ref, stash, status, status_and_cache, tag_commit, CommitInfo, diff --git a/apps/native/src-tauri/src/main.rs b/apps/native/src-tauri/src/main.rs index 3f2cb6825..5baa751fd 100644 --- a/apps/native/src-tauri/src/main.rs +++ b/apps/native/src-tauri/src/main.rs @@ -318,7 +318,6 @@ fn run_gui_mode( // Git commands::git::git_status, commands::git::git_status_and_cache, - commands::git::git_cached, commands::git::git_commit, commands::git::git_stash, // Darwin/Nix @@ -327,7 +326,6 @@ fn run_gui_mode( commands::evolve::darwin_evolve_answer, commands::apply::darwin_apply_stream_start, commands::apply::darwin_activate_store_path, - commands::apply::darwin_apply_stream_cancel, commands::apply::finalize_apply, commands::apply::finalize_rollback, commands::rollback::rollback_erase, @@ -342,8 +340,6 @@ fn run_gui_mode( commands::apply::nix_check, commands::apply::nix_install_start, commands::apply::darwin_rebuild_prefetch, - commands::apply::finalize_flake_lock, - commands::apply::flake_installed_apps, commands::apply::flake_list_hosts, commands::config::flake_exists, commands::config::bootstrap_default_config, @@ -374,7 +370,6 @@ fn run_gui_mode( // Permissions commands::permissions::permissions_check_all, commands::permissions::permissions_request, - commands::permissions::permissions_all_required_granted, // System defaults scanner commands::system_defaults::get_recommended_prompt, commands::system_defaults::scan_system_defaults, @@ -389,7 +384,6 @@ fn run_gui_mode( // Editor commands::editor::editor_read_file, commands::editor::editor_write_file, - commands::editor::editor_list_files, // LSP commands::editor::lsp_start, commands::editor::lsp_send, diff --git a/apps/native/src-tauri/src/shared_types/system.rs b/apps/native/src-tauri/src/shared_types/system.rs index f73d10b2c..c22db6f6a 100644 --- a/apps/native/src-tauri/src/shared_types/system.rs +++ b/apps/native/src-tauri/src/shared_types/system.rs @@ -120,14 +120,3 @@ pub struct RecommendedPrompt { pub prompt_text: String, } -/// File or directory entry returned by the editor tree. -#[derive(Debug, Clone, Serialize, Type)] -#[serde(rename_all = "camelCase")] -pub struct FileEntry { - /// Path relative to the selected config directory. - pub path: String, - /// File or directory basename. - pub name: String, - /// Whether this entry is a directory. - pub is_dir: bool, -} diff --git a/apps/native/src-tauri/src/system/nix.rs b/apps/native/src-tauri/src/system/nix.rs index fdb1f6d5c..10056b0ab 100644 --- a/apps/native/src-tauri/src/system/nix.rs +++ b/apps/native/src-tauri/src/system/nix.rs @@ -26,7 +26,7 @@ use anyhow::Result; use log::{error, info}; -use serde_json::Value; + use std::io::{Read as _, Write as _}; use std::process::{Command, Stdio}; use std::sync::OnceLock; @@ -97,31 +97,6 @@ pub fn determine_host_attr(app: &tauri::AppHandle) -> Opti crate::storage::store::read_host_attr_from_file() } -pub fn evaluate_installed_apps(config_dir: &str, host_attr: &str) -> Result> { - let attr = format!( - ".#darwinConfigurations.{}.config.environment.systemPackages", - host_attr - ); - - let output = Command::new("nix") - .args(["eval", "--json", &attr]) - .current_dir(config_dir) - .env("PATH", get_nix_path()) - .env("NIX_CONFIG", "experimental-features = nix-command flakes") - .output()?; - - if !output.status.success() { - anyhow::bail!( - "Failed to evaluate installed apps: {}", - String::from_utf8_lossy(&output.stderr) - ); - } - - let stdout = String::from_utf8(output.stdout)?; - let apps: Vec = serde_json::from_str(&stdout)?; - Ok(apps) -} - pub fn list_darwin_hosts(config_dir: &str) -> Result> { let output = Command::new("nix") .args([ diff --git a/apps/native/src-tauri/src/system/permissions.rs b/apps/native/src-tauri/src/system/permissions.rs index f2343844f..332e01023 100644 --- a/apps/native/src-tauri/src/system/permissions.rs +++ b/apps/native/src-tauri/src/system/permissions.rs @@ -373,12 +373,6 @@ pub fn request_permission(permission_id: &str) -> Result { } } -/// Check if all required permissions are granted -pub fn all_required_permissions_granted() -> bool { - let state: PermissionsState = check_all_permissions(); - state.all_required_granted -} - #[cfg(test)] mod tests { use super::*; diff --git a/apps/native/src/tauri-api.ts b/apps/native/src/tauri-api.ts index d4ccb9684..5f872ee6c 100644 --- a/apps/native/src/tauri-api.ts +++ b/apps/native/src/tauri-api.ts @@ -18,7 +18,6 @@ import type { FeedbackPanicDetails, FeedbackShareOptions, FeedbackSystemInfo, - FileEntry, FinalizeApplyResult, GitStatus, HomebrewState, @@ -65,7 +64,6 @@ export type { FeedbackPanicDetails, FeedbackShareOptions, FeedbackSystemInfo, - FileEntry, FinalizeApplyResult, GitFileStatus, GitStatus, @@ -137,7 +135,6 @@ export const darwinAPI = { git: { status: () => invoke("git_status"), statusAndCache: () => invoke("git_status_and_cache"), - cached: () => invoke("git_cached"), commit: (message: string) => invoke("git_commit", { message }), stash: (message: string) => invoke("git_stash", { message }), }, @@ -151,7 +148,6 @@ export const darwinAPI = { invoke("darwin_apply_stream_start", { hostOverride }), activateStorePath: (storePath: string) => invoke("darwin_activate_store_path", { storePath }), - applyStreamCancel: () => invoke("darwin_apply_stream_cancel"), finalizeApply: () => invoke("finalize_apply"), finalizeRollback: (storePath: string | null, changesetId: number | null) => invoke("finalize_rollback", { storePath, changesetId }), @@ -167,11 +163,9 @@ export const darwinAPI = { }, flake: { listHosts: () => invoke("flake_list_hosts"), - installedApps: () => invoke("flake_installed_apps"), exists: () => invoke("flake_exists"), existsAt: (dir: string) => invoke("flake_exists_at", { dir }), bootstrapDefault: (hostname: string) => invoke("bootstrap_default_config", { hostname }), - finalizeFlakeLock: () => invoke("finalize_flake_lock"), }, path: { exists: (dir: string) => invoke("path_exists", { dir }), @@ -224,7 +218,6 @@ export const darwinAPI = { permissions: { checkAll: () => invoke("permissions_check_all"), request: (permissionId: string) => invoke("permissions_request", { permissionId }), - allRequiredGranted: () => invoke("permissions_all_required_granted"), // macOS-specific permission checks via tauri-plugin-macos-permissions checkFullDiskAccess: () => checkFullDiskAccessPermission(), requestFullDiskAccess: () => requestFullDiskAccessPermission(), @@ -245,7 +238,6 @@ export const darwinAPI = { readFile: (relPath: string) => invoke("editor_read_file", { relPath }), writeFile: (relPath: string, content: string) => invoke("editor_write_file", { relPath, content }), - listFiles: () => invoke("editor_list_files"), }, lsp: { From 44f78a26fba0a8d9fb0371c5736bb32082a9df4c Mon Sep 17 00:00:00 2001 From: CasLinden Date: Thu, 7 May 2026 20:31:33 +0900 Subject: [PATCH 07/13] refactor(api-types): remove type re-exportsand migrate FeedbackMetadata to Specta --- Cargo.lock | 1 + apps/native/src-tauri/Cargo.toml | 2 +- .../src-tauri/examples/specta_gen_ts.rs | 5 +- .../src-tauri/src/shared_types/feedback.rs | 6 +- .../widget/feedback/feedback-dialog.tsx | 18 +- .../widget/history/history-item-card.tsx | 2 +- .../history/history-item-expanded-detail.tsx | 2 +- .../widget/history/history-item-meta.tsx | 2 +- .../promptinput/system-defaults-cta.tsx | 2 +- .../widget/settings/ai-models-tab.tsx | 3 +- .../widget/steps/permissions-step.tsx | 3 +- apps/native/src/hooks/use-evolve.ts | 3 +- apps/native/src/hooks/use-history-card.ts | 2 +- apps/native/src/hooks/use-history-restore.ts | 2 +- apps/native/src/hooks/use-nix-install.ts | 9 +- apps/native/src/hooks/use-panic-handler.ts | 2 +- apps/native/src/hooks/use-permissions.test.ts | 2 +- apps/native/src/hooks/use-permissions.ts | 2 +- apps/native/src/hooks/use-queue-summarizer.ts | 3 +- apps/native/src/hooks/use-rebuild-stream.ts | 9 +- apps/native/src/lib/ai-provider-validation.ts | 2 +- apps/native/src/preview-indicator-window.tsx | 2 +- apps/native/src/stores/widget-store.test.ts | 2 +- apps/native/src/stores/widget-store.ts | 335 ++++++++++-------- apps/native/src/tauri-api.ts | 88 +---- apps/native/src/types/feedback.ts | 76 +--- apps/native/src/types/shared.ts | 73 +++- 27 files changed, 313 insertions(+), 345 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 37b6c5ccf..924da7da4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6022,6 +6022,7 @@ version = "2.0.0-rc.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab7f01e9310a820edd31c80fde3cae445295adde21a3f9416517d7d65015b971" dependencies = [ + "serde_json", "specta-macros", "thiserror 1.0.69", ] diff --git a/apps/native/src-tauri/Cargo.toml b/apps/native/src-tauri/Cargo.toml index 30f7bfe7f..62a329ae3 100644 --- a/apps/native/src-tauri/Cargo.toml +++ b/apps/native/src-tauri/Cargo.toml @@ -62,7 +62,7 @@ fancy-regex = "0.17.0" toml = "1.0.3" backtrace = "0.3" clap = { version = "4", features = ["derive"] } -specta = { version = "=2.0.0-rc.22", features = ["derive"] } +specta = { version = "=2.0.0-rc.22", features = ["derive", "serde_json"] } specta-typescript = "0.0.9" rnix = "0.14.0" serde_yaml = "0.9" diff --git a/apps/native/src-tauri/examples/specta_gen_ts.rs b/apps/native/src-tauri/examples/specta_gen_ts.rs index 7b4f741a7..298d493f1 100644 --- a/apps/native/src-tauri/examples/specta_gen_ts.rs +++ b/apps/native/src-tauri/examples/specta_gen_ts.rs @@ -39,6 +39,8 @@ fn main() { .register::() .register::() .register::() + .register::() + .register::() .register::() .register::() .register::() @@ -94,8 +96,7 @@ fn main() { .register::() .register::() .register::() - .register::() - .register::(); + .register::(); let shared_output_path = "../src/types/shared.ts"; diff --git a/apps/native/src-tauri/src/shared_types/feedback.rs b/apps/native/src-tauri/src/shared_types/feedback.rs index 94394628b..5efa5ae45 100644 --- a/apps/native/src-tauri/src/shared_types/feedback.rs +++ b/apps/native/src-tauri/src/shared_types/feedback.rs @@ -43,8 +43,7 @@ pub struct FeedbackSystemInfo { } /// Aggregated usage stats for feedback. -#[allow(dead_code)] -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Type)] #[serde(rename_all = "camelCase")] pub struct FeedbackUsageStats { /// Number of evolutions recorded locally. @@ -117,8 +116,7 @@ pub struct FeedbackMetadataRequest { } /// Metadata collected for feedback submission based on user opt-in. -#[allow(dead_code)] -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Type)] #[serde(rename_all = "camelCase")] pub struct FeedbackMetadata { /// Current frontend/store snapshot, represented as arbitrary JSON. diff --git a/apps/native/src/components/widget/feedback/feedback-dialog.tsx b/apps/native/src/components/widget/feedback/feedback-dialog.tsx index b3a8ec743..9595962b0 100644 --- a/apps/native/src/components/widget/feedback/feedback-dialog.tsx +++ b/apps/native/src/components/widget/feedback/feedback-dialog.tsx @@ -385,15 +385,15 @@ export function FeedbackDialog() { share: shareOptions, // artifact fields left empty for now; will be populated by caller when collecting logs lastPromptText: selectedPromptText, - currentAppStateSnapshot: metadata?.currentAppStateSnapshot, - systemInfo: metadata?.systemInfo, - usageStats: metadata?.usageStats, - evolutionLogContent: metadata?.evolutionLogContent, - changedNixFilesDiff: metadata?.changedNixFilesDiff, - aiProviderModelInfo: metadata?.aiProviderModelInfo, - buildErrorOutput: metadata?.buildErrorOutput, - flakeInputsSnapshot: metadata?.flakeInputsSnapshot, - appLogsContent: metadata?.appLogsContent, + currentAppStateSnapshot: metadata?.currentAppStateSnapshot ?? undefined, + systemInfo: metadata?.systemInfo ?? undefined, + usageStats: metadata?.usageStats ?? undefined, + evolutionLogContent: metadata?.evolutionLogContent ?? undefined, + changedNixFilesDiff: metadata?.changedNixFilesDiff ?? undefined, + aiProviderModelInfo: metadata?.aiProviderModelInfo ?? undefined, + buildErrorOutput: metadata?.buildErrorOutput ?? undefined, + flakeInputsSnapshot: metadata?.flakeInputsSnapshot ?? undefined, + appLogsContent: metadata?.appLogsContent ?? undefined, panicDetails: feedbackType === FeedbackType.Error ? (panicDetails ?? undefined) : undefined, }); diff --git a/apps/native/src/components/widget/history/history-item-card.tsx b/apps/native/src/components/widget/history/history-item-card.tsx index 5ec9a2c18..c4f90cec9 100644 --- a/apps/native/src/components/widget/history/history-item-card.tsx +++ b/apps/native/src/components/widget/history/history-item-card.tsx @@ -10,7 +10,7 @@ import { HistoryItemMeta } from "@/components/widget/history/history-item-meta"; import { HistoryRestoreItemButton } from "@/components/widget/history/history-restore-item-button"; import { useHistoryCard } from "@/hooks/use-history-card"; import { cn } from "@/lib/utils"; -import type { HistoryItem } from "@/tauri-api"; +import type { HistoryItem } from "@/types/shared"; import type { TimelineContext } from "@/components/widget/history/timeline-connector"; import { HistoryItemTimeline, TimeLineConnector, TimelineDot } from "@/components/widget/history/timeline-connector"; diff --git a/apps/native/src/components/widget/history/history-item-expanded-detail.tsx b/apps/native/src/components/widget/history/history-item-expanded-detail.tsx index 4662f7531..df56761ba 100644 --- a/apps/native/src/components/widget/history/history-item-expanded-detail.tsx +++ b/apps/native/src/components/widget/history/history-item-expanded-detail.tsx @@ -1,4 +1,4 @@ -import type { HistoryItem } from "@/tauri-api"; +import type { HistoryItem } from "@/types/shared"; import type { ColorMap } from "@/components/widget/utils"; import { SinglesSection } from "@/components/widget/summaries/singles-section"; import { SummaryGroup } from "@/components/widget/summaries/summary-group"; diff --git a/apps/native/src/components/widget/history/history-item-meta.tsx b/apps/native/src/components/widget/history/history-item-meta.tsx index 88c444c1f..83151c1b5 100644 --- a/apps/native/src/components/widget/history/history-item-meta.tsx +++ b/apps/native/src/components/widget/history/history-item-meta.tsx @@ -1,4 +1,4 @@ -import type { HistoryItem } from "@/tauri-api"; +import type { HistoryItem } from "@/types/shared"; import { CommitHashBadge } from "@/components/widget/badges/commit-hash-badge"; import { FileCountBadge } from "@/components/widget/badges/file-count-badge"; import { TimeBadge } from "@/components/widget/badges/time-badge"; diff --git a/apps/native/src/components/widget/promptinput/system-defaults-cta.tsx b/apps/native/src/components/widget/promptinput/system-defaults-cta.tsx index 4257cfa22..1b4fa50cb 100644 --- a/apps/native/src/components/widget/promptinput/system-defaults-cta.tsx +++ b/apps/native/src/components/widget/promptinput/system-defaults-cta.tsx @@ -7,7 +7,7 @@ import { PopoverTrigger, } from "@/components/ui/popover"; import { useWidgetStore } from "@/stores/widget-store"; -import type { SystemDefault, SystemDefaultsScan } from "@/tauri-api"; +import type { SystemDefault, SystemDefaultsScan } from "@/types/shared"; import { darwinAPI } from "@/tauri-api"; import { Monitor, X } from "lucide-react"; import { useCallback, useEffect, useMemo, useState } from "react"; diff --git a/apps/native/src/components/widget/settings/ai-models-tab.tsx b/apps/native/src/components/widget/settings/ai-models-tab.tsx index 3c85b6952..093c012c2 100644 --- a/apps/native/src/components/widget/settings/ai-models-tab.tsx +++ b/apps/native/src/components/widget/settings/ai-models-tab.tsx @@ -9,7 +9,8 @@ import { } from "@/components/ui/select"; import { ModelCombobox } from "@/components/widget/controls/model-combobox"; import { getProviderConfigInvalidReason, isCliProvider } from "@/lib/ai-provider-validation"; -import { darwinAPI, DEFAULT_MAX_ITERATIONS, type CliToolsState } from "@/tauri-api"; +import { darwinAPI, DEFAULT_MAX_ITERATIONS } from "@/tauri-api"; +import type { CliToolsState } from "@/types/shared"; import type { AnyFieldApi, ReactFormExtendedApi } from "@tanstack/react-form"; import { Info } from "lucide-react"; import { useEffect, useState } from "react"; diff --git a/apps/native/src/components/widget/steps/permissions-step.tsx b/apps/native/src/components/widget/steps/permissions-step.tsx index c222c7b32..f20697f74 100644 --- a/apps/native/src/components/widget/steps/permissions-step.tsx +++ b/apps/native/src/components/widget/steps/permissions-step.tsx @@ -1,7 +1,8 @@ "use client"; import { useWidgetStore } from "@/stores/widget-store"; -import { darwinAPI, type Permission, type PermissionStatus } from "@/tauri-api"; +import { darwinAPI } from "@/tauri-api"; +import type { Permission, PermissionStatus } from "@/types/shared"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { Shield, Check, X, AlertCircle, Loader2 } from "lucide-react"; diff --git a/apps/native/src/hooks/use-evolve.ts b/apps/native/src/hooks/use-evolve.ts index aaee525dd..b18de7402 100644 --- a/apps/native/src/hooks/use-evolve.ts +++ b/apps/native/src/hooks/use-evolve.ts @@ -1,5 +1,6 @@ import { useWidgetStore } from "@/stores/widget-store"; -import { darwinAPI, EVOLVE_EVENT_CHANNEL, ipcRenderer, type EvolveEvent } from "@/tauri-api"; +import { darwinAPI, EVOLVE_EVENT_CHANNEL, ipcRenderer } from "@/tauri-api"; +import type { EvolveEvent } from "@/types/shared"; import { useCallback } from "react"; import { useGitOperations } from "./use-git-operations"; import { usePromptHistory } from "./use-prompt-history"; diff --git a/apps/native/src/hooks/use-history-card.ts b/apps/native/src/hooks/use-history-card.ts index 9c1a54eef..125ac91b9 100644 --- a/apps/native/src/hooks/use-history-card.ts +++ b/apps/native/src/hooks/use-history-card.ts @@ -1,6 +1,6 @@ import { useCallback, useState } from "react"; import type { KeyboardEvent } from "react"; -import type { HistoryItem } from "@/tauri-api"; +import type { HistoryItem } from "@/types/shared"; import { cn } from "@/lib/utils"; import { buildColorMap } from "@/components/widget/utils"; import type { ColorMap } from "@/components/widget/utils"; diff --git a/apps/native/src/hooks/use-history-restore.ts b/apps/native/src/hooks/use-history-restore.ts index 732451872..7c798b6c1 100644 --- a/apps/native/src/hooks/use-history-restore.ts +++ b/apps/native/src/hooks/use-history-restore.ts @@ -3,7 +3,7 @@ import { useWidgetStore } from "@/stores/widget-store"; import { useRebuildStream } from "@/hooks/use-rebuild-stream"; import { useHistory } from "@/hooks/use-history"; import { darwinAPI } from "@/tauri-api"; -import type { HistoryItem } from "@/tauri-api"; +import type { HistoryItem } from "@/types/shared"; // Sentinel hash used to identify the frontend-only preview item. export const PREVIEW_ITEM_HASH = "n1xm4c0"; diff --git a/apps/native/src/hooks/use-nix-install.ts b/apps/native/src/hooks/use-nix-install.ts index 17d9c1531..305981dcb 100644 --- a/apps/native/src/hooks/use-nix-install.ts +++ b/apps/native/src/hooks/use-nix-install.ts @@ -1,11 +1,6 @@ import { useWidgetStore } from "@/stores/widget-store"; -import { - darwinAPI, - ipcRenderer, - type NixDarwinRebuildEndEvent, - type NixInstallEndEvent, - type NixInstallProgressEvent, -} from "@/tauri-api"; +import { darwinAPI, ipcRenderer } from "@/tauri-api"; +import type { NixDarwinRebuildEndEvent, NixInstallEndEvent, NixInstallProgressEvent } from "@/types/shared"; import { useCallback } from "react"; export function useNixInstall() { diff --git a/apps/native/src/hooks/use-panic-handler.ts b/apps/native/src/hooks/use-panic-handler.ts index e7df81462..e0589812e 100644 --- a/apps/native/src/hooks/use-panic-handler.ts +++ b/apps/native/src/hooks/use-panic-handler.ts @@ -5,7 +5,7 @@ import { useEffect } from "react"; import { listen } from "@tauri-apps/api/event"; import { toast } from "sonner"; import { useWidgetStore } from "@/stores/widget-store"; -import type { RustPanicEvent } from "@/tauri-api"; +import type { RustPanicEvent } from "@/types/shared"; import { FeedbackType } from "@/types/feedback"; export function usePanicHandler() { diff --git a/apps/native/src/hooks/use-permissions.test.ts b/apps/native/src/hooks/use-permissions.test.ts index 0273df4e0..0e39e6b0f 100644 --- a/apps/native/src/hooks/use-permissions.test.ts +++ b/apps/native/src/hooks/use-permissions.test.ts @@ -1,4 +1,4 @@ -import type { PermissionsState } from "@/tauri-api"; +import type { PermissionsState } from "@/types/shared"; import { act, renderHook } from "@testing-library/react"; import { beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/apps/native/src/hooks/use-permissions.ts b/apps/native/src/hooks/use-permissions.ts index ce68f3952..6f312b7d1 100644 --- a/apps/native/src/hooks/use-permissions.ts +++ b/apps/native/src/hooks/use-permissions.ts @@ -1,6 +1,6 @@ import { useWidgetStore } from "@/stores/widget-store"; import { darwinAPI } from "@/tauri-api"; -import type { PermissionStatus, PermissionsState } from "@/tauri-api"; +import type { PermissionStatus, PermissionsState } from "@/types/shared"; import { useCallback } from "react"; /** diff --git a/apps/native/src/hooks/use-queue-summarizer.ts b/apps/native/src/hooks/use-queue-summarizer.ts index 440cf5664..8aa21d1f1 100644 --- a/apps/native/src/hooks/use-queue-summarizer.ts +++ b/apps/native/src/hooks/use-queue-summarizer.ts @@ -1,6 +1,7 @@ import { useHistory } from "@/hooks/use-history"; import { useWidgetStore } from "@/stores/widget-store"; -import { ipcRenderer, type SummarizerUpdateEvent } from "@/tauri-api"; +import { ipcRenderer } from "@/tauri-api"; +import type { SummarizerUpdateEvent } from "@/types/shared"; import { useCallback, useRef } from "react"; /** diff --git a/apps/native/src/hooks/use-rebuild-stream.ts b/apps/native/src/hooks/use-rebuild-stream.ts index 088308ece..6aa2c7293 100644 --- a/apps/native/src/hooks/use-rebuild-stream.ts +++ b/apps/native/src/hooks/use-rebuild-stream.ts @@ -1,11 +1,6 @@ import { useWidgetStore, type RebuildContext } from "@/stores/widget-store"; -import { - darwinAPI, - ipcRenderer, - type DarwinApplyDataEvent, - type DarwinApplyEndEvent, - type DarwinApplySummaryEvent, -} from "@/tauri-api"; +import { darwinAPI, ipcRenderer } from "@/tauri-api"; +import type { DarwinApplyDataEvent, DarwinApplyEndEvent, DarwinApplySummaryEvent } from "@/types/shared"; import { useCallback, useRef } from "react"; import { useGitOperations } from "./use-git-operations"; diff --git a/apps/native/src/lib/ai-provider-validation.ts b/apps/native/src/lib/ai-provider-validation.ts index f44d90d37..69f16a399 100644 --- a/apps/native/src/lib/ai-provider-validation.ts +++ b/apps/native/src/lib/ai-provider-validation.ts @@ -1,4 +1,4 @@ -import type { CliToolsState, DarwinPrefs } from "@/tauri-api"; +import type { CliToolsState, UiPrefs as DarwinPrefs } from "@/types/shared"; const CLI_PROVIDER_VALUES = ["claude", "codex", "opencode"] as const; diff --git a/apps/native/src/preview-indicator-window.tsx b/apps/native/src/preview-indicator-window.tsx index 8cae67db6..0211ca64d 100644 --- a/apps/native/src/preview-indicator-window.tsx +++ b/apps/native/src/preview-indicator-window.tsx @@ -3,7 +3,7 @@ import { listen } from "@tauri-apps/api/event"; import React, { useEffect, useState } from "react"; import ReactDOM from "react-dom/client"; import { PreviewIndicator } from "@/components/preview-indicator/preview-indicator"; -import type { PreviewIndicatorState } from "@/tauri-api"; +import type { PreviewIndicatorState } from "@/types/shared"; import "./index.css"; function PreviewIndicatorWindow() { diff --git a/apps/native/src/stores/widget-store.test.ts b/apps/native/src/stores/widget-store.test.ts index 658bd01b0..3c108cd90 100644 --- a/apps/native/src/stores/widget-store.test.ts +++ b/apps/native/src/stores/widget-store.test.ts @@ -1,4 +1,4 @@ -import type { EvolveEvent, GitStatus } from "@/tauri-api"; +import type { EvolveEvent, GitStatus } from "@/types/shared"; import { describe, expect, it } from "vitest"; import { createWidgetStore, diff --git a/apps/native/src/stores/widget-store.ts b/apps/native/src/stores/widget-store.ts index 6af04555f..70492d79a 100644 --- a/apps/native/src/stores/widget-store.ts +++ b/apps/native/src/stores/widget-store.ts @@ -1,4 +1,5 @@ import { computeCurrentStep } from "@/components/widget/utils"; +import { FeedbackType } from "@/types/feedback"; import type { EvolveEvent, EvolveState, @@ -6,17 +7,16 @@ import type { HistoryItem, PermissionsState, RecommendedPrompt, -} from "@/tauri-api"; -import { FeedbackType } from "@/types/feedback"; -import type { SemanticChangeMap } from "@/types/shared"; + SemanticChangeMap, +} from "@/types/shared"; import { create } from "zustand"; import { devtools } from "zustand/middleware"; export type { EvolveEvent, - EvolveEventType, EvolveState, + EvolveEventType, + EvolveState, GitStatus, - -} from "@/tauri-api"; +} from "@/types/shared"; // ============================================================================= // Types @@ -25,11 +25,31 @@ export type { /** * Widget step state - updated by useEffect based on app state. */ -export type SettingsTab = "general" | "api-keys" | "ai-models" | "preferences" | "developer"; -export type WidgetStep = "permissions" | "nix-setup" | "setup" | "begin" | "evolve" | "commit" | "manualEvolve" | "manualCommit" | "history"; +export type SettingsTab = + | "general" + | "api-keys" + | "ai-models" + | "preferences" + | "developer"; +export type WidgetStep = + | "permissions" + | "nix-setup" + | "setup" + | "begin" + | "evolve" + | "commit" + | "manualEvolve" + | "manualCommit" + | "history"; type ProcessingAction = "evolve" | "apply" | "commit" | "cancel" | null; -export type ConfirmPrefKey = "confirmBuild" | "confirmClear" | "confirmRollback"; -export type BoolPrefKey = ConfirmPrefKey | "autoSummarizeOnFocus" | "scanHomebrewOnStartup"; +export type ConfirmPrefKey = + | "confirmBuild" + | "confirmClear" + | "confirmRollback"; +export type BoolPrefKey = + | ConfirmPrefKey + | "autoSummarizeOnFocus" + | "scanHomebrewOnStartup"; // Rebuild state for showing progress inline in the widget export type RebuildErrorType = @@ -76,7 +96,11 @@ export interface WidgetState { // Nix installation nixInstalled: boolean | null; // null = not checked yet nixInstalling: boolean; - nixInstallPhase: "downloading" | "waiting-for-installer" | "prefetching" | null; + nixInstallPhase: + | "downloading" + | "waiting-for-installer" + | "prefetching" + | null; nixDownloadProgress: { downloaded: number; total: number } | null; // nix-darwin (darwin-rebuild availability) @@ -167,7 +191,9 @@ interface WidgetActions { setNixInstallPhase: ( phase: "downloading" | "waiting-for-installer" | "prefetching" | null, ) => void; - setNixDownloadProgress: (progress: { downloaded: number; total: number } | null) => void; + setNixDownloadProgress: ( + progress: { downloaded: number; total: number } | null, + ) => void; setDarwinRebuildAvailable: (available: boolean | null) => void; setDarwinRebuildPrefetching: (prefetching: boolean) => void; setEvolveState: (state: EvolveState | null) => void; @@ -182,7 +208,12 @@ interface WidgetActions { setFeedbackOpen: (open: boolean) => void; setError: (error: string | null) => void; setPanicDetails: ( - details: { message: string; location?: string; backtrace?: string; timestamp: string } | null, + details: { + message: string; + location?: string; + backtrace?: string; + timestamp: string; + } | null, ) => void; setPromptHistory: (history: string[]) => void; setRecommendedPrompt: (prompt: RecommendedPrompt | null | undefined) => void; @@ -346,138 +377,152 @@ export function createWidgetStore(initialState?: Partial) { return create()( devtools( (set, _get) => ({ - ...initialWidgetState, - ...initialState, - - // Permissions - setPermissionsState: (permissionsState) => set({ permissionsState }), - setPermissionsChecked: (permissionsChecked) => set({ permissionsChecked }), - - // Setters - setConfigDir: (configDir) => set({ configDir }), - setHosts: (hosts) => set({ hosts }), - setHost: (host) => set({ host }), - setEvolveState: (evolveState) => set({ evolveState: evolveState }), - setExternalBuildDetected: (externalBuildDetected) => set({ externalBuildDetected }), - setGitStatus: (gitStatus) => set({ gitStatus }), - setEvolvePrompt: (evolvePrompt) => set({ evolvePrompt }), - setProcessing: (isProcessing, action = null) => - set({ - isProcessing, - processingAction: isProcessing ? action : null, - }), - setChangeMap: (changeMap) => set({ changeMap }), - setBoolPref: (key: BoolPrefKey, value: boolean) => set({ [key]: value }), - initConfirmPrefs: (prefs) => - set({ - confirmBuild: prefs.confirmBuild ?? true, - confirmClear: prefs.confirmClear ?? true, - confirmRollback: prefs.confirmRollback ?? true, - }), - setAutoSummarizeOnFocus: (value) => set({ autoSummarizeOnFocus: value }), - setDeveloperMode: (value) => set({ developerMode: value }), - setPinnedVersion: (value) => set({ pinnedVersion: value }), - setHistory: (history) => set({ history }), - setHistoryLoading: (historyLoading) => set({ historyLoading }), - addAnalyzingHistoryHash: (hash) => - set((state) => ({ - analyzingHistoryForHashes: new Set([...state.analyzingHistoryForHashes, hash]), - })), - removeAnalyzingHistoryHash: (hash) => - set((state) => { - const next = new Set(state.analyzingHistoryForHashes); - next.delete(hash); - return { analyzingHistoryForHashes: next }; - }), - setSettingsOpen: (settingsOpen, tab) => - set({ settingsOpen, settingsActiveTab: tab ?? null }), - setPrefsLoaded: (prefsLoaded) => set({ prefsLoaded }), - setShowHistory: (showHistory) => set({ showHistory }), - setFeedbackOpen: (feedbackOpen) => set({ feedbackOpen }), - setFeedbackTypeOverride: (feedbackTypeOverride) => set({ feedbackTypeOverride }), - openFeedback: (type, initialText) => - set({ - feedbackOpen: true, - feedbackTypeOverride: type ?? null, - feedbackInitialText: initialText ?? null, - }), - setError: (error) => set({ error }), - setPanicDetails: (panicDetails) => set({ panicDetails }), - setPromptHistory: (promptHistory) => set({ promptHistory }), - setRecommendedPrompt: (recommendedPrompt) => set({ recommendedPrompt }), - - // Client-side UI state (NOT from server) - setBootstrapping: (isBootstrapping) => set({ isBootstrapping }), - setNixInstalled: (nixInstalled) => set({ nixInstalled }), - setNixInstalling: (nixInstalling) => set({ nixInstalling }), - setNixInstallPhase: (nixInstallPhase) => set({ nixInstallPhase }), - setNixDownloadProgress: (nixDownloadProgress) => set({ nixDownloadProgress }), - setDarwinRebuildAvailable: (darwinRebuildAvailable) => set({ darwinRebuildAvailable }), - setDarwinRebuildPrefetching: (darwinRebuildPrefetching) => set({ darwinRebuildPrefetching }), - setSummarizing: (isSummarizing) => set({ isSummarizing }), - setGenerating: (isGenerating) => set({ isGenerating }), - - // Console - appendLog: (text) => set((state) => ({ consoleLogs: state.consoleLogs + text })), - clearLogs: () => set({ consoleLogs: "" }), - - // Evolve events - appendEvolveEvent: (event) => - set((state) => ({ evolveEvents: [...state.evolveEvents, event] })), - clearEvolveEvents: () => set({ evolveEvents: [] }), - - // Conversational response - setConversationalResponse: (conversationalResponse) => set({ conversationalResponse }), - - // Commit message suggestion - setCommitMessageSuggestion: (commitMessageSuggestion) => set({ commitMessageSuggestion }), - - // Rebuild state - startRebuild: (context) => - set({ - rebuild: { - isRunning: true, - context, - lines: [{ id: 0, text: "Preparing rebuild...", type: "info" }], - rawLines: [], - exitCode: undefined, - success: undefined, - errorType: undefined, - errorMessage: undefined, - }, - }), - appendRebuildLine: (line) => - set((state) => ({ - rebuild: { - ...state.rebuild, - lines: [...state.rebuild.lines, line].slice(-50), // Keep last 50 lines - }, - })), - appendRawLine: (line) => - set((state) => ({ - rebuild: { - ...state.rebuild, - rawLines: [...state.rebuild.rawLines, line].slice(-500), // Keep last 500 raw lines - }, - })), - setRebuildError: (errorType, errorMessage) => - set((state) => ({ - rebuild: { - ...state.rebuild, - errorType, - errorMessage, - }, - })), - setRebuildComplete: (success, exitCode) => - set((state) => ({ - rebuild: { - ...state.rebuild, - isRunning: false, - success, - exitCode, - }, - })), - clearRebuild: () => set({ rebuild: initialRebuildState }), + ...initialWidgetState, + ...initialState, + + // Permissions + setPermissionsState: (permissionsState) => set({ permissionsState }), + setPermissionsChecked: (permissionsChecked) => + set({ permissionsChecked }), + + // Setters + setConfigDir: (configDir) => set({ configDir }), + setHosts: (hosts) => set({ hosts }), + setHost: (host) => set({ host }), + setEvolveState: (evolveState) => set({ evolveState: evolveState }), + setExternalBuildDetected: (externalBuildDetected) => + set({ externalBuildDetected }), + setGitStatus: (gitStatus) => set({ gitStatus }), + setEvolvePrompt: (evolvePrompt) => set({ evolvePrompt }), + setProcessing: (isProcessing, action = null) => + set({ + isProcessing, + processingAction: isProcessing ? action : null, + }), + setChangeMap: (changeMap) => set({ changeMap }), + setBoolPref: (key: BoolPrefKey, value: boolean) => + set({ [key]: value }), + initConfirmPrefs: (prefs) => + set({ + confirmBuild: prefs.confirmBuild ?? true, + confirmClear: prefs.confirmClear ?? true, + confirmRollback: prefs.confirmRollback ?? true, + }), + setAutoSummarizeOnFocus: (value) => + set({ autoSummarizeOnFocus: value }), + setDeveloperMode: (value) => set({ developerMode: value }), + setPinnedVersion: (value) => set({ pinnedVersion: value }), + setHistory: (history) => set({ history }), + setHistoryLoading: (historyLoading) => set({ historyLoading }), + addAnalyzingHistoryHash: (hash) => + set((state) => ({ + analyzingHistoryForHashes: new Set([ + ...state.analyzingHistoryForHashes, + hash, + ]), + })), + removeAnalyzingHistoryHash: (hash) => + set((state) => { + const next = new Set(state.analyzingHistoryForHashes); + next.delete(hash); + return { analyzingHistoryForHashes: next }; + }), + setSettingsOpen: (settingsOpen, tab) => + set({ settingsOpen, settingsActiveTab: tab ?? null }), + setPrefsLoaded: (prefsLoaded) => set({ prefsLoaded }), + setShowHistory: (showHistory) => set({ showHistory }), + setFeedbackOpen: (feedbackOpen) => set({ feedbackOpen }), + setFeedbackTypeOverride: (feedbackTypeOverride) => + set({ feedbackTypeOverride }), + openFeedback: (type, initialText) => + set({ + feedbackOpen: true, + feedbackTypeOverride: type ?? null, + feedbackInitialText: initialText ?? null, + }), + setError: (error) => set({ error }), + setPanicDetails: (panicDetails) => set({ panicDetails }), + setPromptHistory: (promptHistory) => set({ promptHistory }), + setRecommendedPrompt: (recommendedPrompt) => set({ recommendedPrompt }), + + // Client-side UI state (NOT from server) + setBootstrapping: (isBootstrapping) => set({ isBootstrapping }), + setNixInstalled: (nixInstalled) => set({ nixInstalled }), + setNixInstalling: (nixInstalling) => set({ nixInstalling }), + setNixInstallPhase: (nixInstallPhase) => set({ nixInstallPhase }), + setNixDownloadProgress: (nixDownloadProgress) => + set({ nixDownloadProgress }), + setDarwinRebuildAvailable: (darwinRebuildAvailable) => + set({ darwinRebuildAvailable }), + setDarwinRebuildPrefetching: (darwinRebuildPrefetching) => + set({ darwinRebuildPrefetching }), + setSummarizing: (isSummarizing) => set({ isSummarizing }), + setGenerating: (isGenerating) => set({ isGenerating }), + + // Console + appendLog: (text) => + set((state) => ({ consoleLogs: state.consoleLogs + text })), + clearLogs: () => set({ consoleLogs: "" }), + + // Evolve events + appendEvolveEvent: (event) => + set((state) => ({ evolveEvents: [...state.evolveEvents, event] })), + clearEvolveEvents: () => set({ evolveEvents: [] }), + + // Conversational response + setConversationalResponse: (conversationalResponse) => + set({ conversationalResponse }), + + // Commit message suggestion + setCommitMessageSuggestion: (commitMessageSuggestion) => + set({ commitMessageSuggestion }), + + // Rebuild state + startRebuild: (context) => + set({ + rebuild: { + isRunning: true, + context, + lines: [{ id: 0, text: "Preparing rebuild...", type: "info" }], + rawLines: [], + exitCode: undefined, + success: undefined, + errorType: undefined, + errorMessage: undefined, + }, + }), + appendRebuildLine: (line) => + set((state) => ({ + rebuild: { + ...state.rebuild, + lines: [...state.rebuild.lines, line].slice(-50), // Keep last 50 lines + }, + })), + appendRawLine: (line) => + set((state) => ({ + rebuild: { + ...state.rebuild, + rawLines: [...state.rebuild.rawLines, line].slice(-500), // Keep last 500 raw lines + }, + })), + setRebuildError: (errorType, errorMessage) => + set((state) => ({ + rebuild: { + ...state.rebuild, + errorType, + errorMessage, + }, + })), + setRebuildComplete: (success, exitCode) => + set((state) => ({ + rebuild: { + ...state.rebuild, + isRunning: false, + success, + exitCode, + }, + })), + clearRebuild: () => set({ rebuild: initialRebuildState }), }), { name: "widget-store", diff --git a/apps/native/src/tauri-api.ts b/apps/native/src/tauri-api.ts index 5f872ee6c..f532ab999 100644 --- a/apps/native/src/tauri-api.ts +++ b/apps/native/src/tauri-api.ts @@ -13,11 +13,8 @@ import type { EvolveCancelResult, EvolutionResult, EvolveState, - FeedbackAiProviderModelInfo, - FeedbackFlakeInputsSnapshot, - FeedbackPanicDetails, + FeedbackMetadata, FeedbackShareOptions, - FeedbackSystemInfo, FinalizeApplyResult, GitStatus, HomebrewState, @@ -37,91 +34,8 @@ import type { UiPrefsUpdate as DarwinPrefsUpdate, } from "./types/shared"; -export type { - BuildCheckResult, - ChangeType, - CliToolsState, - CommitResult, - Config as DarwinConfig, - ConfigChangedEvent, - ConfigEditApplyResult, - DarwinApplyDataEvent, - DarwinApplyEndEvent, - DarwinApplySummaryEvent, - EvolveCancelResult, - EvolveEvent, - EvolveEventType, - EvolutionFailureResult, - EvolutionResult, - EvolutionState, - EvolutionTelemetry, - EvolveState, - EvolveStep, - FeedbackAiProviderModelInfo, - FeedbackFlakeInputEntry, - FeedbackFlakeInputsSnapshot, - FeedbackMetadataRequest, - FeedbackPanicDetails, - FeedbackShareOptions, - FeedbackSystemInfo, - FinalizeApplyResult, - GitFileStatus, - GitStatus, - HomebrewState, - HistoryItem, - NixCheckResult, - NixDarwinRebuildEndEvent, - NixInstallEndEvent, - NixInstallErrorType, - NixInstallPhase, - NixInstallProgressEvent, - OkResult, - Permission, - PermissionStatus, - PermissionsState, - PreviewIndicatorState, - RecommendedPrompt, - RebuildErrorType, - SemanticChangeMap, - SetDirResult, - SummarizerUpdateEvent, - SummarizedChangeSet, - RustPanicEvent, - SystemDefault, - SystemDefaultsScan, - UiPrefs as DarwinPrefs, - UiPrefsUpdate as DarwinPrefsUpdate, - WatcherEvent, -} from "./types/shared"; -export type { Change, Commit } from "./types/sqlite"; - export const DEFAULT_MAX_ITERATIONS = 25; -// ============================================================================= -// Feedback Types -// ============================================================================= - -export interface FeedbackUsageStats { - totalEvolutions?: number; - successRate?: number; - avgIterations?: number; - lastComputedAt?: string; - extra?: Record; -} - -export interface FeedbackMetadata { - currentAppStateSnapshot?: unknown; - systemInfo?: FeedbackSystemInfo; - usageStats?: FeedbackUsageStats; - evolutionLogContent?: string; - changedNixFilesDiff?: string; - aiProviderModelInfo?: FeedbackAiProviderModelInfo; - buildErrorOutput?: string; - flakeInputsSnapshot?: FeedbackFlakeInputsSnapshot; - appLogsContent?: string; - panicDetails?: FeedbackPanicDetails; -} - export const EVOLVE_EVENT_CHANNEL = "darwin:evolve:event"; export const CONFIG_CHANGED_CHANNEL = "config:changed"; diff --git a/apps/native/src/types/feedback.ts b/apps/native/src/types/feedback.ts index 3a950b23d..f59144fd4 100644 --- a/apps/native/src/types/feedback.ts +++ b/apps/native/src/types/feedback.ts @@ -1,3 +1,17 @@ +import type { + FeedbackAiProviderModelInfo, + FeedbackFlakeInputsSnapshot, + FeedbackSystemInfo, + FeedbackUsageStats, +} from "./shared"; + +export type { + FeedbackAiProviderModelInfo, + FeedbackFlakeInputsSnapshot, + FeedbackSystemInfo, + FeedbackUsageStats, +}; + /** * The high-level categories of feedback the user can submit. * @@ -32,64 +46,10 @@ export interface ShareOptions { lastPrompt?: boolean; } -/** - * System information captured from the runtime. Fields are optional so - * collectors can populate what is available on the platform. - */ -interface SystemInfo { - osName?: string | null; // e.g. "macOS" - osVersion?: string | null; // e.g. "15.3" - arch?: string | null; // e.g. "aarch64-darwin" - nixVersion?: string | null; // e.g. "2.24.1" - appVersion?: string | null; // app build/version string -} - -/** - * Aggregated nixmac usage statistics from the app. - * Fields are optional and may be filled by the runtime when - * the user opts-in to sharing usage stats. - */ -interface UsageStats { - /** Total number of evolutions the user has run */ - totalEvolutions?: number; - /** Success rate as a percentage (0.0 - 100.0) of evolutions */ - successRate?: number; - /** Average number of iterations per evolution */ - avgIterations?: number; - /** ISO timestamp when these stats were last computed */ - lastComputedAt?: string; - extra?: Record; -} - -/** - * AI provider/model details and usage signals captured from the app. - * Fields are optional and may be partially populated. - */ -interface AiProviderModelInfo { - evolveProvider?: string | null; - evolveModel?: string | null; - summaryProvider?: string | null; - summaryModel?: string | null; - totalTokens?: number | null; - latencyMs?: number | null; - iterations?: number | null; - buildAttempts?: number | null; -} - -/** - * Flake.lock input metadata (subset) captured from the user's configuration. - */ -interface FlakeInputEntry { - rev?: string | null; - lastModified?: number | null; - narHash?: string | null; -} - -interface FlakeInputsSnapshot { - nixpkgs?: FlakeInputEntry | null; - "nix-darwin"?: FlakeInputEntry | null; - "home-manager"?: FlakeInputEntry | null; -} +type SystemInfo = FeedbackSystemInfo; +type UsageStats = FeedbackUsageStats; +type AiProviderModelInfo = FeedbackAiProviderModelInfo; +type FlakeInputsSnapshot = FeedbackFlakeInputsSnapshot; /** * The serializable shape of feedback that will be sent to the server. diff --git a/apps/native/src/types/shared.ts b/apps/native/src/types/shared.ts index 2a207f224..56a1769b1 100644 --- a/apps/native/src/types/shared.ts +++ b/apps/native/src/types/shared.ts @@ -406,7 +406,7 @@ export type EvolveEventType = /** * Agent is reading a file. */ -"reading" | +"reading" | "searchPackages" | /** * Agent is editing a file. */ @@ -589,6 +589,51 @@ nixpkgs: FeedbackFlakeInputEntry | null; */ "home-manager": FeedbackFlakeInputEntry | null } +/** + * Metadata collected for feedback submission based on user opt-in. + */ +export type FeedbackMetadata = { +/** + * Current frontend/store snapshot, represented as arbitrary JSON. + */ +currentAppStateSnapshot: JsonValue | null; +/** + * Runtime system information. + */ +systemInfo: FeedbackSystemInfo | null; +/** + * Aggregated local usage statistics. + */ +usageStats: FeedbackUsageStats | null; +/** + * Captured evolution log content. + */ +evolutionLogContent: string | null; +/** + * Diff for changed Nix files at submission time. + */ +changedNixFilesDiff: string | null; +/** + * AI provider/model metadata for the related run. + */ +aiProviderModelInfo: FeedbackAiProviderModelInfo | null; +/** + * Latest build error output. + */ +buildErrorOutput: string | null; +/** + * Selected locked flake input metadata. + */ +flakeInputsSnapshot: FeedbackFlakeInputsSnapshot | null; +/** + * Recent application log content. + */ +appLogsContent: string | null; +/** + * Panic details when feedback is submitted after a crash. + */ +panicDetails: FeedbackPanicDetails | null } + /** * Request payload for gathering feedback metadata. */ @@ -690,21 +735,29 @@ nixVersion: string | null; appVersion: string | null } /** - * File or directory entry returned by the editor tree. + * Aggregated usage stats for feedback. */ -export type FileEntry = { +export type FeedbackUsageStats = { /** - * Path relative to the selected config directory. + * Number of evolutions recorded locally. */ -path: string; +totalEvolutions: number | null; /** - * File or directory basename. + * Percentage of evolutions that completed successfully. */ -name: string; +successRate: number | null; +/** + * Average number of agent iterations per evolution. + */ +avgIterations: number | null; /** - * Whether this entry is a directory. + * Timestamp when the stats were computed. */ -isDir: boolean } +lastComputedAt: string | null; +/** + * Additional structured usage fields that are not part of the stable contract. + */ +extra: JsonValue | null } /** * Result of a successful `finalize_apply` or `finalize_rollback` command. @@ -863,6 +916,8 @@ source: string | null; */ lastChecked: number } +export type JsonValue = null | boolean | number | string | JsonValue[] | Partial<{ [key in string]: JsonValue }> + /** * Result of `nix_check` — reports whether Nix and darwin-rebuild are available. */ From 9cda571dcc4e7c5cc2d9d5adba0f09feffdcb406 Mon Sep 17 00:00:00 2001 From: CasLinden Date: Thu, 7 May 2026 20:37:07 +0900 Subject: [PATCH 08/13] chore(tauri-api): move constants --- apps/native/shared/constants.ts | 2 ++ .../components/widget/settings/ai-models-tab.tsx | 3 ++- .../components/widget/settings/settings-dialog.tsx | 3 ++- apps/native/src/components/widget/widget.test.tsx | 2 -- apps/native/src/hooks/use-evolve.ts | 3 ++- apps/native/src/tauri-api.ts | 13 ------------- 6 files changed, 8 insertions(+), 18 deletions(-) diff --git a/apps/native/shared/constants.ts b/apps/native/shared/constants.ts index 409dfaad8..5581e9912 100644 --- a/apps/native/shared/constants.ts +++ b/apps/native/shared/constants.ts @@ -1,5 +1,7 @@ // Shared app constants for easy renaming/rebranding export const APP_NAME = "nixmac"; +export const DEFAULT_MAX_ITERATIONS = 25; +export const EVOLVE_EVENT_CHANNEL = "darwin:evolve:event"; // If you change icons, update these to point to your bundled assets export const APP_TRAY_ICON = "image.png"; // under APP_ROOT / resources as used in main export const APP_OVERLAY_ICON = "/activation.png"; // served by Vite/public in dev diff --git a/apps/native/src/components/widget/settings/ai-models-tab.tsx b/apps/native/src/components/widget/settings/ai-models-tab.tsx index 093c012c2..1acacfae4 100644 --- a/apps/native/src/components/widget/settings/ai-models-tab.tsx +++ b/apps/native/src/components/widget/settings/ai-models-tab.tsx @@ -9,7 +9,8 @@ import { } from "@/components/ui/select"; import { ModelCombobox } from "@/components/widget/controls/model-combobox"; import { getProviderConfigInvalidReason, isCliProvider } from "@/lib/ai-provider-validation"; -import { darwinAPI, DEFAULT_MAX_ITERATIONS } from "@/tauri-api"; +import { DEFAULT_MAX_ITERATIONS } from "@/../shared/constants"; +import { darwinAPI } from "@/tauri-api"; import type { CliToolsState } from "@/types/shared"; import type { AnyFieldApi, ReactFormExtendedApi } from "@tanstack/react-form"; import { Info } from "lucide-react"; diff --git a/apps/native/src/components/widget/settings/settings-dialog.tsx b/apps/native/src/components/widget/settings/settings-dialog.tsx index 7006f80a2..77178c833 100644 --- a/apps/native/src/components/widget/settings/settings-dialog.tsx +++ b/apps/native/src/components/widget/settings/settings-dialog.tsx @@ -2,7 +2,8 @@ import { Button } from "@/components/ui/button"; import { useDarwinConfig } from "@/hooks/use-darwin-config"; import { cn } from "@/lib/utils"; import { type SettingsTab, useWidgetStore } from "@/stores/widget-store"; -import { darwinAPI, DEFAULT_MAX_ITERATIONS } from "@/tauri-api"; +import { DEFAULT_MAX_ITERATIONS } from "@/../shared/constants"; +import { darwinAPI } from "@/tauri-api"; import { useForm } from "@tanstack/react-form"; import { Bot, FolderOpen, Key, Settings2, SlidersHorizontal, Wrench } from "lucide-react"; import { Suspense, useEffect, useRef, useState } from "react"; diff --git a/apps/native/src/components/widget/widget.test.tsx b/apps/native/src/components/widget/widget.test.tsx index 9ad231462..8d5db0491 100644 --- a/apps/native/src/components/widget/widget.test.tsx +++ b/apps/native/src/components/widget/widget.test.tsx @@ -17,8 +17,6 @@ vi.mock("@/tauri-api", () => ({ ipcRenderer: { on: vi.fn().mockReturnValue(Promise.resolve(() => {})), }, - CONFIG_CHANGED_CHANNEL: "config-changed", - DEFAULT_MAX_ITERATIONS: 25, })); // Mock hooks diff --git a/apps/native/src/hooks/use-evolve.ts b/apps/native/src/hooks/use-evolve.ts index b18de7402..a89fdd9d9 100644 --- a/apps/native/src/hooks/use-evolve.ts +++ b/apps/native/src/hooks/use-evolve.ts @@ -1,5 +1,6 @@ import { useWidgetStore } from "@/stores/widget-store"; -import { darwinAPI, EVOLVE_EVENT_CHANNEL, ipcRenderer } from "@/tauri-api"; +import { EVOLVE_EVENT_CHANNEL } from "@/../shared/constants"; +import { darwinAPI, ipcRenderer } from "@/tauri-api"; import type { EvolveEvent } from "@/types/shared"; import { useCallback } from "react"; import { useGitOperations } from "./use-git-operations"; diff --git a/apps/native/src/tauri-api.ts b/apps/native/src/tauri-api.ts index f532ab999..1134df2ed 100644 --- a/apps/native/src/tauri-api.ts +++ b/apps/native/src/tauri-api.ts @@ -34,11 +34,6 @@ import type { UiPrefsUpdate as DarwinPrefsUpdate, } from "./types/shared"; -export const DEFAULT_MAX_ITERATIONS = 25; - -export const EVOLVE_EVENT_CHANNEL = "darwin:evolve:event"; -export const CONFIG_CHANGED_CHANNEL = "config:changed"; - export const darwinAPI = { config: { get: () => invoke("config_get"), @@ -183,14 +178,6 @@ export const ipcRenderer = { once(channel, listener), }; -// const w = new Window("lol"); -// w.once("tauri://window-created", (event) => { -// console.log(event); -// }); -// w.once("tauri://destroyed", (event) => { -// console.log(event); -// }); - declare global { interface Window { darwinAPI?: typeof darwinAPI; From 53ce6afe5f5e34482ddaff4968a8699217b86457 Mon Sep 17 00:00:00 2001 From: CasLinden Date: Thu, 7 May 2026 21:02:26 +0900 Subject: [PATCH 09/13] chore(tauri-api): update READMEs --- apps/native/src-tauri/src/README.md | 4 ++-- apps/native/src/README.md | 32 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 apps/native/src/README.md diff --git a/apps/native/src-tauri/src/README.md b/apps/native/src-tauri/src/README.md index 4b9a3f0c2..e80b13152 100644 --- a/apps/native/src-tauri/src/README.md +++ b/apps/native/src-tauri/src/README.md @@ -43,9 +43,9 @@ Provider abstraction layer for chat completions. **Not** evolve-specific. ### `commands/` — Tauri IPC handlers Thin `#[tauri::command]` handlers that the frontend calls via `invoke()`. -Each file (config, apply, evolve, git, rollback, summarize, homebrew, feedback, ui_prefs, permissions, system_defaults, cli_tool, updater, editor, evolve_state, peek, debug) delegates to the corresponding backend module. +Each file (apply, cli_tool, config, debug, editor, evolve, evolve_state, feedback, git, homebrew, peek, permissions, rollback, summarize, system_defaults, ui_prefs, updater) delegates to the corresponding backend module. `helpers.rs` contains shared command utilities. -**Called by:** Tauri invoke handler in main.rs; primary callers are the TypeScript frontend +**Called by:** Tauri invoke handler in `main.rs`. All frontend calls go through `tauri-api.ts` in the TypeScript layer. ### `db/` — SQLite persistence diff --git a/apps/native/src/README.md b/apps/native/src/README.md new file mode 100644 index 000000000..354ed6849 --- /dev/null +++ b/apps/native/src/README.md @@ -0,0 +1,32 @@ +# nixmac frontend source layout + +React + TypeScript frontend served by Vite inside a Tauri webview. + +## IPC Gateway + +`tauri-api.ts` mirrors the Rust command layer: handlers registered in `main.rs` have corresponding `invoke` calls here. Hooks and occasionally components call `darwinAPI.*`; `tauri-api.ts` reflects the handlers exposed one-to-one. + +### DarwinWidget + +`components/widget/widget.tsx` is the root of the app. It initializes state on mount and handles step routing. + +`components/ui/` — shadcn/ui primitives (buttons, dialogs, inputs, etc.) + +## Domain Hooks + +Hooks in `hooks/` sit between components and `tauri-api.ts`. Each hook owns a slice of async state: loading flags, error handling, IPC subscriptions. Components call hooks; (mostly) hooks call `darwinAPI`. + +## App State + +`widget-store.ts` is a Zustand store that holds widget step routing, git status, evolve state, UI preferences, and shared flags. Most hooks read from and write to this store. + +Components read from this store directly when possible to minimize prop-drilling. + +## Preview Indicator + +A separate floating overlay window (`preview-indicator-window.tsx`) shows uncommitted-change state as a corner widget. Its components live in `components/preview-indicator/`. The Rust side (`peek.rs`, `commands/peek.rs`) manages window visibility and state. + +## Shared Types + +- `shared.ts` — Auto-generated by Specta from Rust structs in `src-tauri/src/shared_types/`. Do not edit manually. +- `sqlite.ts` — Auto-generated SQLite row types. Same process. From 3f9becc4bc8d09538e8bf783e9570fe084a9268b Mon Sep 17 00:00:00 2001 From: CasLinden Date: Thu, 7 May 2026 21:18:01 +0900 Subject: [PATCH 10/13] chore(tests): update tauri-api mock and snapshots --- apps/native/.storybook/mocks/tauri-api.ts | 59 ++++++------------- .../__snapshots__/widget.stories.tsx.snap | 14 ++--- 2 files changed, 23 insertions(+), 50 deletions(-) diff --git a/apps/native/.storybook/mocks/tauri-api.ts b/apps/native/.storybook/mocks/tauri-api.ts index 37190e653..0b0e80fac 100644 --- a/apps/native/.storybook/mocks/tauri-api.ts +++ b/apps/native/.storybook/mocks/tauri-api.ts @@ -1,12 +1,7 @@ import { storybookDarwinAPI, tauriEvent } from "./tauri-runtime"; -export const DEFAULT_MAX_ITERATIONS = 25; - export const darwinAPI = storybookDarwinAPI; -export const EVOLVE_EVENT_CHANNEL = "darwin:evolve:event"; -export const CONFIG_CHANGED_CHANNEL = "config:changed"; - export const ipcRenderer = { on: tauriEvent.listen, once: tauriEvent.once, @@ -15,33 +10,40 @@ export const ipcRenderer = { // Re-export types from shared (Specta-generated) export type { BuildCheckResult, + Change, ChangeType, CliToolsState, Config as DarwinConfig, ConfigChangedEvent, + ConfigEditApplyResult, + Commit, + CommitResult, DarwinApplyDataEvent, DarwinApplyEndEvent, DarwinApplySummaryEvent, - EvolutionFailureResult, - EvolutionResult, - EvolutionState, - EvolutionTelemetry, + EvolveCancelResult, EvolveEvent, EvolveEventType, EvolveState, EvolveStep, + EvolutionFailureResult, + EvolutionResult, + EvolutionState, + EvolutionTelemetry, FeedbackAiProviderModelInfo, FeedbackFlakeInputEntry, FeedbackFlakeInputsSnapshot, + FeedbackMetadata, FeedbackMetadataRequest, FeedbackPanicDetails, FeedbackShareOptions, FeedbackSystemInfo, - FileEntry, + FeedbackUsageStats, + FinalizeApplyResult, GitFileStatus, GitStatus, - HomebrewState, HistoryItem, + HomebrewState, NixCheckResult, NixDarwinRebuildEndEvent, NixInstallEndEvent, @@ -53,44 +55,17 @@ export type { PermissionStatus, PermissionsState, PreviewIndicatorState, - RecommendedPrompt, RebuildErrorType, + RecommendedPrompt, + RollbackResult, + RustPanicEvent, SemanticChangeMap, SetDirResult, - SummarizerUpdateEvent, SummarizedChangeSet, - RustPanicEvent, + SummarizerUpdateEvent, SystemDefault, SystemDefaultsScan, UiPrefs as DarwinPrefs, UiPrefsUpdate as DarwinPrefsUpdate, WatcherEvent, - Change, - Commit, - CommitResult, - ConfigEditApplyResult, - EvolveCancelResult, - FinalizeApplyResult, - RollbackResult, } from "../../src/types/shared"; - -export interface FeedbackUsageStats { - totalEvolutions?: number; - successRate?: number; - avgIterations?: number; - lastComputedAt?: string; - extra?: Record; -} - -export interface FeedbackMetadata { - currentAppStateSnapshot?: unknown; - systemInfo?: import("../../src/types/shared").FeedbackSystemInfo; - usageStats?: FeedbackUsageStats; - evolutionLogContent?: string; - changedNixFilesDiff?: string; - aiProviderModelInfo?: import("../../src/types/shared").FeedbackAiProviderModelInfo; - buildErrorOutput?: string; - flakeInputsSnapshot?: import("../../src/types/shared").FeedbackFlakeInputsSnapshot; - appLogsContent?: string; - panicDetails?: import("../../src/types/shared").FeedbackPanicDetails; -} diff --git a/apps/native/src/components/widget/__snapshots__/widget.stories.tsx.snap b/apps/native/src/components/widget/__snapshots__/widget.stories.tsx.snap index 8f61ca6fb..17af62caa 100644 --- a/apps/native/src/components/widget/__snapshots__/widget.stories.tsx.snap +++ b/apps/native/src/components/widget/__snapshots__/widget.stories.tsx.snap @@ -2,9 +2,9 @@ exports[`Applying 1`] = `"

nixmac

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Nix is not installed on this system. Click below to install it using the standard macOS installer.

Evolving...
8 events
Waiting for next event...

Applying changes

Updating your configuration and preparing the review step.

"`; -exports[`Commit Screen 1`] = `"

nixmac

Failed to connect to nix daemon. Is the Nix daemon running?

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Failed to connect to nix daemon. Is the Nix daemon running?

Determinate Systems Installer
Evolving...
8 events
Waiting for next event...
Settings

General

Select your own, or proceed below for defaults

The darwin configuration to use for this machine

Send diagnostics to the nixmac team
Share anonymized crash and error reports to improve stability. Restart required.
Version
"`; +exports[`Commit Screen 1`] = `"

nixmac

Failed to connect to nix daemon. Is the Nix daemon running?

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Failed to connect to nix daemon. Is the Nix daemon running?

Determinate Systems Installer
Evolving...
8 events
Waiting for next event...
Settings

General

Select your own, or proceed below for defaults

The darwin configuration to use for this machine

Send diagnostics to the nixmac team
Share anonymized crash and error reports to improve stability. Restart required.
Version
"`; -exports[`Commit Screen With Message 1`] = `"

nixmac

Failed to connect to nix daemon. Is the Nix daemon running?

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Failed to connect to nix daemon. Is the Nix daemon running?

Determinate Systems Installer
Evolving...
8 events
Waiting for next event...
Settings

General

Select your own, or proceed below for defaults

The darwin configuration to use for this machine

Send diagnostics to the nixmac team
Share anonymized crash and error reports to improve stability. Restart required.
Version
"`; +exports[`Commit Screen With Message 1`] = `"

nixmac

Failed to connect to nix daemon. Is the Nix daemon running?

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Failed to connect to nix daemon. Is the Nix daemon running?

Determinate Systems Installer
Evolving...
8 events
Waiting for next event...
Settings

General

Select your own, or proceed below for defaults

The darwin configuration to use for this machine

Send diagnostics to the nixmac team
Share anonymized crash and error reports to improve stability. Restart required.
Version
"`; exports[`Committing 1`] = `"

nixmac

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Nix is not installed on this system. Click below to install it using the standard macOS installer.

Evolving...
8 events
Waiting for next event...
"`; @@ -12,9 +12,9 @@ exports[`Console With Output 1`] = `"

nixmac

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Nix is not installed on this system. Click below to install it using the standard macOS installer.

Evolving...
8 events
Waiting for next event...
"`; -exports[`Evolving Ready To Commit 1`] = `"

nixmac

Failed to connect to nix daemon. Is the Nix daemon running?

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Failed to connect to nix daemon. Is the Nix daemon running?

Determinate Systems Installer
Evolving...
8 events
Waiting for next event...
Settings

General

Select your own, or proceed below for defaults

The darwin configuration to use for this machine

Send diagnostics to the nixmac team
Share anonymized crash and error reports to improve stability. Restart required.
Version
"`; +exports[`Evolving Ready To Commit 1`] = `"

nixmac

Failed to connect to nix daemon. Is the Nix daemon running?

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Failed to connect to nix daemon. Is the Nix daemon running?

Determinate Systems Installer
Evolving...
8 events
Waiting for next event...
Settings

General

Select your own, or proceed below for defaults

The darwin configuration to use for this machine

Send diagnostics to the nixmac team
Share anonymized crash and error reports to improve stability. Restart required.
Version
"`; -exports[`Evolving With Unstaged Changes 1`] = `"

nixmac

Failed to connect to nix daemon. Is the Nix daemon running?

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Failed to connect to nix daemon. Is the Nix daemon running?

Determinate Systems Installer
Evolving...
8 events
Waiting for next event...
Settings

General

Select your own, or proceed below for defaults

The darwin configuration to use for this machine

Send diagnostics to the nixmac team
Share anonymized crash and error reports to improve stability. Restart required.
Version
"`; +exports[`Evolving With Unstaged Changes 1`] = `"

nixmac

Failed to connect to nix daemon. Is the Nix daemon running?

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Failed to connect to nix daemon. Is the Nix daemon running?

Determinate Systems Installer
Evolving...
8 events
Waiting for next event...
Settings

General

Select your own, or proceed below for defaults

The darwin configuration to use for this machine

Send diagnostics to the nixmac team
Share anonymized crash and error reports to improve stability. Restart required.
Version
"`; exports[`Generating 1`] = `"

nixmac

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Nix is not installed on this system. Click below to install it using the standard macOS installer.

Evolving...
6 events
Waiting for next event...
"`; @@ -28,14 +28,12 @@ exports[`Many Changed Files 1`] = `"

nixmac

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Nix is not installed on this system. Click below to install it using the standard macOS installer.

"`; -exports[`Onboarding Permissions Step 1`] = `"
Console icon

System Permissions

To manage your macOS system declaratively, nixmac needs the following permissions

Desktop Folder Access

Required○ Pending

Required to manage and sync desktop files and configurations

Documents Folder Access

Required○ Pending

Required to access and manage configuration files stored in Documents

Administrator Privileges

Required○ Pending

Required to install system packages and modify system configurations

You will be prompted for your password when needed

Full Disk Access

Required○ Pending

Required for darwin-rebuild to apply system changes

First make sure nixmac is in your Applications folder (not running from the install disk image). Then go to System Settings → Privacy & Security → Full Disk Access and add nixmac to the list.

Grant all required permissions to continue

Information

Why does nixmac need these permissions?

nixmac manages your macOS system declaratively, similar to NixOS. It needs access to configuration files, the ability to install packages, and permission to modify system settings to provide a complete system management experience.

"`; - exports[`Onboarding With Directory 1`] = `"

nixmac

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Nix is not installed on this system. Click below to install it using the standard macOS installer.

"`; -exports[`Onboarding With Permissions 1`] = `"

nixmac

Failed to connect to nix daemon. Is the Nix daemon running?

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Failed to connect to nix daemon. Is the Nix daemon running?

Determinate Systems Installer
Evolving...
8 events
Waiting for next event...
Settings

General

Select your own, or proceed below for defaults

The darwin configuration to use for this machine

Send diagnostics to the nixmac team
Share anonymized crash and error reports to improve stability. Restart required.
Version
"`; +exports[`Onboarding With Permissions 1`] = `"

nixmac

Failed to connect to nix daemon. Is the Nix daemon running?

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Failed to connect to nix daemon. Is the Nix daemon running?

Determinate Systems Installer
Evolving...
8 events
Waiting for next event...
Settings

General

Select your own, or proceed below for defaults

The darwin configuration to use for this machine

Send diagnostics to the nixmac team
Share anonymized crash and error reports to improve stability. Restart required.
Version
"`; exports[`Preview 1`] = `"

nixmac

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Nix is not installed on this system. Click below to install it using the standard macOS installer.

Evolving...
8 events
Waiting for next event...

Applying changes

Updating your configuration and preparing the review step.

"`; -exports[`Settings Open 1`] = `"

nixmac

Failed to connect to nix daemon. Is the Nix daemon running?

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Failed to connect to nix daemon. Is the Nix daemon running?

Determinate Systems Installer
Evolving...
8 events
Waiting for next event...
Settings

General

Select your own, or proceed below for defaults

The darwin configuration to use for this machine

Send diagnostics to the nixmac team
Share anonymized crash and error reports to improve stability. Restart required.
Version
"`; +exports[`Settings Open 1`] = `"

nixmac

Failed to connect to nix daemon. Is the Nix daemon running?

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Failed to connect to nix daemon. Is the Nix daemon running?

Determinate Systems Installer
Evolving...
8 events
Waiting for next event...
Settings

General

Select your own, or proceed below for defaults

The darwin configuration to use for this machine

Send diagnostics to the nixmac team
Share anonymized crash and error reports to improve stability. Restart required.
Version
"`; exports[`With Error 1`] = `"

nixmac

Failed to connect to nix daemon. Is the Nix daemon running?

System Setup

nixmac needs to install a few things to get started. While you wait, feel free to set up your AI provider and preferences using the gear icon above.

Failed to connect to nix daemon. Is the Nix daemon running?

Determinate Systems Installer
Evolving...
8 events
Waiting for next event...
"`; From d4dabaa24a737c3216973f82b2ff93a132006886 Mon Sep 17 00:00:00 2001 From: CasLinden Date: Thu, 7 May 2026 21:33:59 +0900 Subject: [PATCH 11/13] chore(frotnend): fix compile errors --- .../components/widget/controls/directory-picker.test.tsx | 7 +++---- apps/native/src/tauri-api.ts | 5 +---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/apps/native/src/components/widget/controls/directory-picker.test.tsx b/apps/native/src/components/widget/controls/directory-picker.test.tsx index fb4296459..0700606b3 100644 --- a/apps/native/src/components/widget/controls/directory-picker.test.tsx +++ b/apps/native/src/components/widget/controls/directory-picker.test.tsx @@ -2,6 +2,8 @@ import { act, fireEvent, render, screen, waitFor } from "@testing-library/react" import "@testing-library/jest-dom"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import type { SetDirResult } from "@/types/shared"; + import { useWidgetStore } from "@/stores/widget-store"; import { DirectoryPicker } from "@/components/widget/controls/directory-picker"; @@ -34,7 +36,7 @@ vi.mock("@/hooks/use-darwin-config", () => ({ const mockNormalize = vi.fn<(p: string) => Promise>(); const mockExists = vi.fn<(p: string) => Promise>(); -const mockSetDir = vi.fn<(p: string) => Promise>(); +const mockSetDir = vi.fn<(p: string) => Promise>(); const mockSetHostAttr = vi.fn<(h: string) => Promise>(); const mockListHosts = vi.fn<() => Promise>(); const mockFlakeExistsAt = vi.fn<(p: string) => Promise>(); @@ -51,9 +53,6 @@ vi.mock("@/tauri-api", () => ({ pickDir: () => mockPickDir(), setHostAttr: (h: string) => mockSetHostAttr(h), }, - flake: { - setHostAttr: (h: string) => mockSetHostAttr(h), - }, flake: { listHosts: () => mockListHosts(), existsAt: (p: string) => mockFlakeExistsAt(p), diff --git a/apps/native/src/tauri-api.ts b/apps/native/src/tauri-api.ts index 17ba536bb..3d82742f0 100644 --- a/apps/native/src/tauri-api.ts +++ b/apps/native/src/tauri-api.ts @@ -102,6 +102,7 @@ export const darwinAPI = { stage, clientTimestampUnixMs: clientTimestampUnixMs ?? null, }), + sentryEvent: () => invoke("debug_sentry_event"), }, ui: { getPrefs: () => invoke("ui_get_prefs"), @@ -178,10 +179,6 @@ export const darwinAPI = { relaunch: () => invoke("relaunch_after_update"), clearPinnedVersion: () => invoke("clear_pinned_version"), }, - - debug: { - sentryEvent: () => invoke("debug_sentry_event"), - }, }; export const ipcRenderer = { From 2dedc3b6f4db74a2b2bf7cf1c4d4effbbe743211 Mon Sep 17 00:00:00 2001 From: CasLinden Date: Fri, 8 May 2026 15:30:07 +0900 Subject: [PATCH 12/13] chore(constants): move constants from branding file --- apps/native/shared/constants.ts | 2 -- apps/native/src/components/widget/settings/ai-models-tab.tsx | 2 +- apps/native/src/components/widget/settings/settings-dialog.tsx | 2 +- apps/native/src/hooks/use-evolve.ts | 2 +- apps/native/src/lib/constants.ts | 2 ++ 5 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 apps/native/src/lib/constants.ts diff --git a/apps/native/shared/constants.ts b/apps/native/shared/constants.ts index 5581e9912..409dfaad8 100644 --- a/apps/native/shared/constants.ts +++ b/apps/native/shared/constants.ts @@ -1,7 +1,5 @@ // Shared app constants for easy renaming/rebranding export const APP_NAME = "nixmac"; -export const DEFAULT_MAX_ITERATIONS = 25; -export const EVOLVE_EVENT_CHANNEL = "darwin:evolve:event"; // If you change icons, update these to point to your bundled assets export const APP_TRAY_ICON = "image.png"; // under APP_ROOT / resources as used in main export const APP_OVERLAY_ICON = "/activation.png"; // served by Vite/public in dev diff --git a/apps/native/src/components/widget/settings/ai-models-tab.tsx b/apps/native/src/components/widget/settings/ai-models-tab.tsx index b2761affe..795e14e15 100644 --- a/apps/native/src/components/widget/settings/ai-models-tab.tsx +++ b/apps/native/src/components/widget/settings/ai-models-tab.tsx @@ -9,7 +9,7 @@ import { } from "@/components/ui/select"; import { ModelCombobox } from "@/components/widget/controls/model-combobox"; import { getProviderConfigInvalidReason, isCliProvider } from "@/lib/ai-provider-validation"; -import { DEFAULT_MAX_ITERATIONS } from "@/../shared/constants"; +import { DEFAULT_MAX_ITERATIONS } from "@/lib/constants"; import { darwinAPI } from "@/tauri-api"; import type { CliToolsState } from "@/types/shared"; import type { AnyFieldApi, ReactFormExtendedApi } from "@tanstack/react-form"; diff --git a/apps/native/src/components/widget/settings/settings-dialog.tsx b/apps/native/src/components/widget/settings/settings-dialog.tsx index c203817ef..41f80bdd1 100644 --- a/apps/native/src/components/widget/settings/settings-dialog.tsx +++ b/apps/native/src/components/widget/settings/settings-dialog.tsx @@ -2,7 +2,7 @@ import { Button } from "@/components/ui/button"; import { useDarwinConfig } from "@/hooks/use-darwin-config"; import { cn } from "@/lib/utils"; import { type SettingsTab, useWidgetStore } from "@/stores/widget-store"; -import { DEFAULT_MAX_ITERATIONS } from "@/../shared/constants"; +import { DEFAULT_MAX_ITERATIONS } from "@/lib/constants"; import { darwinAPI } from "@/tauri-api"; import { useForm } from "@tanstack/react-form"; import { Bot, FolderOpen, Key, Settings2, SlidersHorizontal, Wrench } from "lucide-react"; diff --git a/apps/native/src/hooks/use-evolve.ts b/apps/native/src/hooks/use-evolve.ts index a89fdd9d9..4d186d44b 100644 --- a/apps/native/src/hooks/use-evolve.ts +++ b/apps/native/src/hooks/use-evolve.ts @@ -1,5 +1,5 @@ import { useWidgetStore } from "@/stores/widget-store"; -import { EVOLVE_EVENT_CHANNEL } from "@/../shared/constants"; +import { EVOLVE_EVENT_CHANNEL } from "@/lib/constants"; import { darwinAPI, ipcRenderer } from "@/tauri-api"; import type { EvolveEvent } from "@/types/shared"; import { useCallback } from "react"; diff --git a/apps/native/src/lib/constants.ts b/apps/native/src/lib/constants.ts new file mode 100644 index 000000000..1b04c538c --- /dev/null +++ b/apps/native/src/lib/constants.ts @@ -0,0 +1,2 @@ +export const DEFAULT_MAX_ITERATIONS = 25; +export const EVOLVE_EVENT_CHANNEL = "darwin:evolve:event"; From 860791d27fc46e4c38943245cff76a2bb84578fc Mon Sep 17 00:00:00 2001 From: CasLinden Date: Fri, 8 May 2026 15:41:28 +0900 Subject: [PATCH 13/13] chore(storybook): fix export in tauri mock and update snapshots --- apps/native/.storybook/mocks/tauri-runtime.ts | 2 -- .../nix-editor/__snapshots__/nix-editor.stories.tsx.snap | 1 - .../overlays/__snapshots__/editor-panel.stories.tsx.snap | 5 ++--- .../settings/__snapshots__/ai-models-tab.stories.tsx.snap | 2 +- .../unsummarized-changes-section.stories.tsx.snap | 5 ++--- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/apps/native/.storybook/mocks/tauri-runtime.ts b/apps/native/.storybook/mocks/tauri-runtime.ts index a0cc1b960..ed72b52de 100644 --- a/apps/native/.storybook/mocks/tauri-runtime.ts +++ b/apps/native/.storybook/mocks/tauri-runtime.ts @@ -461,5 +461,3 @@ if (typeof window !== "undefined") { transformCallback, }; } - -export { invoke, storybookDarwinAPI, tauriEvent }; diff --git a/apps/native/src/components/kibo-ui/nix-editor/__snapshots__/nix-editor.stories.tsx.snap b/apps/native/src/components/kibo-ui/nix-editor/__snapshots__/nix-editor.stories.tsx.snap index 72d21ae82..6fc16a877 100644 --- a/apps/native/src/components/kibo-ui/nix-editor/__snapshots__/nix-editor.stories.tsx.snap +++ b/apps/native/src/components/kibo-ui/nix-editor/__snapshots__/nix-editor.stories.tsx.snap @@ -5,4 +5,3 @@ exports[`Configuration Nix 1`] = `"
"`; exports[`Unknown File 1`] = `"
"`; - diff --git a/apps/native/src/components/widget/overlays/__snapshots__/editor-panel.stories.tsx.snap b/apps/native/src/components/widget/overlays/__snapshots__/editor-panel.stories.tsx.snap index 2b03e97ef..11d1df83f 100644 --- a/apps/native/src/components/widget/overlays/__snapshots__/editor-panel.stories.tsx.snap +++ b/apps/native/src/components/widget/overlays/__snapshots__/editor-panel.stories.tsx.snap @@ -1,6 +1,5 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Editing Configuration 1`] = `"
Editingconfiguration.nix(configuration.nix)
"`; - -exports[`Editing Flake 1`] = `"
Editingflake.nix(flake.nix)
"`; +exports[`Editing Configuration 1`] = `"
Editingconfiguration.nix(configuration.nix)
Loading editor...
"`; +exports[`Editing Flake 1`] = `"
Editingflake.nix(flake.nix)
Loading editor...
"`; diff --git a/apps/native/src/components/widget/settings/__snapshots__/ai-models-tab.stories.tsx.snap b/apps/native/src/components/widget/settings/__snapshots__/ai-models-tab.stories.tsx.snap index 2c506f8ae..130f261af 100644 --- a/apps/native/src/components/widget/settings/__snapshots__/ai-models-tab.stories.tsx.snap +++ b/apps/native/src/components/widget/settings/__snapshots__/ai-models-tab.stories.tsx.snap @@ -1,3 +1,3 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Cli Providers 1`] = `"

AI Models

Evolution Model

Model used to plan and apply configuration changes in Nix

Summary Model

Model used to explain and summarize changes

Evolution Limits

Control how long the AI will try before giving up

"`; +exports[`Cli Providers 1`] = `"

AI Models

OpenRouter is the supported cloud provider in the main UI. Previously saved direct OpenAI keys still work as a legacy fallback, but they are no longer shown in Settings.

Evolution Model

Model used to plan and apply configuration changes in Nix

Summary Model

Model used to explain and summarize changes

Evolution Limits

Control how long the AI will try before giving up

"`; diff --git a/apps/native/src/components/widget/summaries/__snapshots__/unsummarized-changes-section.stories.tsx.snap b/apps/native/src/components/widget/summaries/__snapshots__/unsummarized-changes-section.stories.tsx.snap index 75ed93c5f..96bae1974 100644 --- a/apps/native/src/components/widget/summaries/__snapshots__/unsummarized-changes-section.stories.tsx.snap +++ b/apps/native/src/components/widget/summaries/__snapshots__/unsummarized-changes-section.stories.tsx.snap @@ -4,9 +4,8 @@ exports[`Also Unsummarized 1`] = `"
Manual Changes found innixpkgs:
modules/darwin/packages.nix
modules/darwin/fonts.nix
modules/home/shell.nix
modules/darwin/terminal.nix
"`; -exports[`Repeated File Hunks 1`] = `"
Manual Changes found indarwin:
flake.lockx18
hosts/common/home.nix
flake.nix
lib/mkHost.nix
files/config/zed/settings.json
"`; +exports[`Single 1`] = `"
Manual Changes found innixpkgs:
flake.nix
"`; exports[`With Rename 1`] = `"
Manual Changes found innixpkgs:
modules/darwin/packages.nix
modules/darwin/homebrew.nixmodules/darwin/brew.nix
home.nix
"`;