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
28 changes: 28 additions & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,39 @@ use tauri::Manager;
use tauri_plugin_deep_link::DeepLinkExt;
use tauri_plugin_single_instance::init as single_instance;

fn raise_main_window_for_auth(handle: &tauri::AppHandle) {
let Some(win) = handle.get_webview_window("main") else {
warn!("main window not found, cannot raise for auth callback");
return;
};

let _ = win.unminimize();
let _ = win.show();
let _ = win.set_focus();

let was_always_on_top = win.is_always_on_top().unwrap_or(false);
if let Err(e) = win.set_always_on_top(true) {
warn!("Failed to set main window always on top: {}", e);
return;
}

if !was_always_on_top {
let app_handle = handle.clone();
tauri::async_runtime::spawn(async move {
tokio::time::sleep(std::time::Duration::from_millis(1500)).await;
if let Some(win) = app_handle.get_webview_window("main") {
let _ = win.set_always_on_top(false);
}
});
}
}

fn process_deep_link(handle: &tauri::AppHandle, raw: &str) -> bool {
info!("deep link received: {}", raw);

if is_auth_callback(raw) {
info!("deep link is auth callback, handling in current instance");
raise_main_window_for_auth(handle);
let flow_state = handle.state::<AuthFlowState>();
handle_auth_callback(&flow_state, raw);
return false;
Expand Down
5 changes: 4 additions & 1 deletion ui/components/Header/ActionButtons.vue
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ const actionItems = computed<ActionItem[]>(() => [

// 直接创建窗口
// eslint-disable-next-line no-new
const isMac = isMacOS.value;
new useTauriWebviewWindowWebviewWindow(label, {
title: t("Common.ConnectionSettings"),
url: localePath({ path: "/setting" }),
Expand All @@ -204,7 +205,9 @@ const actionItems = computed<ActionItem[]>(() => [
maxHeight: 675,
hiddenTitle: true,
titleBarStyle: "overlay",
trafficLightPosition: new LogicalPosition(10, 22)
trafficLightPosition: new LogicalPosition(10, 22),
decorations: isMac,
shadow: isMac
});
}
}
Expand Down
67 changes: 36 additions & 31 deletions ui/composables/useAssetAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ export const useAssetAction = () => {
const settingManager = useSettingManager();
// prettier-ignore
const { currentSite, currentUser, currentConnectionInfoMap, currentRdpClientOption, orgId } = storeToRefs(userInfoStore);
const { charset, rdpResolution, backspaceAsCtrlH, keyboardLayout, rdpClientOption, rdpColorQuality, rdpSmartSize }
= settingManager;
const { charset, rdpResolution, backspaceAsCtrlH, keyboardLayout, rdpClientOption, rdpColorQuality, rdpSmartSize } =
settingManager;

function buildLocalRdpParams() {
const prefs = resolveGraphicsPreferences();
Expand Down Expand Up @@ -92,10 +92,10 @@ export const useAssetAction = () => {
* @description 生成连接选项
*/
function resolveGraphicsPreferences() {
const resolvedKeyboardLayout
= keyboardLayout.value || currentRdpClientOption.value.keyboard_layout || "en-us-qwerty";
const resolvedClientOptions
= Array.isArray(rdpClientOption.value) && rdpClientOption.value.length > 0
const resolvedKeyboardLayout =
keyboardLayout.value || currentRdpClientOption.value.keyboard_layout || "en-us-qwerty";
const resolvedClientOptions =
Array.isArray(rdpClientOption.value) && rdpClientOption.value.length > 0
? [...rdpClientOption.value]
: [...(currentRdpClientOption.value.rdp_client_option || [])];
const resolvedColorQuality = rdpColorQuality.value || currentRdpClientOption.value.rdp_color_quality || "32";
Expand Down Expand Up @@ -152,8 +152,8 @@ export const useAssetAction = () => {
// prettier-ignore
const isManual = saved?.accountMode === "manual" || username === "手动输入" || username === "Manual input";

const isDynamic
= saved?.accountMode === "dynamic" || username.includes("同名账号") || username.includes("Dynamic user");
const isDynamic =
saved?.accountMode === "dynamic" || username.includes("同名账号") || username.includes("Dynamic user");

// 已保存过托管账号的 ID 则优先使用
if (!isManual && !isDynamic && saved?.accountId) {
Expand Down Expand Up @@ -249,10 +249,10 @@ export const useAssetAction = () => {
accounts?: PermedAccount[],
protocolOverride?: string,
ephemeral?: {
accountMode?: "hosted" | "dynamic" | "manual"
manualUsername?: string
manualPassword?: string
dynamicPassword?: string
accountMode?: "hosted" | "dynamic" | "manual";
manualUsername?: string;
manualPassword?: string;
dynamicPassword?: string;
}
) => {
const saved = currentConnectionInfoMap.value[assetId];
Expand Down Expand Up @@ -377,8 +377,8 @@ export const useAssetAction = () => {
try {
unlistenGetTokenSuccess = await useTauriEventListen("get-token-success", (event) => {
interface eventPayload {
status: number
data: TokenResponse
status: number;
data: TokenResponse;
}

const payload = event.payload as eventPayload;
Expand All @@ -390,8 +390,8 @@ export const useAssetAction = () => {

unlistenGetTokenFailure = await useTauriEventListen("get-token-failure", (event) => {
interface eventPayload {
status: number
data: string
status: number;
data: string;
}

const payload = event.payload as eventPayload;
Expand Down Expand Up @@ -421,7 +421,7 @@ export const useAssetAction = () => {

unlistenFavoriteSuccess = await useTauriEventListen("set-favorite-success", (event) => {
interface eventPayload {
status: string
status: string;
}

const payload = event.payload as eventPayload;
Expand All @@ -438,7 +438,7 @@ export const useAssetAction = () => {

unlistenFavoriteFailed = await useTauriEventListen("set-favorite-failure", (event) => {
interface eventPayload {
status: string
status: string;
}

const payload = event.payload as eventPayload;
Expand All @@ -455,7 +455,7 @@ export const useAssetAction = () => {

unlistenUnfavoriteSuccess = await useTauriEventListen("unfavorite-success", (event) => {
interface eventPayload {
status: string
status: string;
}

const payload = event.payload as eventPayload;
Expand All @@ -473,7 +473,7 @@ export const useAssetAction = () => {

unlistenUnfavoriteFailed = await useTauriEventListen("unfavorite-failure", (event) => {
interface eventPayload {
status: string
status: string;
}

const payload = event.payload as eventPayload;
Expand All @@ -490,9 +490,9 @@ export const useAssetAction = () => {

unlistenGetAssetDetailSuccess = await useTauriEventListen("get-asset-detail-success", (event) => {
interface eventPayload {
status: string
data: string
asset_id: string
status: string;
data: string;
asset_id: string;
}

const payload = event.payload as eventPayload;
Expand All @@ -502,10 +502,15 @@ export const useAssetAction = () => {
const permedAccounts = assetDetail.permed_accounts ?? [];
const permedProtocols = assetDetail.permed_protocols ?? [];

// 不支持目录服务的 winrm 协议
const filteredPermedProtocols = permedProtocols.filter(
(protocol: PermedProtocol) => protocol.name !== "winrm"
);

useEventBus().emit("assetDetailUpdated", {
assetId: payload.asset_id,
permedAccounts,
permedProtocols
permedProtocols: filteredPermedProtocols
});
}
});
Expand All @@ -519,9 +524,9 @@ export const useAssetAction = () => {

unlistenRenameSuccess = await useTauriEventListen("rename-success", (event) => {
interface eventPayload {
success: boolean
status?: number
data?: string
success: boolean;
status?: number;
data?: string;
}

const payload = event.payload as eventPayload;
Expand All @@ -546,9 +551,9 @@ export const useAssetAction = () => {

unlistenRenameError = await useTauriEventListen("rename-error", (event) => {
interface eventPayload {
success: boolean
status?: number
data?: string
success: boolean;
status?: number;
data?: string;
}

const payload = event.payload as eventPayload;
Expand All @@ -570,7 +575,7 @@ export const useAssetAction = () => {

unlistenPullUpFailure = await useTauriEventListen("pull-up-failure", (event) => {
interface eventPayload {
error: string
error: string;
}

const payload = event.payload as eventPayload;
Expand Down
79 changes: 74 additions & 5 deletions ui/layouts/setting.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,60 @@ import type { NavigationMenuItem } from "@nuxt/ui";
const localePath = useLocalePath();

const { t } = useI18n();
const { isMacOS } = usePlatform();
const { theme } = useSettingManager();
const { initialTheme, listenOSThemeChange } = useThemeAdapter();

const commonButtonProps = {
size: "sm" as const,
variant: "ghost" as const,
color: "neutral" as const
};

const windowControlButtons = computed(() => {
return [
{
key: "minimize",
iconName: "i-lucide-minus",
tooltipLabel: t("ToolTips.Minimize"),
onClick: async () => {
await useTauriCoreInvoke("minimize_window");
}
},
{
key: "maximize",
iconName: "i-lucide-square",
tooltipLabel: t("ToolTips.Maximize"),
onClick: async () => {
await useTauriCoreInvoke("toggle_maximize_window");
}
},
{
key: "close",
iconName: "i-lucide-x",
tooltipLabel: t("ToolTips.Close"),
onClick: async () => {
await useTauriCoreInvoke("close_window");
}
}
];
});

const getWindowControlButtonClass = (buttonKey: string) => {
const baseClass = "rounded-none w-12 h-13 p-1 flex items-center justify-center";

switch (buttonKey) {
case "minimize":
return `${baseClass} `;
case "maximize":
return `${baseClass} `;
case "close":
return `${baseClass} hover:bg-red-500 hover:text-white active:bg-red-600`;
default:
return baseClass;
}
};

const settingMenu = computed<NavigationMenuItem[]>(() => {
return [
{
Expand Down Expand Up @@ -50,14 +101,32 @@ onMounted(() => {
>
<UPageHeader
:ui="{
root: 'py-2.5'
root: 'p-0'
}"
>
<template #default>
<div data-tauri-drag-region class="flex items-center justify-center select-none cursor-default">
<p class="text-sm font-bold pointer-events-none">
{{ t("Common.ConnectionSettings") }}
</p>
<div class="header-bg h-13 grid grid-cols-[1fr_auto_1fr] items-center px-4">
<div data-tauri-drag-region class="h-full" />

<div data-tauri-drag-region class="flex items-center justify-center select-none cursor-default">
<p class="text-sm font-bold pointer-events-none">
{{ t("Common.ConnectionSettings") }}
</p>
</div>

<div class="flex items-center justify-end" data-tauri-drag-region="false">
<template v-if="!isMacOS">
<template v-for="button of windowControlButtons" :key="button.key">
<UButton
:icon="button.iconName"
:class="getWindowControlButtonClass(button.key)"
:title="button.tooltipLabel"
v-bind="commonButtonProps"
@click="button.onClick"
/>
</template>
</template>
</div>
</div>
</template>
</UPageHeader>
Expand Down