feat(stage-tamagotchi): WindowLink for opening window in Tauri
This commit is contained in:
Generated
+1
@@ -206,6 +206,7 @@ dependencies = [
|
||||
"tauri-plugin-prevent-default",
|
||||
"tokenizers",
|
||||
"tokio",
|
||||
"url",
|
||||
"windows 0.61.3",
|
||||
]
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ rubato = "0.16.2"
|
||||
byteorder = "1.5.0"
|
||||
clap = { version = "4.5.40", features = ["derive"] }
|
||||
tokenizers = "0.21.2"
|
||||
url = "2.5.4"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
objc2 = "0.6.1"
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::path::Path;
|
||||
use tauri::TitleBarStyle;
|
||||
use tauri::{WebviewUrl, WebviewWindowBuilder};
|
||||
|
||||
pub fn new_chat_window(app: &tauri::AppHandle) -> Result<(), tauri::Error> {
|
||||
pub fn new_chat_window(app: &tauri::AppHandle) -> Result<tauri::WebviewWindow, tauri::Error> {
|
||||
let mut builder = WebviewWindowBuilder::new(
|
||||
app,
|
||||
"chat",
|
||||
@@ -28,6 +28,5 @@ pub fn new_chat_window(app: &tauri::AppHandle) -> Result<(), tauri::Error> {
|
||||
builder = builder.traffic_light_position(tauri::LogicalPosition::new(14.0, 20.0));
|
||||
}
|
||||
|
||||
builder.build()?;
|
||||
Ok(())
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::path::Path;
|
||||
use tauri::TitleBarStyle;
|
||||
use tauri::{WebviewUrl, WebviewWindowBuilder};
|
||||
|
||||
pub fn new_settings_window(app: &tauri::AppHandle) -> Result<(), tauri::Error> {
|
||||
pub fn new_settings_window(app: &tauri::AppHandle) -> Result<tauri::WebviewWindow, tauri::Error> {
|
||||
let mut builder = WebviewWindowBuilder::new(
|
||||
app,
|
||||
"settings",
|
||||
@@ -28,6 +28,5 @@ pub fn new_settings_window(app: &tauri::AppHandle) -> Result<(), tauri::Error> {
|
||||
builder = builder.traffic_light_position(tauri::LogicalPosition::new(14.0, 20.0));
|
||||
}
|
||||
|
||||
builder.build()?;
|
||||
Ok(())
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{sync::atomic::Ordering, time::Duration};
|
||||
use std::{str::FromStr, sync::atomic::Ordering, time::Duration};
|
||||
|
||||
use log::info;
|
||||
use tauri::{
|
||||
@@ -126,6 +126,42 @@ async fn load_models(window: tauri::Window) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn open_route_in_window(
|
||||
window: tauri::Window,
|
||||
route: String,
|
||||
window_label: String,
|
||||
) -> Result<(), String> {
|
||||
let app = window.app_handle();
|
||||
|
||||
let target_window = match window_label.as_str() {
|
||||
"chat" => match app.get_webview_window("chat") {
|
||||
Some(window) => window,
|
||||
None => app_windows::chat::new_chat_window(app)
|
||||
.map_err(|e| format!("Failed to create chat window: {}", e))?,
|
||||
},
|
||||
"settings" => match app.get_webview_window("settings") {
|
||||
Some(window) => window,
|
||||
None => app_windows::settings::new_settings_window(app)
|
||||
.map_err(|e| format!("Failed to create settings window: {}", e))?,
|
||||
},
|
||||
_ => {
|
||||
return Err(format!("Unknown window label: {}", window_label));
|
||||
},
|
||||
};
|
||||
|
||||
let mut current_url = target_window
|
||||
.url()
|
||||
.map_err(|e| format!("Failed to get current URL: {}", e))?;
|
||||
let route: String = "/".to_string() + route.trim_start_matches('/');
|
||||
current_url.set_fragment(Some(route.to_string().as_str()));
|
||||
|
||||
let _ = target_window.show();
|
||||
let _ = target_window.navigate(current_url);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
pub fn run() {
|
||||
@@ -238,6 +274,7 @@ pub fn run() {
|
||||
start_click_through,
|
||||
stop_click_through,
|
||||
load_models,
|
||||
open_route_in_window,
|
||||
])
|
||||
.build(tauri::generate_context!())
|
||||
.expect("error while building tauri application")
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
import { useTauriCore } from '../../composables/tauri'
|
||||
|
||||
const props = defineProps<{
|
||||
to: string
|
||||
label?: string
|
||||
}>()
|
||||
|
||||
const { invoke } = useTauriCore()
|
||||
|
||||
function handleClick() {
|
||||
invoke('open_route_in_window', {
|
||||
route: props.to,
|
||||
windowLabel: props.label,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a cursor-pointer @click="handleClick">
|
||||
<slot />
|
||||
</a>
|
||||
</template>
|
||||
+4
-2
@@ -3,6 +3,8 @@ import type { ProgressInfo } from '../../../stores/resources'
|
||||
|
||||
import { computed } from 'vue'
|
||||
|
||||
import WindowLink from '../../Tauri/WindowLink.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
progressInfo: ProgressInfo
|
||||
}>()
|
||||
@@ -25,7 +27,7 @@ const totalProgress = computed(() => {
|
||||
</div>
|
||||
<ul ml-4 mt-3>
|
||||
<li>
|
||||
<RouterLink to="/settings/modules/hearing">
|
||||
<WindowLink to="/settings/modules/hearing" label="settings">
|
||||
<div flex items-center gap-1>
|
||||
<div flex items-center gap-1>
|
||||
<div i-solar:microphone-3-bold-duotone />
|
||||
@@ -35,7 +37,7 @@ const totalProgress = computed(() => {
|
||||
due to loading inference models...
|
||||
</div>
|
||||
</div>
|
||||
</RouterLink>
|
||||
</WindowLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { computedAsync } from '@vueuse/core'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
async function getTauri() {
|
||||
try {
|
||||
@@ -11,27 +11,25 @@ async function getTauri() {
|
||||
}
|
||||
}
|
||||
|
||||
async function getTauriOSPluginInternal() {
|
||||
const os = await import('@tauri-apps/plugin-os')
|
||||
return os
|
||||
}
|
||||
|
||||
export function useAppRuntime() {
|
||||
const isTauri = ref<boolean>(false)
|
||||
const isInitialized = ref(false)
|
||||
|
||||
const platform = computedAsync(async () => {
|
||||
if (!isTauri.value) {
|
||||
return 'web'
|
||||
const res = (await getTauri())?.platform?.() || 'web'
|
||||
if (!isInitialized.value) {
|
||||
isInitialized.value = true
|
||||
}
|
||||
|
||||
return (await getTauriOSPluginInternal())?.platform?.() || 'web'
|
||||
})
|
||||
return res
|
||||
}, 'web')
|
||||
|
||||
onMounted(async () => {
|
||||
isTauri.value = (await getTauri()) != null
|
||||
const isTauri = computed(() => {
|
||||
return platform.value !== 'web'
|
||||
})
|
||||
|
||||
return {
|
||||
platform,
|
||||
isInitialized,
|
||||
isTauri,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { computedAsync, until } from '@vueuse/core'
|
||||
import { useAppRuntime } from './runtime'
|
||||
|
||||
async function untilNoError<T>(fn: () => Promise<T>, onError?: (err?: unknown | null) => void): Promise<T> {
|
||||
const fnRetry = withRetry(fn, { retryDelay: 5000, retry: Number.MAX_SAFE_INTEGER - 2, onError })
|
||||
const fnRetry = withRetry(fn, { retryDelay: 5000, retry: 5, onError })
|
||||
return await fnRetry()
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export interface AiriTamagotchiEvents extends Events {
|
||||
}
|
||||
|
||||
export function useTauriEvent<ES = Events>() {
|
||||
const { platform } = useAppRuntime()
|
||||
const { platform, isInitialized } = useAppRuntime()
|
||||
|
||||
const tauriEventApi = computedAsync(() => {
|
||||
if (platform.value !== 'web') {
|
||||
@@ -63,6 +63,8 @@ export function useTauriEvent<ES = Events>() {
|
||||
})
|
||||
|
||||
async function _listen<E extends keyof ES>(event: E, callback: EventCallback<ES[E]>) {
|
||||
await until(isInitialized).toBeTruthy()
|
||||
|
||||
if (platform.value === 'web') {
|
||||
return () => {}
|
||||
}
|
||||
@@ -106,6 +108,13 @@ export interface InvokeMethods {
|
||||
load_models: { args: undefined, options: undefined, returns: void }
|
||||
stop_click_through: { args: undefined, options: undefined, returns: void }
|
||||
start_click_through: { args: undefined, options: undefined, returns: void }
|
||||
|
||||
// WindowLink.vue
|
||||
open_route_in_window: {
|
||||
args: { route: string, windowLabel?: string } | undefined
|
||||
options: undefined
|
||||
returns: void
|
||||
}
|
||||
}
|
||||
|
||||
interface InvokeMethodShape {
|
||||
@@ -115,7 +124,7 @@ interface InvokeMethodShape {
|
||||
}
|
||||
|
||||
export function useTauriCore<IM extends Record<keyof IM, InvokeMethodShape> = InvokeMethods>() {
|
||||
const { platform } = useAppRuntime()
|
||||
const { platform, isInitialized } = useAppRuntime()
|
||||
|
||||
const tauriCoreApi = computedAsync(() => {
|
||||
if (platform.value !== 'web') {
|
||||
@@ -123,15 +132,15 @@ export function useTauriCore<IM extends Record<keyof IM, InvokeMethodShape> = In
|
||||
}
|
||||
})
|
||||
|
||||
async function invoke<
|
||||
C extends keyof IM,
|
||||
>(
|
||||
async function invoke<C extends keyof IM>(
|
||||
command: C,
|
||||
args?: IM[C]['args'],
|
||||
options?: IM[C]['options'],
|
||||
): Promise<IM[C]['returns'] | undefined> {
|
||||
await until(isInitialized).toBeTruthy()
|
||||
|
||||
if (platform.value === 'web') {
|
||||
console.warn(`Attempted to invoke Tauri command "${String(command)}" in web platform, however, currently we are not in a Tauri environment.`)
|
||||
console.warn(`Attempted to invoke Tauri command "${String(command)}" in web platform`)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,13 @@ import type { AiriTamagotchiEvents, Point, WindowFrame } from '../composables/ta
|
||||
import { WidgetStage } from '@proj-airi/stage-ui/components'
|
||||
import { useMcpStore } from '@proj-airi/stage-ui/stores'
|
||||
import { connectServer } from '@proj-airi/tauri-plugin-mcp'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
import ResourceStatusIsland from '../components/Widgets/ResourceStatusIsland/index.vue'
|
||||
import { useAppRuntime } from '../composables/runtime'
|
||||
import { useTauriCore, useTauriEvent } from '../composables/tauri'
|
||||
import { useTauriEvent } from '../composables/tauri'
|
||||
import { useWindowShortcuts } from '../composables/window-shortcuts'
|
||||
import { useResourcesStore } from '../stores/resources'
|
||||
import { useWindowControlStore } from '../stores/window-controls'
|
||||
@@ -21,7 +22,6 @@ const { platform } = useAppRuntime()
|
||||
const windowStore = useWindowControlStore()
|
||||
const mcpStore = useMcpStore()
|
||||
const { listen } = useTauriEvent<AiriTamagotchiEvents>()
|
||||
const { invoke } = useTauriCore()
|
||||
|
||||
const isCursorInside = ref(false)
|
||||
const { connected, serverCmd, serverArgs } = storeToRefs(mcpStore)
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { useTauriCore } from '../composables/tauri'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
export async function startClickThrough() {
|
||||
const { invoke } = useTauriCore()
|
||||
await invoke('start_click_through')
|
||||
}
|
||||
|
||||
export async function stopClickThrough() {
|
||||
const { invoke } = useTauriCore()
|
||||
await invoke('stop_click_through')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user