Skip to content

Commit

Permalink
chore(tamagotchi): move settings a single window (#14)
Browse files Browse the repository at this point in the history
* move settings to a new window
* use custom menu
* fix routing
  • Loading branch information
LemonNekoGH authored Jan 15, 2025
1 parent 790189f commit ca02572
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 98 deletions.
29 changes: 3 additions & 26 deletions packages/stage-tamagotchi/electron.vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { join, resolve } from 'node:path'
import { Download } from '@proj-airi/unplugin-download'
import { DownloadLive2DSDK } from '@proj-airi/unplugin-live2d-sdk'
import vue from '@vitejs/plugin-vue'
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import unocss from 'unocss/vite'

import rendererConfig from './renderer.vite.config'

export default defineConfig({
main: {
Expand All @@ -12,25 +9,5 @@ export default defineConfig({
preload: {
plugins: [externalizeDepsPlugin()],
},
renderer: {
optimizeDeps: {
exclude: [
'@proj-airi/stage-ui/*',
],
},
resolve: {
alias: {
'@renderer': resolve(join('src', 'renderer', 'src')),
'@proj-airi/stage-ui': resolve(join(import.meta.dirname, '..', 'stage-ui', 'dist')),
'@proj-airi/stage-ui/stores': resolve(join(import.meta.dirname, '..', 'stage-ui', 'dist', 'stores')),
},
},
plugins: [
vue(),
unocss(),
DownloadLive2DSDK(),
Download('https://dist.ayaka.moe/live2d-models/hiyori_free_zh.zip', 'hiyori_free_zh.zip', 'assets/live2d/models'),
Download('https://dist.ayaka.moe/live2d-models/hiyori_pro_zh.zip', 'hiyori_pro_zh.zip', 'assets/live2d/models'),
],
},
renderer: rendererConfig,
})
33 changes: 33 additions & 0 deletions packages/stage-tamagotchi/renderer.vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { join, resolve } from 'node:path'
import { Download } from '@proj-airi/unplugin-download'
import { DownloadLive2DSDK } from '@proj-airi/unplugin-live2d-sdk'
import Vue from '@vitejs/plugin-vue'
import UnoCss from 'unocss/vite'
import VueRouter from 'unplugin-vue-router/vite'
import { defineConfig } from 'vite'

export default defineConfig({
optimizeDeps: {
exclude: [
'@proj-airi/stage-ui/*',
],
},
resolve: {
alias: {
'@renderer': resolve(join('src', 'renderer', 'src')),
'@proj-airi/stage-ui': resolve(join(import.meta.dirname, '..', 'stage-ui', 'dist')),
'@proj-airi/stage-ui/stores': resolve(join(import.meta.dirname, '..', 'stage-ui', 'dist', 'stores')),
},
},
plugins: [
Vue(),
UnoCss(),
VueRouter({
dts: resolve(import.meta.dirname, 'src/typed-router.d.ts'),
routesFolder: 'src/renderer/src/pages',
}),
DownloadLive2DSDK(),
Download('https://dist.ayaka.moe/live2d-models/hiyori_free_zh.zip', 'hiyori_free_zh.zip', 'assets/live2d/models'),
Download('https://dist.ayaka.moe/live2d-models/hiyori_pro_zh.zip', 'hiyori_pro_zh.zip', 'assets/live2d/models'),
],
})
86 changes: 84 additions & 2 deletions packages/stage-tamagotchi/src/main/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { join } from 'node:path'
import { env, platform } from 'node:process'
import { electronApp, is, optimizer } from '@electron-toolkit/utils'
import { app, BrowserWindow, ipcMain, shell } from 'electron'
import { app, BrowserWindow, dialog, ipcMain, Menu, shell } from 'electron'
import icon from '../../build/icon.png?asset'

function createWindow(): void {
Expand Down Expand Up @@ -50,10 +50,78 @@ function createWindow(): void {
})
}

let settingsWindow: BrowserWindow | null = null

function createSettingsWindow(): void {
if (settingsWindow) {
settingsWindow.show()
return
}

settingsWindow = new BrowserWindow({
width: 300,
height: 400,
show: false,
webPreferences: {
preload: join(import.meta.dirname, '..', 'preload', 'index.js'),
sandbox: false,
},
})

settingsWindow.on('ready-to-show', () => {
settingsWindow?.show()
})

settingsWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
return { action: 'deny' }
})

settingsWindow.on('close', () => {
settingsWindow = null
})

settingsWindow.show()

if (is.dev && env.ELECTRON_RENDERER_URL) {
settingsWindow.loadURL(join(env.ELECTRON_RENDERER_URL, '#/settings'))
}
else {
settingsWindow.loadFile(join(import.meta.dirname, '..', '..', 'out', 'renderer', 'index.html'), {
hash: '/settings',
})
}
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
// Menu
const menu = Menu.buildFromTemplate([
{
label: 'airi',
role: 'appMenu',
submenu: [
{
role: 'about',
},
{
role: 'toggleDevTools',
},
{
label: 'Settings',
click: () => createSettingsWindow(),
},
{
label: 'Quit',
click: () => app.quit(),
},
],
},
])
Menu.setApplicationMenu(menu)

// Set app user model id for windows
electronApp.setAppUserModelId('com.github.moeru-ai.airi-tamagotchi')

Expand All @@ -65,7 +133,21 @@ app.whenReady().then(() => {
})

// IPC test
ipcMain.on('quit', () => app.quit())
// TODO: i18n
ipcMain.on('quit', () => {
dialog.showMessageBox({
type: 'info',
title: 'Quit',
message: 'Are you sure you want to quit?',
buttons: ['Quit', 'Cancel'],
}).then((result) => {
if (result.response === 0) {
app.quit()
}
})
})

ipcMain.on('open-settings', () => createSettingsWindow())

createWindow()

Expand Down
46 changes: 2 additions & 44 deletions packages/stage-tamagotchi/src/renderer/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,49 +1,7 @@
<script setup lang="ts">
import { WidgetStage } from '@proj-airi/stage-ui/components'
import { ref } from 'vue'
import InteractiveArea from './components/InteractiveArea.vue'
const dragDelay = ref(0)
const isDragging = ref(false)
function handleMouseDown() {
dragDelay.value = window.setTimeout(() => {
isDragging.value = true
}, 500)
}
function handleMouseUp() {
clearTimeout(dragDelay.value)
isDragging.value = false
}
function handleMouseLeave() {
isDragging.value = false
}
function handleMouseMove(event: MouseEvent) {
if (isDragging.value) {
window.electron.ipcRenderer.send('move-window', event.movementX, event.movementY)
}
}
import { RouterView } from 'vue-router'
</script>

<template>
<div relative max-h="[100vh]" max-w="[100vw]" p="2" flex="~ col" z-2 h-full overflow-hidden @mousedown="handleMouseDown" @mouseup="handleMouseUp" @mousemove="handleMouseMove" @mouseleave="handleMouseLeave">
<div relative h-full w-full items-end gap-2 class="view">
<WidgetStage h-full w-full flex-1 mb="<md:18" />
<InteractiveArea class="interaction-area block" pointer-events-none absolute bottom-0 w-full opacity-0 transition="opacity duration-250" />
</div>
</div>
<RouterView />
</template>

<style scoped>
.view {
&:hover {
.interaction-area {
opacity: 1;
pointer-events: auto;
}
}
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ import { useChatStore, useSettings } from '@proj-airi/stage-ui/stores'
// import { useDevicesList } from '@vueuse/core'
import { storeToRefs } from 'pinia'
import { DrawerContent, DrawerPortal, DrawerRoot, DrawerTrigger } from 'vaul-vue'
import { onMounted, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import TamagotchiChatHistory from './ChatHistory.vue'
import TamagotchiSettings from './Settings.vue'
const messageInput = ref('')
const listening = ref(false)
Expand Down Expand Up @@ -72,6 +70,10 @@ function handleTranscription(_buffer: Float32Array<ArrayBufferLike>) {
// selectedAudioDevice.value = found
// }
function openSettings() {
window.electron.ipcRenderer.send('open-settings')
}
watch(isAudioInputOn, async (value) => {
if (value === 'false') {
destroy()
Expand Down Expand Up @@ -103,26 +105,16 @@ onMounted(() => {
@submit="handleSend"
/>
</div>
<DrawerRoot should-scale-background>
<DrawerTrigger
class="px-4 py-2.5"
border="solid 2 pink-100 "
text="lg pink-400 hover:pink-600 placeholder:pink-400 placeholder:hover:pink-600"
bg="pink-50 dark:[#3c2632]" max-h="[10lh]" min-h="[1lh]" rounded-r-xl
>
<div i-solar:settings-bold-duotone />
</DrawerTrigger>
<DrawerPortal>
<DrawerContent
max-h="[90%]"
fixed bottom-0 left-0 right-0 z-50 mt-24 h-full flex flex-col rounded-t-lg bg="[#fffbff] dark:[#1f1a1d]"
>
<div class="flex flex-1 flex-col rounded-t-lg p-5" bg="[#fffbff] dark:[#1f1a1d]" gap-2>
<TamagotchiSettings />
</div>
</DrawerContent>
</DrawerPortal>
</DrawerRoot>
<div
class="px-4 py-2.5"
border="solid 2 pink-100 "
text="lg pink-400 hover:pink-600 placeholder:pink-400 placeholder:hover:pink-600"
bg="pink-50 dark:[#3c2632]" max-h="[10lh]" min-h="[1lh]"
flex items-center justify-center rounded-r-xl
@click="openSettings"
>
<div i-solar:settings-bold-duotone />
</div>
</div>
</div>
</template>
11 changes: 10 additions & 1 deletion packages/stage-tamagotchi/src/renderer/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import { MotionPlugin } from '@vueuse/motion'
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import { createI18n } from 'vue-i18n'
import App from './App.vue'
import { createRouter, createWebHashHistory } from 'vue-router'

import { routes } from 'vue-router/auto-routes'

import App from './App.vue'
import '@unocss/reset/tailwind.css'
import 'uno.css'
import './main.css'
Expand All @@ -22,9 +25,15 @@ const i18n = createI18n({
},
})

const router = createRouter({
history: createWebHashHistory(),
routes,
})

createApp(App)
.use(MotionPlugin)
.use(autoAnimatePlugin)
.use(router)
.use(pinia)
.use(i18n)
.use(Tres)
Expand Down
49 changes: 49 additions & 0 deletions packages/stage-tamagotchi/src/renderer/src/pages/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script setup lang="ts">
import { WidgetStage } from '@proj-airi/stage-ui/components'
import { ref } from 'vue'
import InteractiveArea from '../components/InteractiveArea.vue'
const dragDelay = ref(0)
const isDragging = ref(false)
function handleMouseDown() {
dragDelay.value = window.setTimeout(() => {
isDragging.value = true
}, 500)
}
function handleMouseUp() {
clearTimeout(dragDelay.value)
isDragging.value = false
}
function handleMouseLeave() {
isDragging.value = false
}
function handleMouseMove(event: MouseEvent) {
if (isDragging.value) {
window.electron.ipcRenderer.send('move-window', event.movementX, event.movementY)
}
}
</script>

<template>
<div relative max-h="[100vh]" max-w="[100vw]" p="2" flex="~ col" z-2 h-full overflow-hidden @mousedown="handleMouseDown" @mouseup="handleMouseUp" @mousemove="handleMouseMove" @mouseleave="handleMouseLeave">
<div relative h-full w-full items-end gap-2 class="view">
<WidgetStage h-full w-full flex-1 mb="<md:18" />
<InteractiveArea class="interaction-area block" pointer-events-none absolute bottom-0 w-full opacity-0 transition="opacity duration-250" />
</div>
</div>
</template>

<style scoped>
.view {
&:hover {
.interaction-area {
opacity: 1;
pointer-events: auto;
}
}
}
</style>
Loading

0 comments on commit ca02572

Please sign in to comment.