refactor(stage-tamagotchi): cleanup code

This commit is contained in:
Neko Ayaka
2025-10-23 21:40:57 +08:00
parent 08bbeb34c6
commit 04b8e82b21
18 changed files with 6 additions and 1606 deletions
@@ -1,34 +0,0 @@
// import { useMagicKeys } from '@vueuse/core'
// import { until, whenever } from '@vueuse/shared'
// import { storeToRefs } from 'pinia'
// import { watch } from 'vue'
// import { useShortcutsStore } from '../stores/shortcuts'
// import { useAppRuntime } from './runtime'
// import { useTauriRdevEventTarget } from './tauri-rdev'
// export function useTauriGlobalShortcuts() {
// const { shortcuts } = storeToRefs(useShortcutsStore())
// const { platform, isInitialized } = useAppRuntime()
// const eventTarget = useTauriRdevEventTarget()
// const keys = useMagicKeys({ target: eventTarget })
// watch(shortcuts, async () => {
// await until(isInitialized).toBeTruthy()
// if (platform.value === 'web') {
// return
// }
// for (const handler of shortcuts.value) {
// if (!handler.shortcut) {
// return
// }
// whenever(keys[handler.shortcut], async () => {
// handler.handle().catch((error) => {
// console.error('Error handling shortcut', error)
// })
// })
// }
// }, { immediate: true })
// }
@@ -1,42 +0,0 @@
// import type { Position } from '@tauri-apps/plugin-positioner'
// import { computedAsync, until } from '@vueuse/core'
// import { useAppRuntime } from './runtime'
// import { untilImported } from './tauri'
// export function useTauriPositioner() {
// const { platform, isInitialized } = useAppRuntime()
// const tauriPositionerApi = computedAsync(async () => {
// await until(isInitialized).toBeTruthy()
// if (platform.value !== 'web') {
// return untilImported(() => import('@tauri-apps/plugin-positioner'), console.warn)
// }
// })
// async function ensureImported() {
// await until(isInitialized).toBeTruthy()
// if (platform.value === 'web') {
// console.warn('Attempted to use Tauri positioner in web platform')
// return
// }
// await until(tauriPositionerApi).toBeTruthy()
// const imported = await tauriPositionerApi.value
// if (!imported) {
// throw new Error('Tauri positioner API not available')
// }
// }
// async function moveWindow(to: Position) {
// await ensureImported()
// return tauriPositionerApi.value?.moveWindow(to)
// }
// return {
// moveWindow,
// }
// }
@@ -1,117 +0,0 @@
// import type { AiriTamagotchiEvents } from './tauri'
// import { onMounted, onUnmounted } from 'vue'
// import { KeyCode, mapKeyCode, mapKeyKey } from '../tauri/rdev'
// import { useTauriEvent } from './tauri'
// export function useTauriRdevEventTarget(): EventTarget {
// const unListenFuncs: (() => void)[] = []
// const eventTarget = new EventTarget()
// const { listen } = useTauriEvent<AiriTamagotchiEvents>()
// async function setup() {
// window.addEventListener('keyup', (event) => {
// event.preventDefault()
// event.stopPropagation()
// const e = new KeyboardEvent('keyup', {
// key: event.key,
// code: event.code,
// metaKey: event.metaKey,
// ctrlKey: event.ctrlKey,
// altKey: event.altKey,
// shiftKey: event.shiftKey,
// })
// eventTarget.dispatchEvent(e)
// })
// window.addEventListener('keydown', (event) => {
// event.preventDefault()
// event.stopPropagation()
// const e = new KeyboardEvent('keydown', {
// key: event.key,
// code: event.code,
// metaKey: event.metaKey,
// ctrlKey: event.ctrlKey,
// altKey: event.altKey,
// shiftKey: event.shiftKey,
// })
// eventTarget.dispatchEvent(e)
// })
// unListenFuncs.push(await listen('tauri-plugins:tauri-plugin-rdev:mousemove', (event) => {
// if (event.payload.event_type.MouseMove) {
// const { x, y } = event.payload.event_type.MouseMove
// const e = new MouseEvent('mousemove', {
// clientX: x,
// clientY: y,
// })
// eventTarget.dispatchEvent(e)
// }
// }))
// unListenFuncs.push(await listen('tauri-plugins:tauri-plugin-rdev:keyup', (event) => {
// if (typeof event.payload.event_type.KeyRelease === 'object' && 'Unknown' in event.payload.event_type.KeyRelease) {
// if (event.payload.event_type.KeyRelease.Unknown === 62) {
// event.payload.event_type = { KeyRelease: KeyCode.ControlLeft }
// }
// }
// if (typeof event.payload.event_type.KeyRelease !== 'string') {
// console.warn('unknown key release event:', event.payload.event_type.KeyRelease)
// return
// }
// const e = new KeyboardEvent('keyup', {
// key: mapKeyKey[event.payload.event_type.KeyRelease],
// code: mapKeyCode[event.payload.event_type.KeyRelease],
// metaKey: mapKeyKey[event.payload.event_type.KeyRelease] === 'Meta',
// ctrlKey: mapKeyKey[event.payload.event_type.KeyRelease] === 'Control',
// altKey: mapKeyKey[event.payload.event_type.KeyRelease] === 'Alt',
// shiftKey: mapKeyKey[event.payload.event_type.KeyRelease] === 'Shift',
// })
// eventTarget.dispatchEvent(e)
// }))
// unListenFuncs.push(await listen('tauri-plugins:tauri-plugin-rdev:keydown', (event) => {
// if (typeof event.payload.event_type.KeyPress === 'object' && 'Unknown' in event.payload.event_type.KeyPress) {
// if (event.payload.event_type.KeyPress.Unknown === 62) {
// event.payload.event_type = { KeyPress: KeyCode.ControlLeft }
// }
// }
// if (typeof event.payload.event_type.KeyPress !== 'string') {
// console.warn('unknown key release event:', event.payload.event_type.KeyPress)
// return
// }
// const e = new KeyboardEvent('keydown', {
// key: mapKeyKey[event.payload.event_type.KeyPress],
// code: mapKeyCode[event.payload.event_type.KeyPress],
// metaKey: mapKeyKey[event.payload.event_type.KeyPress] === 'Meta',
// ctrlKey: mapKeyKey[event.payload.event_type.KeyPress] === 'Control',
// altKey: mapKeyKey[event.payload.event_type.KeyPress] === 'Alt',
// shiftKey: mapKeyKey[event.payload.event_type.KeyPress] === 'Shift',
// })
// eventTarget.dispatchEvent(e)
// }))
// }
// function cleanup() {
// unListenFuncs.forEach(unListen => unListen())
// unListenFuncs.length = 0
// }
// onMounted(() => setup())
// onUnmounted(() => cleanup())
// if (import.meta.hot) { // For better DX
// import.meta.hot.on('vite:beforeUpdate', () => cleanup())
// import.meta.hot.on('vite:afterUpdate', async () => await setup())
// }
// return eventTarget
// }
@@ -1,134 +0,0 @@
// import type { MaybeRefOrGetter } from 'vue'
// import type { AiriTamagotchiEvents, Point, WindowFrame } from './tauri'
// import { onMounted, onUnmounted, ref, toValue, watch } from 'vue'
// import { startClickThrough, stopClickThrough } from '../utils/windows'
// import { useAppRuntime } from './runtime'
// import { useTauriCore, useTauriEvent } from './tauri'
// export function useTauriPointAndWindowFrame() {
// const { listen } = useTauriEvent<AiriTamagotchiEvents>()
// const { invoke } = useTauriCore()
// const unListenFuncs = ref<(() => void)[]>([])
// const mousePos = ref<Point>({ x: 0, y: 0 })
// const windowFrame = ref<WindowFrame>({
// origin: { x: 0, y: 0 },
// size: { width: 0, height: 0 },
// })
// function _onCursorPosition(event: { payload: Point }) {
// mousePos.value = event.payload
// }
// function _onWindowFrame(event: { payload: WindowFrame }) {
// windowFrame.value = event.payload
// }
// function addListeners() {
// listen('tauri-plugins:tauri-plugin-window-pass-through-on-hover:cursor-position', (event) => {
// _onCursorPosition(event)
// }).then((fn) => {
// unListenFuncs.value.push(fn)
// })
// listen('tauri-plugins:tauri-plugin-window-pass-through-on-hover:window-frame', (event) => {
// _onWindowFrame(event)
// }).then((fn) => {
// unListenFuncs.value.push(fn)
// })
// }
// onMounted(() => {
// addListeners()
// invoke('plugin:window-pass-through-on-hover|start_tracing_cursor')
// })
// onUnmounted(() => {
// unListenFuncs.value.forEach(fn => fn?.())
// unListenFuncs.value.length = 0
// invoke('plugin:window-pass-through-on-hover|stop_tracing_cursor')
// })
// if (import.meta.hot) { // For better DX
// import.meta.hot.on('vite:beforeUpdate', () => {
// unListenFuncs.value.forEach(fn => fn?.())
// unListenFuncs.value.length = 0
// invoke('plugin:window-pass-through-on-hover|stop_tracing_cursor')
// })
// import.meta.hot.on('vite:afterUpdate', () => {
// addListeners()
// invoke('plugin:window-pass-through-on-hover|start_tracing_cursor')
// })
// }
// return {
// mousePos,
// windowFrame,
// }
// }
// export function useTauriWindowClickThrough(live2DLookAtDefault: MaybeRefOrGetter<{ x: number, y: number }>) {
// const { platform, isInitialized } = useAppRuntime()
// const { mousePos, windowFrame } = useTauriPointAndWindowFrame()
// const live2dLookAtX = ref(toValue(live2DLookAtDefault).x)
// const live2dLookAtY = ref(toValue(live2DLookAtDefault).y)
// const isCursorInside = ref(false)
// function updateLive2DLookAt() {
// if (platform.value === 'macos') {
// live2dLookAtX.value = mousePos.value.x - windowFrame.value.origin.x
// live2dLookAtY.value = windowFrame.value.size.height - mousePos.value.y + windowFrame.value.origin.y
// return
// }
// live2dLookAtX.value = mousePos.value.x - windowFrame.value.origin.x
// live2dLookAtY.value = mousePos.value.y - windowFrame.value.origin.y
// }
// watch(mousePos, () => {
// updateLive2DLookAt()
// })
// function updateIsCursorInside() {
// isCursorInside.value
// = mousePos.value.x >= windowFrame.value.origin.x
// && mousePos.value.x <= windowFrame.value.origin.x + windowFrame.value.size.width
// && mousePos.value.y >= windowFrame.value.origin.y
// && mousePos.value.y <= windowFrame.value.origin.y + windowFrame.value.size.height
// }
// watch([mousePos, windowFrame], () => {
// updateIsCursorInside()
// })
// watch(isInitialized, async (initialized) => {
// if (initialized) {
// updateLive2DLookAt()
// updateIsCursorInside()
// }
// })
// onMounted(async () => {
// await startClickThrough()
// })
// onUnmounted(async () => {
// await stopClickThrough()
// })
// return {
// isCursorInside,
// live2dLookAtX,
// live2dLookAtY,
// }
// }
@@ -1,77 +0,0 @@
// import { computedAsync, until } from '@vueuse/core'
// import { useAppRuntime } from './runtime'
// import { untilImported } from './tauri'
// export enum StateFlags {
// SIZE = 1,
// POSITION = 2,
// MAXIMIZED = 4,
// VISIBLE = 8,
// DECORATIONS = 16,
// FULLSCREEN = 32,
// ALL = 63,
// }
// export function useTauriWindowState() {
// const { platform, isInitialized } = useAppRuntime()
// const tauriWindowStateApi = computedAsync(async () => {
// await until(isInitialized).toBeTruthy()
// if (platform.value !== 'web') {
// return untilImported(() => import('@tauri-apps/plugin-window-state'), console.warn)
// }
// })
// async function ensureImported() {
// await until(isInitialized).toBeTruthy()
// if (platform.value === 'web') {
// console.warn('Attempted to save window state in web platform')
// return
// }
// await until(tauriWindowStateApi).toBeTruthy()
// const imported = await tauriWindowStateApi.value
// if (!imported) {
// throw new Error('Tauri window state API not available')
// }
// }
// async function saveWindowState(stateFlag?: StateFlags) {
// await ensureImported()
// if (stateFlag != null) {
// return tauriWindowStateApi.value?.saveWindowState(stateFlag)
// }
// else {
// return tauriWindowStateApi.value?.saveWindowState(tauriWindowStateApi.value.StateFlags.ALL)
// }
// }
// async function restoreState(stateFlag?: StateFlags, windowLabel = 'main') {
// await ensureImported()
// if (stateFlag != null) {
// return tauriWindowStateApi.value?.restoreState(windowLabel, stateFlag)
// }
// else {
// return tauriWindowStateApi.value?.restoreState(windowLabel, tauriWindowStateApi.value.StateFlags.ALL)
// }
// }
// async function restoreStateCurrent(stateFlag?: StateFlags) {
// await ensureImported()
// if (stateFlag != null) {
// return tauriWindowStateApi.value?.restoreStateCurrent(stateFlag)
// }
// else {
// return tauriWindowStateApi.value?.restoreStateCurrent(tauriWindowStateApi.value.StateFlags.ALL)
// }
// }
// return {
// saveWindowState,
// restoreState,
// restoreStateCurrent,
// }
// }
@@ -1,340 +0,0 @@
// import type { InvokeArgs, InvokeOptions } from '@tauri-apps/api/core'
// import type { EventCallback, EventName, UnlistenFn } from '@tauri-apps/api/event'
// import type { Monitor } from '@tauri-apps/api/window'
// import type { InvokeMethods, InvokeMethodShape } from '../tauri/invoke'
// import type { KeyCode } from '../tauri/rdev'
// import { withRetry } from '@moeru/std'
// import { computedAsync, until } from '@vueuse/core'
// import { useAppRuntime } from './runtime'
// export async function untilNoError<T>(fn: () => Promise<T>, onError?: (err?: unknown | null) => void): Promise<T> {
// const fnRetry = withRetry(fn, { retryDelay: 5000, retry: 5, onError })
// return await fnRetry()
// }
// export async function untilImported<T>(fn: () => Promise<T>, onError?: (err?: unknown | null) => void): Promise<T> {
// return await untilNoError(fn, onError)
// }
// export interface Point {
// x: number
// y: number
// }
// export interface Size {
// width: number
// height: number
// }
// export interface WindowFrame {
// origin: Point
// size: Size
// }
// interface Events {
// 'tauri://resize': unknown
// 'tauri://move': { x: number, y: number }
// 'tauri://close-requested': unknown
// 'tauri://destroyed': unknown
// 'tauri://focus': unknown
// 'tauri://blur': unknown
// 'tauri://scale-change': unknown
// 'tauri://theme-changed': unknown
// 'tauri://window-created': unknown
// 'tauri://webview-created': unknown
// 'tauri://drag-enter': unknown
// 'tauri://drag-over': unknown
// 'tauri://drag-drop': unknown
// 'tauri://drag-leave': unknown
// }
// export interface AiriTamagotchiEvents extends Events {
// // from main
// 'tauri-main:main:window-mode:fade-on-hover': true
// 'tauri-main:main:window-mode:move': true
// 'tauri-main:main:window-mode:resize': true
// // from tauri-plugin-window-pass-through-on-hover
// 'tauri-plugins:tauri-plugin-window-pass-through-on-hover:cursor-position': Point
// 'tauri-plugins:tauri-plugin-window-pass-through-on-hover:window-frame': WindowFrame
// 'tauri-plugins:tauri-plugin-window-pass-through-on-hover:pass-through-enabled': boolean
// // from tauri-plugin-ipc-audio-transcription-ort
// 'tauri-plugins:tauri-plugin-ipc-audio-vad-ort:load-model-silero-vad-progress': [boolean, string, number, number, number]
// // from tauri-plugin-ipc-audio-vad-ort
// 'tauri-plugins:tauri-plugin-ipc-audio-transcription-ort:load-model-whisper-progress': [boolean, string, number, number, number]
// // from tauri-plugin-rdev
// 'tauri-plugins:tauri-plugin-rdev:keydown': { time: { secs_since_epoch: number, nanos_since_epoch: number }, name: string, event_type: { KeyPress: KeyCode | { Unknown: number } } } // similar to 'keydown' events from DOM elements
// 'tauri-plugins:tauri-plugin-rdev:keyup': { time: { secs_since_epoch: number, nanos_since_epoch: number }, name: string, event_type: { KeyRelease: KeyCode | { Unknown: number } } } // similar to 'keyup' events from DOM elements
// 'tauri-plugins:tauri-plugin-rdev:mousedown': { time: { secs_since_epoch: number, nanos_since_epoch: number }, name: string, event_type: { ButtonPress: string } } // similar to 'mousedown' events from DOM elements
// 'tauri-plugins:tauri-plugin-rdev:mouseup': { time: { secs_since_epoch: number, nanos_since_epoch: number }, name: string, event_type: { ButtonRelease: string } } // similar to 'mouseup' events from DOM elements
// 'tauri-plugins:tauri-plugin-rdev:mousemove': { time: { secs_since_epoch: number, nanos_since_epoch: number }, name: string, event_type: { MouseMove: { x: number, y: number } } } // similar to 'mousemove' events from DOM elements
// // MCP
// 'mcp_plugin_destroyed': undefined
// }
// export interface DisplayInfo {
// monitors: Monitor[]
// primaryMonitor: Monitor
// }
// export enum PlacementStrategy {
// Center = 'Center',
// TopLeft = 'TopLeft',
// TopRight = 'TopRight',
// BottomLeft = 'BottomLeft',
// BottomRight = 'BottomRight',
// NearCursor = 'NearCursor',
// RestorePrevious = 'RestorePrevious',
// AvoidOverlap = 'AvoidOverlap',
// }
// export interface WindowPlacementRequest {
// window_size: Size
// preferred_position?: Point
// placement_strategy: PlacementStrategy
// }
// export interface WindowPlacementResult {
// position: Point
// target_monitor_id: number
// is_constrained: boolean
// }
// export function useTauriEvent<ES = Events>() {
// const { platform, isInitialized } = useAppRuntime()
// const tauriEventApi = computedAsync(async () => {
// await until(isInitialized).toBeTruthy()
// if (platform.value !== 'web') {
// return untilImported(() => import('@tauri-apps/api/event'), console.warn)
// }
// })
// async function _listen<E extends keyof ES>(event: E, callback: EventCallback<ES[E]>) {
// await until(isInitialized).toBeTruthy()
// if (platform.value === 'web') {
// return () => {}
// }
// await until(tauriEventApi).toBeTruthy()
// const imported = await tauriEventApi.value
// if (!imported) {
// throw new Error('Tauri event API not available')
// }
// return await imported.listen(event as EventName, callback)
// }
// async function listen<E extends keyof ES>(event: E, callback: EventCallback<ES[E]>) {
// let cleanupListener: UnlistenFn | undefined
// _listen(event, callback).then((listener) => {
// cleanupListener = listener
// })
// return () => {
// cleanupListener?.()
// }
// }
// return {
// listen,
// }
// }
// export function useTauriCore<IM extends Record<keyof IM, InvokeMethodShape> = InvokeMethods>() {
// const { platform, isInitialized } = useAppRuntime()
// const tauriCoreApi = computedAsync(async () => {
// await until(isInitialized).toBeTruthy()
// if (platform.value !== 'web') {
// return untilImported(() => import('@tauri-apps/api/core'), console.warn)
// }
// })
// const tauriCoreApiInvoke = computedAsync(async () => {
// await until(isInitialized).toBeTruthy()
// if (platform.value !== 'web') {
// return untilImported(() => import('../tauri/invoke'), console.warn)
// }
// })
// 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`)
// return
// }
// await until(tauriCoreApiInvoke).toBeTruthy()
// const imported = await tauriCoreApiInvoke.value
// if (!imported) {
// throw new Error('Tauri core API not available')
// }
// return await imported.invoke<C, IM>(command, args as InvokeArgs | undefined, options as InvokeOptions | undefined)
// }
// return {
// invoke,
// core: tauriCoreApi,
// }
// }
// export function useTauriDpi() {
// const { platform, isInitialized } = useAppRuntime()
// const tauriDpiApi = computedAsync(async () => {
// await until(isInitialized).toBeTruthy()
// if (platform.value !== 'web') {
// return untilImported(() => import('@tauri-apps/api/window'), console.warn)
// }
// })
// async function createLogicalPosition(x: number, y: number) {
// const imported = await tauriDpiApi.value
// if (!imported) {
// throw new Error('Tauri DPI API not available')
// }
// return new imported.LogicalPosition(x, y)
// }
// return {
// createLogicalPosition,
// }
// }
// export function useTauriWindow() {
// const { platform, isInitialized } = useAppRuntime()
// const { createLogicalPosition } = useTauriDpi()
// const tauriWindowApi = computedAsync(async () => {
// await until(isInitialized).toBeTruthy()
// if (platform.value !== 'web') {
// return untilImported(() => import('@tauri-apps/api/window'), console.warn)
// }
// })
// async function _ensureImported() {
// await until(isInitialized).toBeTruthy()
// if (platform.value === 'web') {
// throw new Error('Tauri Window API is not available in web platform')
// }
// await until(tauriWindowApi).toBeTruthy()
// const imported = await tauriWindowApi.value
// if (!imported) {
// throw new Error('Tauri Window API not available')
// }
// return imported
// }
// async function getCurrentMonitor() {
// try {
// return await _ensureImported().then(imported => imported.currentMonitor())
// }
// catch (error) {
// console.error('Failed to get current monitor:', error)
// return undefined
// }
// }
// async function getAvailableMonitors() {
// try {
// return await _ensureImported().then(imported => imported.availableMonitors())
// }
// catch (error) {
// console.error('Failed to get available monitors:', error)
// return []
// }
// }
// async function getPrimaryMonitor() {
// try {
// return await _ensureImported().then(imported => imported.primaryMonitor())
// }
// catch (error) {
// console.error('Failed to get primary monitor:', error)
// return undefined
// }
// }
// async function setPosition(x: number, y: number) {
// try {
// const imported = await _ensureImported()
// const window = imported.getCurrentWindow()
// return await window.setPosition(await createLogicalPosition(x, y))
// }
// catch (error) {
// console.error('Failed to set window position:', error)
// }
// }
// async function getPosition() {
// try {
// const imported = await _ensureImported()
// const window = imported.getCurrentWindow()
// return await window.innerPosition()
// }
// catch (error) {
// console.error('Failed to get window position:', error)
// return undefined
// }
// }
// async function closeWindow(label?: string) {
// try {
// const imported = await _ensureImported()
// if (!label) {
// const window = imported.getCurrentWindow()
// return await window.close()
// }
// else {
// const windows = await imported.getAllWindows()
// const targetWindow = windows.find(win => win.label === label)
// if (targetWindow) {
// return await targetWindow.close()
// }
// else {
// console.warn(`No window found with label: ${label}`)
// }
// }
// }
// catch (error) {
// console.error('Failed to close window:', error)
// }
// }
// return {
// getAvailableMonitors,
// getCurrentMonitor,
// getPrimaryMonitor,
// setPosition,
// getPosition,
// closeWindow,
// }
// }
// export function createTauriEventTarget(): EventTarget {
// const eventTarget = new EventTarget()
// return eventTarget
// }
@@ -1,30 +0,0 @@
// import type { AiriTamagotchiEvents } from './tauri'
// import { createSharedComposable } from '@vueuse/core'
// import { ref } from 'vue'
// import { useTauriEvent } from './tauri'
// export const useRdevMouse = createSharedComposable(() => {
// const mouseX = ref(0)
// const mouseY = ref(0)
// const { listen } = useTauriEvent<AiriTamagotchiEvents>()
// async function setup() {
// await listen('tauri-plugins:tauri-plugin-rdev:mousemove', (event) => {
// if (event.payload.event_type.MouseMove) {
// const { x, y } = event.payload.event_type.MouseMove
// mouseX.value = x
// mouseY.value = y
// }
// })
// }
// setup()
// return {
// mouseX,
// mouseY,
// }
// })
@@ -4,7 +4,7 @@ import { useCanvasPixelIsTransparentAtPoint } from '@proj-airi/stage-ui/composab
import { useLive2d } from '@proj-airi/stage-ui/stores/live2d'
import { debouncedRef, watchPausable } from '@vueuse/core'
import { storeToRefs } from 'pinia'
import { computed, ref, toRef, watch } from 'vue'
import { ref, toRef, watch } from 'vue'
import ControlsIsland from '../components/Widgets/ControlsIsland/index.vue'
import ResourceStatusIsland from '../components/Widgets/ResourceStatusIsland/index.vue'
@@ -12,8 +12,6 @@ import ResourceStatusIsland from '../components/Widgets/ResourceStatusIsland/ind
import { electron } from '../../shared/electron'
import { useElectronEventaInvoke, useElectronMouseInElement, useElectronRelativeMouse } from '../composables/electron-vueuse'
import { useWindowStore } from '../stores/window'
import { useWindowControlStore } from '../stores/window-controls'
import { WindowControlMode } from '../types/window-controls'
const resourceStatusIslandRef = ref<InstanceType<typeof ResourceStatusIsland>>()
const controlsIslandRef = ref<InstanceType<typeof ControlsIsland>>()
@@ -24,7 +22,6 @@ const isPassingThrough = ref(false)
const isLoading = ref(true)
const componentStateStage = ref<'pending' | 'loading' | 'mounted'>('pending')
const windowControlStore = useWindowControlStore()
const { x: relativeMouseX, y: relativeMouseY } = useElectronRelativeMouse()
const isTransparent = useCanvasPixelIsTransparentAtPoint(stageCanvas, relativeMouseX, relativeMouseY)
const setIgnoreMouseEvents = useElectronEventaInvoke(electron.window.setIgnoreMouseEvents)
@@ -34,26 +31,12 @@ const isOutsideFor250Ms = debouncedRef(isOutside, 250)
const { scale, positionInPercentageString } = storeToRefs(useLive2d())
const { live2dLookAtX, live2dLookAtY } = storeToRefs(useWindowStore())
const modeIndicatorClass = computed(() => {
switch (windowControlStore.controlMode) {
case WindowControlMode.MOVE:
return 'cursor-move'
case WindowControlMode.RESIZE:
return 'cursor-se-resize'
case WindowControlMode.DEBUG:
return 'debug-mode'
default:
return ''
}
})
watch(componentStateStage, () => isLoading.value = componentStateStage.value !== 'mounted', { immediate: true })
const { pause, resume } = watchPausable(isTransparent, (transparent) => {
isClickThrough.value = transparent
isPassingThrough.value = !transparent
windowControlStore.isIgnoringMouseEvent = !transparent
if (windowControlStore.isIgnoringMouseEvent) {
if (isPassingThrough.value) {
setIgnoreMouseEvents([true, { forward: true }])
}
else {
@@ -65,14 +48,12 @@ watch(isOutsideFor250Ms, () => {
if (!isOutsideFor250Ms.value) {
isClickThrough.value = false
isPassingThrough.value = false
windowControlStore.isIgnoringMouseEvent = false
setIgnoreMouseEvents([false, { forward: true }])
pause()
}
else {
isClickThrough.value = true
isPassingThrough.value = true
windowControlStore.isIgnoringMouseEvent = true
setIgnoreMouseEvents([true, { forward: true }])
resume()
}
@@ -81,7 +62,6 @@ watch(isOutsideFor250Ms, () => {
<template>
<div
:class="[modeIndicatorClass]"
max-h="[100vh]"
max-w="[100vw]"
flex="~ col"
@@ -97,7 +77,7 @@ watch(isOutsideFor250Ms, () => {
>
<div
:class="[
windowControlStore.isIgnoringMouseEvent && !isClickThrough ? 'op-0' : 'op-100',
isPassingThrough && !isClickThrough ? 'op-0' : 'op-100',
'absolute',
'top-0 left-0 w-full h-full',
'transition-opacity duration-250 ease-in-out',
@@ -153,7 +133,7 @@ watch(isOutsideFor250Ms, () => {
leave-to-class="opacity-0"
>
<div
v-if="windowControlStore.controlMode === WindowControlMode.MOVE"
v-if="false"
class="absolute left-0 top-0 z-99 h-full w-full flex cursor-grab items-center justify-center overflow-hidden drag-region"
>
<div
@@ -177,7 +157,7 @@ watch(isOutsideFor250Ms, () => {
leave-to-class="opacity-50"
>
<div
v-if="windowControlStore.controlMode === WindowControlMode.RESIZE"
v-if="false"
class="absolute left-0 top-0 z-999 h-full w-full"
>
<div h-full w-full animate-flash animate-duration-2.5s animate-count-infinite b-4 b-primary rounded-2xl />
@@ -2,8 +2,6 @@
import { OnboardingScreen } from '@proj-airi/stage-ui/components'
import { useOnboardingStore } from '@proj-airi/stage-ui/stores/onboarding'
// import { useTauriWindow } from '../composables/tauri'
// const window = useTauriWindow()
const onboardingStore = useOnboardingStore()
@@ -1,73 +0,0 @@
// import type { AiriTamagotchiEvents } from '../composables/tauri'
// import { getCurrentWindow } from '@tauri-apps/api/window'
import { defineStore } from 'pinia'
import { onMounted, onUnmounted, ref } from 'vue'
// import { useTauriEvent } from '../composables/tauri'
import { WindowControlMode } from '../types/window-controls'
import { startClickThrough, stopClickThrough } from '../utils/windows'
export const useWindowControlStore = defineStore('windowControl', () => {
const controlMode = ref<WindowControlMode>(WindowControlMode.NONE)
const isControlActive = ref(false)
const isIgnoringMouseEvent = ref(false)
function toggleMode(mode: WindowControlMode) {
controlMode.value = mode
isControlActive.value = !isControlActive.value
if (!isControlActive.value) {
controlMode.value = WindowControlMode.NONE
if (isIgnoringMouseEvent.value)
startClickThrough()
return
}
stopClickThrough()
// const window = getCurrentWindow()
// window.setFocus()
}
return {
controlMode,
isControlActive,
isIgnoringMouseEvent,
toggleMode,
}
})
export const useWindowMode = defineStore('window-mode', () => {
// const { listen } = useTauriEvent<AiriTamagotchiEvents>()
// const windowStore = useWindowControlStore()
const unlistenFuncs = ref<(() => void)[]>([])
async function setup() {
// unlistenFuncs.value.push(await listen('tauri-main:main:window-mode:move', () => {
// windowStore.toggleMode(WindowControlMode.MOVE)
// }))
// unlistenFuncs.value.push(await listen('tauri-main:main:window-mode:resize', () => {
// windowStore.toggleMode(WindowControlMode.RESIZE)
// }))
// unlistenFuncs.value.push(await listen('tauri-main:main:window-mode:fade-on-hover', async () => {
// windowStore.isIgnoringMouseEvent = !windowStore.isIgnoringMouseEvent
// }))
}
function cleanup() {
unlistenFuncs.value.forEach(unlisten => unlisten())
unlistenFuncs.value.length = 0
}
onMounted(setup)
onUnmounted(cleanup)
if (import.meta.hot) { // For better DX
import.meta.hot.on('vite:beforeUpdate', () => cleanup())
import.meta.hot.on('vite:afterUpdate', async () => await setup())
}
})
@@ -1,13 +1,10 @@
import { useWindowSize } from '@vueuse/core'
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
import { computed } from 'vue'
import { useElectronRelativeMouse } from '../composables/electron-vueuse'
import { useWindowControlStore } from './window-controls'
export const useWindowStore = defineStore('tamagotchi-window', () => {
const windowControlStore = useWindowControlStore()
const { width, height } = useWindowSize()
const centerPos = computed(() => ({ x: width.value / 2, y: height.value / 2 }))
@@ -15,16 +12,11 @@ export const useWindowStore = defineStore('tamagotchi-window', () => {
// Transforms screen coordinates to window-relative coordinates
const { x: live2dLookAtX, y: live2dLookAtY } = useElectronRelativeMouse({ initialValue: centerPos.value })
const isCursorInside = ref(false)
const shouldHideView = computed(() => isCursorInside.value && !windowControlStore.isControlActive && windowControlStore.isIgnoringMouseEvent)
return {
width,
height,
centerPos,
live2dLookAtX,
live2dLookAtY,
isCursorInside,
shouldHideView,
}
})
@@ -1,35 +0,0 @@
// import type { InvokeArgs, InvokeOptions } from '@tauri-apps/api/core'
// import { invoke as tauriInvoke } from '@tauri-apps/api/core'
// export interface InvokeMethods {
// // app windows
// 'open_settings_window': { args: undefined, options: undefined, returns: void }
// 'open_chat_window': { args: undefined, options: undefined, returns: void }
// // Plugin - Audio Transcription
// 'plugin:ipc-audio-transcription-ort|load_ort_model_whisper': { args: { modelType: 'base' | 'largev3' | 'tiny' | 'medium' }, options: undefined, returns: void }
// 'plugin:ipc-audio-transcription-ort|ipc_audio_transcription': { args: { chunk: number[], language: string }, options: undefined, returns: string }
// // Plugin - Audio VAD
// 'plugin:ipc-audio-vad-ort|load_ort_model_silero_vad': { args: undefined, options: undefined, returns: void }
// 'plugin:ipc-audio-vad-ort|ipc_audio_vad': { args: { inputData: { input: number[], sr: number, state: number[] } }, options: undefined, returns: number }
// // Plugin - Window Pass through on hover
// 'plugin:window-pass-through-on-hover|start_tracing_cursor': { args: undefined, options: undefined, returns: void }
// 'plugin:window-pass-through-on-hover|stop_tracing_cursor': { args: undefined, options: undefined, returns: void }
// }
// export interface InvokeMethodShape {
// args: InvokeArgs | undefined
// options: InvokeOptions | undefined
// returns: any
// }
// export async function invoke<C extends keyof IM, IM extends Record<keyof IM, InvokeMethodShape> = InvokeMethods>(
// command: C,
// args?: IM[C]['args'],
// options?: IM[C]['options'],
// ): Promise<IM[C]['returns'] | undefined> {
// return await tauriInvoke(command as string, args as InvokeArgs | undefined, options as InvokeOptions | undefined)
// }
@@ -1,407 +0,0 @@
// rdev enums
export enum KeyCode {
// Alt key on Linux and Windows (option key on macOS)
Alt = 'Alt',
AltGr = 'AltGr',
Backspace = 'Backspace',
CapsLock = 'CapsLock',
ControlLeft = 'ControlLeft',
ControlRight = 'ControlRight',
Delete = 'Delete',
DownArrow = 'DownArrow',
End = 'End',
Escape = 'Escape',
F1 = 'F1',
F10 = 'F10',
F11 = 'F11',
F12 = 'F12',
F13 = 'F13',
F14 = 'F14',
F15 = 'F15',
F16 = 'F16',
F17 = 'F17',
F18 = 'F18',
F19 = 'F19',
F20 = 'F20',
F21 = 'F21',
F22 = 'F22',
F23 = 'F23',
F24 = 'F24',
F2 = 'F2',
F3 = 'F3',
F4 = 'F4',
F5 = 'F5',
F6 = 'F6',
F7 = 'F7',
F8 = 'F8',
F9 = 'F9',
Home = 'Home',
LeftArrow = 'LeftArrow',
/// also known as "windows", "super", and "command"
MetaLeft = 'MetaLeft',
/// also known as "windows", "super", and "command"
MetaRight = 'MetaRight',
PageDown = 'PageDown',
PageUp = 'PageUp',
Return = 'Return',
RightArrow = 'RightArrow',
ShiftLeft = 'ShiftLeft',
ShiftRight = 'ShiftRight',
Space = 'Space',
Tab = 'Tab',
UpArrow = 'UpArrow',
PrintScreen = 'PrintScreen',
ScrollLock = 'ScrollLock',
Pause = 'Pause',
NumLock = 'NumLock',
BackQuote = 'BackQuote',
Num1 = 'Num1',
Num2 = 'Num2',
Num3 = 'Num3',
Num4 = 'Num4',
Num5 = 'Num5',
Num6 = 'Num6',
Num7 = 'Num7',
Num8 = 'Num8',
Num9 = 'Num9',
Num0 = 'Num0',
Minus = 'Minus',
Equal = 'Equal',
KeyQ = 'KeyQ',
KeyW = 'KeyW',
KeyE = 'KeyE',
KeyR = 'KeyR',
KeyT = 'KeyT',
KeyY = 'KeyY',
KeyU = 'KeyU',
KeyI = 'KeyI',
KeyO = 'KeyO',
KeyP = 'KeyP',
LeftBracket = 'LeftBracket',
RightBracket = 'RightBracket',
KeyA = 'KeyA',
KeyS = 'KeyS',
KeyD = 'KeyD',
KeyF = 'KeyF',
KeyG = 'KeyG',
KeyH = 'KeyH',
KeyJ = 'KeyJ',
KeyK = 'KeyK',
KeyL = 'KeyL',
SemiColon = 'SemiColon',
Quote = 'Quote',
BackSlash = 'BackSlash',
IntlBackslash = 'IntlBackslash',
KeyZ = 'KeyZ',
KeyX = 'KeyX',
KeyC = 'KeyC',
KeyV = 'KeyV',
KeyB = 'KeyB',
KeyN = 'KeyN',
KeyM = 'KeyM',
Comma = 'Comma',
Dot = 'Dot',
Slash = 'Slash',
Insert = 'Insert',
KpReturn = 'KpReturn',
KpMinus = 'KpMinus',
KpPlus = 'KpPlus',
KpMultiply = 'KpMultiply',
KpDivide = 'KpDivide',
Kp0 = 'Kp0',
Kp1 = 'Kp1',
Kp2 = 'Kp2',
Kp3 = 'Kp3',
Kp4 = 'Kp4',
Kp5 = 'Kp5',
Kp6 = 'Kp6',
Kp7 = 'Kp7',
Kp8 = 'Kp8',
Kp9 = 'Kp9',
KpDelete = 'KpDelete',
Function = 'Function',
VolumeUp = 'VolumeUp',
VolumeDown = 'VolumeDown',
VolumeMute = 'VolumeMute',
BrightnessUp = 'BrightnessUp',
BrightnessDown = 'BrightnessDown',
PreviousTrack = 'PreviousTrack',
PlayPause = 'PlayPause',
PlayCd = 'PlayCd',
NextTrack = 'NextTrack',
Unknown = 'Unknown',
}
// Follows the exact order
// https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values#code_values_on_windows
export const mapKeyCode: Record<KeyCode, string> = {
[KeyCode.Escape]: 'Escape',
[KeyCode.Kp0]: 'Digit0',
[KeyCode.Kp1]: 'Digit1',
[KeyCode.Kp2]: 'Digit2',
[KeyCode.Kp3]: 'Digit3',
[KeyCode.Kp4]: 'Digit4',
[KeyCode.Kp5]: 'Digit5',
[KeyCode.Kp6]: 'Digit6',
[KeyCode.Kp7]: 'Digit7',
[KeyCode.Kp8]: 'Digit8',
[KeyCode.Kp9]: 'Digit9',
[KeyCode.Minus]: 'Minus',
[KeyCode.Equal]: 'Equal',
[KeyCode.Backspace]: 'Backspace',
[KeyCode.Tab]: 'Tab',
[KeyCode.KeyQ]: 'KeyQ',
[KeyCode.KeyW]: 'KeyW',
[KeyCode.KeyE]: 'KeyE',
[KeyCode.KeyR]: 'KeyR',
[KeyCode.KeyT]: 'KeyT',
[KeyCode.KeyY]: 'KeyY',
[KeyCode.KeyU]: 'KeyU',
[KeyCode.KeyI]: 'KeyI',
[KeyCode.KeyO]: 'KeyO',
[KeyCode.KeyP]: 'KeyP',
[KeyCode.LeftBracket]: 'BracketLeft',
[KeyCode.RightBracket]: 'BracketRight',
[KeyCode.Return]: 'Enter',
[KeyCode.ControlLeft]: 'ControlLeft',
[KeyCode.KeyA]: 'KeyA',
[KeyCode.KeyS]: 'KeyS',
[KeyCode.KeyD]: 'KeyD',
[KeyCode.KeyF]: 'KeyF',
[KeyCode.KeyG]: 'KeyG',
[KeyCode.KeyH]: 'KeyH',
[KeyCode.KeyJ]: 'KeyJ',
[KeyCode.KeyK]: 'KeyK',
[KeyCode.KeyL]: 'KeyL',
[KeyCode.SemiColon]: 'SemiColon',
[KeyCode.Quote]: 'Quote',
[KeyCode.BackQuote]: 'BackQuote',
[KeyCode.ShiftLeft]: 'ShiftLeft',
[KeyCode.BackSlash]: 'BackSlash',
[KeyCode.KeyZ]: 'KeyZ',
[KeyCode.KeyX]: 'KeyX',
[KeyCode.KeyC]: 'KeyC',
[KeyCode.KeyV]: 'KeyV',
[KeyCode.KeyB]: 'KeyB',
[KeyCode.KeyN]: 'KeyN',
[KeyCode.KeyM]: 'KeyM',
[KeyCode.Comma]: 'Comma',
[KeyCode.Dot]: 'Period',
[KeyCode.Slash]: 'Slash',
[KeyCode.ShiftRight]: 'ShiftRight',
[KeyCode.KpMultiply]: 'NumpadMultiply',
[KeyCode.Alt]: 'Alt',
[KeyCode.Space]: 'Space',
[KeyCode.CapsLock]: 'CapsLock',
[KeyCode.F1]: 'F1',
[KeyCode.F2]: 'F2',
[KeyCode.F3]: 'F3',
[KeyCode.F4]: 'F4',
[KeyCode.F5]: 'F5',
[KeyCode.F6]: 'F6',
[KeyCode.F7]: 'F7',
[KeyCode.F8]: 'F8',
[KeyCode.F9]: 'F9',
[KeyCode.F10]: 'F10',
[KeyCode.ScrollLock]: 'ScrollLock',
[KeyCode.Num7]: 'Numpad7',
[KeyCode.Num8]: 'Numpad8',
[KeyCode.Num9]: 'Numpad9',
[KeyCode.KpMinus]: 'NumpadSubtract',
[KeyCode.Num4]: 'Numpad4',
[KeyCode.Num5]: 'Numpad5',
[KeyCode.Num6]: 'Numpad6',
[KeyCode.KpPlus]: 'NumpadAdd',
[KeyCode.Num1]: 'Numpad1',
[KeyCode.Num2]: 'Numpad2',
[KeyCode.Num3]: 'Numpad3',
[KeyCode.Num0]: 'Numpad0',
// Numpad Decimal?
[KeyCode.IntlBackslash]: 'IntlBackslash',
[KeyCode.F11]: 'F11',
[KeyCode.F12]: 'F12',
// NumpadEqual?
[KeyCode.F13]: 'F13',
[KeyCode.F14]: 'F14',
[KeyCode.F15]: 'F15',
[KeyCode.F16]: 'F16',
[KeyCode.F17]: 'F17',
[KeyCode.F18]: 'F18',
[KeyCode.F19]: 'F19',
[KeyCode.F20]: 'F20',
[KeyCode.F21]: 'F21',
[KeyCode.F22]: 'F22',
[KeyCode.F23]: 'F23',
[KeyCode.F24]: 'F24',
// NumpadComma?
[KeyCode.PreviousTrack]: 'MediaTrackPrevious',
[KeyCode.NextTrack]: 'MediaTrackNext',
[KeyCode.KpReturn]: 'NumpadEnter',
[KeyCode.ControlRight]: 'ControlRight',
[KeyCode.VolumeMute]: 'VolumeMute',
[KeyCode.PlayPause]: 'MediaPlayPause',
[KeyCode.VolumeDown]: 'VolumeDown',
[KeyCode.VolumeUp]: 'VolumeUp',
[KeyCode.KpDivide]: 'NumpadDivide',
[KeyCode.PrintScreen]: 'PrintScreen',
[KeyCode.AltGr]: 'AltGr',
[KeyCode.NumLock]: 'NumLock',
[KeyCode.Pause]: 'Pause',
[KeyCode.Home]: 'Home',
[KeyCode.UpArrow]: 'ArrowUp',
[KeyCode.PageUp]: 'PageUp',
[KeyCode.LeftArrow]: 'ArrowLeft',
[KeyCode.RightArrow]: 'ArrowRight',
[KeyCode.End]: 'End',
[KeyCode.DownArrow]: 'ArrowDown',
[KeyCode.PageDown]: 'PageDown',
[KeyCode.Insert]: 'Insert',
[KeyCode.Delete]: 'Delete',
// also known as "windows", "super", and "command"
[KeyCode.MetaLeft]: 'MetaLeft',
// also known as "windows", "super", and "command"
[KeyCode.MetaRight]: 'MetaRight',
[KeyCode.KpDelete]: 'NumpadDelete',
[KeyCode.Function]: 'Function',
[KeyCode.BrightnessUp]: 'BrightnessUp',
[KeyCode.BrightnessDown]: 'BrightnessDown',
[KeyCode.PlayCd]: 'PlayCd',
[KeyCode.Unknown]: 'Unknown',
}
export const mapKeyKey: Record<KeyCode, string> = {
[KeyCode.Escape]: 'Escape',
[KeyCode.Kp0]: '0',
[KeyCode.Kp1]: '1',
[KeyCode.Kp2]: '2',
[KeyCode.Kp3]: '3',
[KeyCode.Kp4]: '4',
[KeyCode.Kp5]: '5',
[KeyCode.Kp6]: '6',
[KeyCode.Kp7]: '7',
[KeyCode.Kp8]: '8',
[KeyCode.Kp9]: '9',
[KeyCode.Minus]: '-',
[KeyCode.Equal]: '=',
[KeyCode.Backspace]: 'Backspace',
[KeyCode.Tab]: 'Tab',
[KeyCode.KeyQ]: 'q',
[KeyCode.KeyW]: 'w',
[KeyCode.KeyE]: 'e',
[KeyCode.KeyR]: 'r',
[KeyCode.KeyT]: 't',
[KeyCode.KeyY]: 'y',
[KeyCode.KeyU]: 'u',
[KeyCode.KeyI]: 'i',
[KeyCode.KeyO]: 'o',
[KeyCode.KeyP]: 'p',
[KeyCode.LeftBracket]: '[',
[KeyCode.RightBracket]: ']',
[KeyCode.Return]: 'Enter',
[KeyCode.ControlLeft]: 'Control',
[KeyCode.KeyA]: 'a',
[KeyCode.KeyS]: 's',
[KeyCode.KeyD]: 'd',
[KeyCode.KeyF]: 'f',
[KeyCode.KeyG]: 'g',
[KeyCode.KeyH]: 'h',
[KeyCode.KeyJ]: 'j',
[KeyCode.KeyK]: 'k',
[KeyCode.KeyL]: 'l',
[KeyCode.SemiColon]: ';',
[KeyCode.Quote]: '\'',
[KeyCode.BackQuote]: '`',
[KeyCode.ShiftLeft]: 'Shift',
[KeyCode.BackSlash]: '\\',
[KeyCode.KeyZ]: 'z',
[KeyCode.KeyX]: 'x',
[KeyCode.KeyC]: 'c',
[KeyCode.KeyV]: 'v',
[KeyCode.KeyB]: 'b',
[KeyCode.KeyN]: 'n',
[KeyCode.KeyM]: 'm',
[KeyCode.Comma]: ',',
[KeyCode.Dot]: '.',
[KeyCode.Slash]: '/',
[KeyCode.ShiftRight]: 'Shift',
[KeyCode.KpMultiply]: '*',
[KeyCode.Alt]: 'Alt',
[KeyCode.Space]: ' ',
[KeyCode.CapsLock]: 'CapsLock',
[KeyCode.F1]: 'F1',
[KeyCode.F2]: 'F2',
[KeyCode.F3]: 'F3',
[KeyCode.F4]: 'F4',
[KeyCode.F5]: 'F5',
[KeyCode.F6]: 'F6',
[KeyCode.F7]: 'F7',
[KeyCode.F8]: 'F8',
[KeyCode.F9]: 'F9',
[KeyCode.F10]: 'F10',
[KeyCode.ScrollLock]: 'ScrollLock',
[KeyCode.Num7]: '7',
[KeyCode.Num8]: '8',
[KeyCode.Num9]: '9',
[KeyCode.KpMinus]: '-',
[KeyCode.Num4]: '4',
[KeyCode.Num5]: '5',
[KeyCode.Num6]: '6',
[KeyCode.KpPlus]: '+',
[KeyCode.Num1]: '1',
[KeyCode.Num2]: '2',
[KeyCode.Num3]: '3',
[KeyCode.Num0]: '0',
// Numpad Decimal?
[KeyCode.IntlBackslash]: 'IntlBackslash',
[KeyCode.F11]: 'F11',
[KeyCode.F12]: 'F12',
// NumpadEqual?
[KeyCode.F13]: 'F13',
[KeyCode.F14]: 'F14',
[KeyCode.F15]: 'F15',
[KeyCode.F16]: 'F16',
[KeyCode.F17]: 'F17',
[KeyCode.F18]: 'F18',
[KeyCode.F19]: 'F19',
[KeyCode.F20]: 'F20',
[KeyCode.F21]: 'F21',
[KeyCode.F22]: 'F22',
[KeyCode.F23]: 'F23',
[KeyCode.F24]: 'F24',
// NumpadComma?
[KeyCode.PreviousTrack]: 'MediaTrackPrevious',
[KeyCode.NextTrack]: 'MediaTrackNext',
[KeyCode.KpReturn]: 'Enter',
[KeyCode.ControlRight]: 'Control',
[KeyCode.VolumeMute]: 'VolumeMute',
[KeyCode.PlayPause]: 'MediaPlayPause',
[KeyCode.VolumeDown]: 'VolumeDown',
[KeyCode.VolumeUp]: 'VolumeUp',
[KeyCode.KpDivide]: '/',
[KeyCode.PrintScreen]: 'PrintScreen',
[KeyCode.AltGr]: 'Alt',
[KeyCode.NumLock]: 'NumLock',
[KeyCode.Pause]: 'Pause',
[KeyCode.Home]: 'Home',
[KeyCode.UpArrow]: 'ArrowUp',
[KeyCode.PageUp]: 'PageUp',
[KeyCode.LeftArrow]: 'ArrowLeft',
[KeyCode.RightArrow]: 'ArrowRight',
[KeyCode.End]: 'End',
[KeyCode.DownArrow]: 'ArrowDown',
[KeyCode.PageDown]: 'PageDown',
[KeyCode.Insert]: 'Insert',
[KeyCode.Delete]: 'Delete',
// also known as "windows", "super", and "command"
[KeyCode.MetaLeft]: 'Meta',
// also known as "windows", "super", and "command"
[KeyCode.MetaRight]: 'Meta',
[KeyCode.KpDelete]: 'Delete',
[KeyCode.Function]: 'Function',
[KeyCode.BrightnessUp]: 'BrightnessUp',
[KeyCode.BrightnessDown]: 'BrightnessDown',
[KeyCode.PlayCd]: 'PlayCd',
[KeyCode.Unknown]: 'Unknown',
}
@@ -1,3 +0,0 @@
// export { createVADStates } from './manager'
// export type { VADAudioOptions } from './manager'
// export { createVAD, VAD } from './vad'
@@ -1,2 +0,0 @@
// export type { VADAudioOptions } from '@proj-airi/stage-ui/libs/audio/vad'
// export { createVADStates } from '@proj-airi/stage-ui/libs/audio/vad'
@@ -1,53 +0,0 @@
// // vad-worklet-processor.ts
// // This file needs to be registered as an AudioWorklet
// /**
// * Minimum chunk size for processing audio
// */
// const MIN_CHUNK_SIZE = 512
// /**
// * Global state for audio buffer accumulation
// */
// let globalPointer = 0
// const globalBuffer = new Float32Array(MIN_CHUNK_SIZE)
// /**
// * VAD AudioWorklet Processor - processes audio chunks and sends them to the main thread
// */
// class VADProcessor extends AudioWorkletProcessor {
// process(inputs: Float32Array[][], _outputs: Float32Array[][], _parameters: Record<string, Float32Array>) {
// const buffer = inputs[0][0]
// if (!buffer)
// return true // buffer is null when the stream ends
// if (buffer.length > MIN_CHUNK_SIZE) {
// // If the buffer is larger than the minimum chunk size, send the entire buffer
// this.port.postMessage({ buffer })
// }
// else {
// const remaining = MIN_CHUNK_SIZE - globalPointer
// if (buffer.length >= remaining) {
// // If the buffer is larger than (or equal to) the remaining space in the global buffer, copy the remaining space
// globalBuffer.set(buffer.subarray(0, remaining), globalPointer)
// // Send the global buffer
// this.port.postMessage({ buffer: globalBuffer })
// // Reset the global buffer and set the remaining buffer
// globalBuffer.fill(0)
// globalBuffer.set(buffer.subarray(remaining), 0)
// globalPointer = buffer.length - remaining
// }
// else {
// // If the buffer is smaller than the remaining space in the global buffer, copy the buffer to the global buffer
// globalBuffer.set(buffer, globalPointer)
// globalPointer += buffer.length
// }
// }
// return true
// }
// }
// registerProcessor('vad-audio-worklet-processor', VADProcessor)
@@ -1,217 +0,0 @@
// import type { BaseVAD, BaseVADConfig, VADEventCallback, VADEvents } from '@proj-airi/stage-ui/libs/audio/vad'
// import { invoke } from '../invoke'
// export class VAD implements BaseVAD {
// private config: BaseVADConfig
// private state: Float32Array = new Float32Array(2 * 1 * 128) // 2, 1, 128
// private buffer: Float32Array
// private bufferPointer: number = 0
// private isRecording: boolean = false
// private postSpeechSamples: number = 0
// private prevBuffers: Float32Array[] = []
// private inferenceChain: Promise<any> = Promise.resolve()
// private eventListeners: Partial<Record<keyof VADEvents, VADEventCallback<any>[]>> = {}
// private isReady: boolean = false
// constructor(userConfig: Partial<BaseVADConfig> = {}) {
// const defaultConfig: BaseVADConfig = {
// sampleRate: 16000,
// speechThreshold: 0.3,
// exitThreshold: 0.1,
// minSilenceDurationMs: 400,
// speechPadMs: 80,
// minSpeechDurationMs: 250,
// maxBufferDuration: 30,
// newBufferSize: 512,
// }
// this.config = { ...defaultConfig, ...userConfig }
// this.buffer = new Float32Array(this.config.maxBufferDuration * this.config.sampleRate)
// }
// public async initialize(): Promise<void> {
// try {
// this.emit('status', { type: 'info', message: 'Loading VAD model...' })
// await invoke('plugin:ipc-audio-vad-ort|load_ort_model_silero_vad')
// this.isReady = true
// this.emit('status', { type: 'info', message: 'VAD model loaded successfully' })
// }
// catch (error) {
// this.emit('status', { type: 'error', message: `Failed to load VAD model: ${error}` })
// throw error
// }
// }
// public on<K extends keyof VADEvents>(event: K, callback: VADEventCallback<K>): void {
// if (!this.eventListeners[event]) {
// this.eventListeners[event] = []
// }
// this.eventListeners[event]!.push(callback as any)
// }
// public off<K extends keyof VADEvents>(event: K, callback: VADEventCallback<K>): void {
// if (!this.eventListeners[event])
// return
// this.eventListeners[event] = this.eventListeners[event]!.filter(cb => cb !== callback)
// }
// private emit<K extends keyof VADEvents>(event: K, data: VADEvents[K]): void {
// if (!this.eventListeners[event])
// return
// for (const callback of this.eventListeners[event]!) {
// callback(data)
// }
// }
// public async processAudio(inputBuffer: Float32Array): Promise<void> {
// if (!this.isReady) {
// throw new Error('VAD model is not initialized. Call initialize() first.')
// }
// const wasRecording = this.isRecording
// // Perform VAD using Rust backend
// const isSpeech = await this.detectSpeech(inputBuffer)
// // The rest of the logic remains the same as your original implementation
// const sampleRateMs = this.config.sampleRate / 1000
// const minSilenceDurationSamples = this.config.minSilenceDurationMs * sampleRateMs
// const speechPadSamples = this.config.speechPadMs * sampleRateMs
// const minSpeechDurationSamples = this.config.minSpeechDurationMs * sampleRateMs
// const maxPrevBuffers = Math.ceil(speechPadSamples / this.config.newBufferSize)
// if (!wasRecording && !isSpeech) {
// if (this.prevBuffers.length >= maxPrevBuffers) {
// this.prevBuffers.shift()
// }
// this.prevBuffers.push(inputBuffer.slice(0))
// return
// }
// const remaining = this.buffer.length - this.bufferPointer
// if (inputBuffer.length >= remaining) {
// this.buffer.set(inputBuffer.subarray(0, remaining), this.bufferPointer)
// this.bufferPointer += remaining
// const overflow = inputBuffer.subarray(remaining)
// this.processSpeechSegment(overflow)
// return
// }
// else {
// this.buffer.set(inputBuffer, this.bufferPointer)
// this.bufferPointer += inputBuffer.length
// }
// if (isSpeech) {
// if (!this.isRecording) {
// this.emit('speech-start', undefined)
// this.emit('status', { type: 'info', message: 'Speech detected' })
// }
// this.isRecording = true
// this.postSpeechSamples = 0
// return
// }
// this.postSpeechSamples += inputBuffer.length
// if (this.postSpeechSamples >= minSilenceDurationSamples) {
// if (this.bufferPointer < minSpeechDurationSamples) {
// this.reset()
// return
// }
// this.processSpeechSegment()
// }
// }
// private async detectSpeech(buffer: Float32Array): Promise<boolean> {
// // Use Rust backend for inference
// const result = await (this.inferenceChain = this.inferenceChain.then(() =>
// invoke('plugin:ipc-audio-vad-ort|ipc_audio_vad', {
// inputData: {
// input: Array.from(buffer),
// sr: this.config.sampleRate,
// state: Array.from(this.state),
// },
// }),
// )) as { output: number[], state: number[] }
// // Update the state
// this.state = new Float32Array(result.state)
// // Get the speech probability
// const speechProb = result.output[0]
// this.emit('debug', {
// message: 'VAD score',
// data: { probability: speechProb },
// })
// // Apply thresholds
// return (
// speechProb > this.config.speechThreshold
// || (this.isRecording && speechProb >= this.config.exitThreshold)
// )
// }
// private processSpeechSegment(overflow?: Float32Array): void {
// const sampleRateMs = this.config.sampleRate / 1000
// const speechPadSamples = this.config.speechPadMs * sampleRateMs
// const duration = (this.bufferPointer / this.config.sampleRate) * 1000
// const overflowLength = overflow?.length ?? 0
// const prevLength = this.prevBuffers.reduce((acc, b) => acc + b.length, 0)
// const finalBuffer = new Float32Array(prevLength + this.bufferPointer + speechPadSamples)
// let offset = 0
// for (const prev of this.prevBuffers) {
// finalBuffer.set(prev, offset)
// offset += prev.length
// }
// finalBuffer.set(this.buffer.slice(0, this.bufferPointer + speechPadSamples), offset)
// this.emit('speech-end', undefined)
// this.emit('speech-ready', {
// buffer: finalBuffer,
// duration,
// })
// if (overflow) {
// this.buffer.set(overflow, 0)
// }
// this.reset(overflowLength)
// }
// private reset(offset: number = 0): void {
// this.buffer.fill(0, offset)
// this.bufferPointer = offset
// this.isRecording = false
// this.postSpeechSamples = 0
// this.prevBuffers = []
// }
// public updateConfig(newConfig: Partial<BaseVADConfig>): void {
// this.config = { ...this.config, ...newConfig }
// if (newConfig.maxBufferDuration || newConfig.sampleRate) {
// this.buffer = new Float32Array(this.config.maxBufferDuration * this.config.sampleRate)
// this.bufferPointer = 0
// }
// }
// public isCurrentlyRecording(): boolean {
// return this.isRecording
// }
// }
// export async function createVAD(config?: Partial<BaseVADConfig>): Promise<VAD> {
// const vad = new VAD(config)
// await vad.initialize()
// return vad
// }
@@ -1,6 +0,0 @@
export enum WindowControlMode {
NONE = 'none',
MOVE = 'move',
RESIZE = 'resize',
DEBUG = 'debug',
}