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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/tauri-app/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod desktop_event_transport;
mod linux_tls;
mod managed_node;
mod shutdown;
mod windows_update;

use cli_manager::{CliProcessManager, CliStatus};
use desktop_event_transport::{
Expand Down Expand Up @@ -656,7 +657,8 @@ fn main() {
client_state::client_state_set_restore_enabled,
client_state::client_state_clear,
client_state::client_state_renderer_flushed,
client_state::client_state_navigation_flushed
client_state::client_state_navigation_flushed,
windows_update::install_stable_update
])
.on_menu_event(|app_handle, event| {
match event.id().0.as_str() {
Expand Down
38 changes: 38 additions & 0 deletions packages/tauri-app/src-tauri/src/windows_update.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use std::process::Command;

#[cfg(windows)]
use std::os::windows::process::CommandExt;

#[tauri::command]
pub async fn install_stable_update() -> Result<(), String> {
tauri::async_runtime::spawn_blocking(|| {
let mut command = Command::new("winget");
command.args([
"upgrade",
"--exact",
"--id",
"NeuralNomadsAI.CodeNomad",
"--source",
"winget",
"--silent",
"--accept-source-agreements",
"--accept-package-agreements",
"--disable-interactivity",
]);

#[cfg(windows)]
command.creation_flags(0x08000000);

let status = command
.status()
.map_err(|err| format!("Unable to start the WinGet update: {err}"))?;

if status.success() {
Ok(())
} else {
Err(format!("WinGet update failed with status {status}"))
}
})
.await
.map_err(|err| format!("Unable to monitor the WinGet update: {err}"))?
}
27 changes: 3 additions & 24 deletions packages/ui/src/components/toast-history-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import {
deleteToastHistoryItem,
markAllToastHistoryAsRead,
markToastHistoryAsRead,
runToastAction,
subscribeToastHistory,
} from "../lib/notifications"
import { isTauriHost } from "../lib/runtime-env"

// ==================== Types ====================

Expand Down Expand Up @@ -182,15 +182,9 @@ const ToastHistoryPanel: Component<ToastHistoryPanelProps> = (props) => {

// Handle item click
const handleItemClick = (item: IToastHistoryItem) => {
// Mark as read
if (!item.read) {
markToastHistoryAsRead(item.id);
}

// Open action link if exists
if (item.action?.href) {
void handleOpenAction(item.action.href);
}
};

// Handle delete
Expand All @@ -209,21 +203,6 @@ const ToastHistoryPanel: Component<ToastHistoryPanelProps> = (props) => {
markAllToastHistoryAsRead();
};

// Open external link
async function handleOpenAction(href: string): Promise<void> {
if (isTauriHost()) {
try {
const { openUrl } = await import("@tauri-apps/plugin-opener");
await openUrl(href);
return;
} catch (error) {
console.warn("[toast-history] unable to open via system opener", error);
}
}

window.open(href, "_blank", "noopener,noreferrer");
}

// Stop click propagation from backdrop to panel
const handleBackdropClick = (event: MouseEvent) => {
if (event.target === event.currentTarget) {
Expand Down Expand Up @@ -383,8 +362,8 @@ const ToastHistoryPanel: Component<ToastHistoryPanelProps> = (props) => {
class="toast-history-item-action inline-flex items-center gap-1 text-xs mt-1"
onClick={(e) => {
e.stopPropagation();
if (item.action?.href) {
void handleOpenAction(item.action.href);
if (item.action) {
void runToastAction(item.action);
}
}}
>
Expand Down
32 changes: 23 additions & 9 deletions packages/ui/src/lib/notifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,19 @@ export type ToastHandle = {

type ToastPosition = "top-left" | "top-right" | "top-center" | "bottom-left" | "bottom-right" | "bottom-center"

export type ToastAction = {
label: string
href: string
onClick?: () => void | Promise<void>
}

export type ToastPayload = {
title?: string
message: string
variant: ToastVariant
duration?: number
position?: ToastPosition
action?: {
label: string
href: string
}
action?: ToastAction
}

// ==================== Toast History Types ====================
Expand All @@ -42,10 +45,7 @@ export interface IToastHistoryItem {
/** Read state (clicked) */
read: boolean;
/** Action link (optional) */
action?: {
label: string;
href: string;
};
action?: ToastAction;
}

/**
Expand Down Expand Up @@ -297,6 +297,20 @@ async function openExternalUrl(url: string): Promise<void> {
}
}

export async function runToastAction(action: ToastAction): Promise<void> {
if (!action.onClick) {
await openExternalUrl(action.href)
return
}

try {
await action.onClick()
} catch (error) {
console.warn("[notifications] action failed; opening fallback link", error)
await openExternalUrl(action.href)
}
}

// ==================== Variant Accent Styles ====================

const variantAccent: Record<
Expand Down Expand Up @@ -381,7 +395,7 @@ export function showToastNotification(payload: ToastPayload): ToastHandle {
<button
type="button"
class="mt-3 inline-flex items-center text-xs font-semibold uppercase tracking-wide text-sky-300 hover:text-sky-200"
onClick={() => void openExternalUrl(payload.action!.href)}
onClick={() => void runToastAction(payload.action!)}
>
{payload.action.label}
</button>
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/src/stores/releases.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { createEffect, createSignal } from "solid-js"
import { invoke } from "@tauri-apps/api/core"
import type { ServerMeta, SupportMeta } from "../../../server/src/api-types"
import { getServerMeta } from "../lib/server-meta"
import { showToastNotification, ToastHandle } from "../lib/notifications"
import { getLogger } from "../lib/logger"
import { tGlobal } from "../lib/i18n"
import { isLocalWindow, isTauriHost } from "../lib/runtime-env"
import { hasInstances, showFolderSelection } from "./ui"

const log = getLogger("actions")
Expand Down Expand Up @@ -61,6 +63,7 @@ function ensureVisibilityEffect() {
? {
label: tGlobal("releases.upgradeRequired.action.getUpdate"),
href: support.latestServerUrl,
onClick: isTauriHost() && isLocalWindow() && navigator.userAgent.includes("Windows") ? () => invoke("install_stable_update") : undefined,
}
: undefined,
})
Expand Down
Loading