feat(stage-tamagotchi): better title bar with customizable theme

This commit is contained in:
Neko Ayaka
2025-05-27 22:32:21 +08:00
parent afae1741d3
commit 57e0edba74
8 changed files with 143 additions and 61 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
<html>
<head>
<meta charset="UTF-8" />
<title>アイリ</title>
<title>AIRI</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<!-- <meta
+15 -26
View File
@@ -1,38 +1,27 @@
use std::path::Path;
use tauri::{Manager, WebviewUrl, WebviewWindowBuilder};
use tauri::Manager;
use crate::windows;
#[tauri::command]
pub async fn open_settings_window(app: tauri::AppHandle) {
if let Some(window) = app.get_webview_window("settings") {
pub async fn open_settings_window(app: tauri::AppHandle) -> Result<(), tauri::Error> {
let window = app.get_webview_window("settings");
if let Some(window) = window {
let _ = window.show();
return;
return Ok(());
}
let _ = WebviewWindowBuilder::new(
&app,
"settings",
WebviewUrl::App(Path::new("#/settings").to_path_buf()),
)
.title("settings")
.inner_size(600.0, 800.0)
.build()
.unwrap();
windows::settings::new_settings_window(&app)?;
Ok(())
}
#[tauri::command]
pub async fn open_chat_window(app: tauri::AppHandle) {
if let Some(window) = app.get_webview_window("chat") {
pub async fn open_chat_window(app: tauri::AppHandle) -> Result<(), tauri::Error> {
let window = app.get_webview_window("chat");
if let Some(window) = window {
let _ = window.show();
return;
return Ok(());
}
let _ = WebviewWindowBuilder::new(
&app,
"chat",
WebviewUrl::App(Path::new("#/chat").to_path_buf()),
)
.title("chat")
.inner_size(600.0, 800.0)
.build()
.unwrap();
windows::chat::new_chat_window(&app)?;
Ok(())
}
+14 -17
View File
@@ -1,13 +1,15 @@
use std::path::Path;
use tauri::menu::{Menu, MenuItem};
use tauri::tray::TrayIconBuilder;
use tauri::Emitter;
use tauri::RunEvent;
#[cfg(target_os = "macos")]
use tauri::{ActivationPolicy, TitleBarStyle};
use tauri::{Emitter, RunEvent};
use tauri::{Manager, WebviewUrl, WebviewWindowBuilder};
use tauri_plugin_prevent_default::Flags;
mod commands;
mod windows;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
@@ -21,7 +23,7 @@ pub fn run() {
.plugin(tauri_plugin_os::init())
.setup(|app| {
let mut builder = WebviewWindowBuilder::new(app, "main", WebviewUrl::default())
.title("airi")
.title("AIRI")
.decorations(false)
.inner_size(450.0, 600.0)
.shadow(false)
@@ -64,28 +66,23 @@ pub fn run() {
app.exit(0);
}
"settings" => {
if let Some(window) = app.get_webview_window("settings") {
let window = app.get_webview_window("settings");
if let Some(window) = window {
let _ = window.show();
return;
}
let _ = WebviewWindowBuilder::new(
app,
"settings",
WebviewUrl::App(Path::new("#/settings").to_path_buf()),
)
.title("settings")
.inner_size(600.0, 800.0)
.build()
.unwrap();
windows::settings::new_settings_window(app).unwrap();
}
"hide" => {
if let Some(window) = app.get_webview_window("main") {
let window = app.get_webview_window("settings");
if let Some(window) = window {
let _ = window.hide();
}
}
"show" => {
if let Some(window) = app.get_webview_window("main") {
let window = app.get_webview_window("settings");
if let Some(window) = window {
let _ = window.show();
}
}
@@ -98,11 +95,11 @@ pub fn run() {
})
.invoke_handler(tauri::generate_handler![
commands::open_settings_window,
commands::open_chat_window
commands::open_chat_window,
])
.build(tauri::generate_context!())
.expect("error while building tauri application")
.run(|app_handle, event| match event {
.run(|_, event| match event {
RunEvent::ExitRequested { .. } => {
println!("Exiting app");
println!("Exited app");
@@ -0,0 +1,33 @@
use std::path::Path;
use tauri::{LogicalPosition, WebviewUrl, WebviewWindowBuilder};
#[cfg(target_os = "macos")]
use tauri::TitleBarStyle;
pub fn new_chat_window(app: &tauri::AppHandle) -> Result<(), tauri::Error> {
let mut builder = WebviewWindowBuilder::new(
app,
"chat",
WebviewUrl::App(Path::new("#/chat").to_path_buf()),
)
.title("Chat")
.inner_size(600.0, 800.0)
.shadow(true)
.hidden_title(true)
.transparent(false)
.accept_first_mouse(true);
#[cfg(target_os = "macos")]
{
// macOS traffic light (red, yellow, green) position customization
//
// feat: traffic light position (#12366) · tauri-apps/tauri@30f5a15
// https://github.com/tauri-apps/tauri/commit/30f5a1553d3c0ce460c9006764200a9210915a44
builder = builder.decorations(true);
builder = builder.title_bar_style(TitleBarStyle::Overlay);
builder = builder.traffic_light_position(LogicalPosition::new(14.0, 20.0));
}
builder.build()?;
return Ok(());
}
@@ -0,0 +1,2 @@
pub mod chat;
pub mod settings;
@@ -0,0 +1,33 @@
use std::path::Path;
use tauri::{LogicalPosition, WebviewUrl, WebviewWindowBuilder};
#[cfg(target_os = "macos")]
use tauri::TitleBarStyle;
pub fn new_settings_window(app: &tauri::AppHandle) -> Result<(), tauri::Error> {
let mut builder = WebviewWindowBuilder::new(
app,
"settings",
WebviewUrl::App(Path::new("#/settings").to_path_buf()),
)
.title("Settings")
.inner_size(450.0, 800.0)
.shadow(true)
.hidden_title(true)
.transparent(false)
.accept_first_mouse(true);
#[cfg(target_os = "macos")]
{
// macOS traffic light (red, yellow, green) position customization
//
// feat: traffic light position (#12366) · tauri-apps/tauri@30f5a15
// https://github.com/tauri-apps/tauri/commit/30f5a1553d3c0ce460c9006764200a9210915a44
builder = builder.decorations(true);
builder = builder.title_bar_style(TitleBarStyle::Overlay);
builder = builder.traffic_light_position(LogicalPosition::new(14.0, 20.0));
}
builder.build()?;
return Ok(());
}
+44 -16
View File
@@ -103,22 +103,50 @@ const routeHeaderMetadata = computed(() => routeHeaderMetadataMap.value[route.pa
</script>
<template>
<div
:style="{
paddingBottom: 'env(safe-area-inset-bottom, 0px)',
paddingTop: 'env(safe-area-inset-top, 0px)',
paddingRight: 'env(safe-area-inset-right, 0px)',
paddingLeft: 'env(safe-area-inset-left, 0px)',
}"
>
<!-- Content -->
<div class="px-3 py-2 md:px-5 md:py-5" flex="~ col" mx-auto max-w-screen-xl>
<PageHeader
:title="routeHeaderMetadata?.title"
:subtitle="routeHeaderMetadata?.subtitle"
:disable-back-button="route.path === '/settings'"
/>
<RouterView />
<div h-full w-full mt="44px" overflow-y-scroll>
<div
bg="neutral-100 dark:neutral-900" w="100dvw"
data-tauri-drag-region
fixed top="0" z-100 w-full py-2 pl-20 pr-4
>
<div data-tauri-drag-region flex>
<div
bg="hover:neutral-200 hover:dark:neutral-800"
transition="all duration-200 ease-in-out"
flex cursor-pointer items-center gap-2 rounded-md px-1.5 py-0.5
>
<div i-solar:settings-bold text="neutral-400 dark:neutral-500" text-nowrap />
<div><span text-nowrap>Settings</span></div>
</div>
<div data-tauri-drag-region w-full />
<div
bg="hover:neutral-200 hover:dark:neutral-800"
transition="all duration-200 ease-in-out"
flex cursor-pointer items-center gap-2 rounded-md px-1.5 py-0.5
>
<div i-solar:info-circle-bold text="neutral-400 dark:neutral-500" text-nowrap />
</div>
</div>
</div>
<div
:style="{
paddingBottom: 'env(safe-area-inset-bottom, 0px)',
paddingTop: 'env(safe-area-inset-top, 0px)',
paddingRight: 'env(safe-area-inset-right, 0px)',
paddingLeft: 'env(safe-area-inset-left, 0px)',
}"
>
<div h-full w-full px-4 pb-4>
<!-- Content -->
<div flex="~ col" mx-auto max-w-screen-xl>
<PageHeader
:title="routeHeaderMetadata?.title"
:subtitle="routeHeaderMetadata?.subtitle"
:disable-back-button="route.path === '/settings'"
/>
<RouterView />
</div>
</div>
</div>
</div>
</template>
@@ -147,7 +147,7 @@ function getModuleShortName(id: string, module: 'consciousness' | 'voice') {
</script>
<template>
<div rounded-xl p-4 flex="~ col gap-4">
<div rounded-xl py-4 flex="~ col gap-4">
<!-- Toolbar with search and filters -->
<div flex="~ row" flex-wrap items-center justify-between gap-4>
<!-- Search bar -->