12 Commits

Author SHA1 Message Date
ayangweb
d9fb19aa2f docs: 更新 QQ 群二维码 (#810) 2026-01-22 11:01:34 +08:00
ayangweb
ef17ba9ebc fix: 修复 JSON 格式错误导致模型不可用的问题 (#790) 2026-01-10 17:51:44 +08:00
ayangweb
d69669bd3a feat: 设置窗口尺寸最大值为 500 (#778) 2026-01-08 17:00:08 +08:00
ayangweb
2fc6ec9d2f fix: 修复切换模型时报 undefined 的问题 (#775) 2026-01-06 22:45:11 +08:00
ayangweb
50803eb2ce perf: 移除 Windows 下拖拽到屏幕边缘的放大效果 (#771) 2026-01-01 17:09:27 +08:00
ayangweb
57892f3ab3 feat: 新增「猫咪设置 > 窗口设置 > 窗口位置」配置项 (#764) 2026-01-01 15:44:07 +08:00
ayang
b59ec09354 v0.9.0 2025-12-24 17:53:23 +08:00
ayangweb
49b5d9954c feat(i18n): 改进英语翻译 (#762) 2025-12-24 17:52:05 +08:00
Suh
f1fac05975 feat: 新增葡萄牙语翻译 (#744)
Co-authored-by: ayang <473033518@qq.com>
2025-12-24 17:26:19 +08:00
BiggerRain
6554508c30 feat: 补全遗漏的国际化内容 (#742) 2025-12-09 14:21:18 +08:00
Thexvoilone
7e7c1aded0 feat: 新增「猫咪设置 > 窗口设置 > 鼠标移入隐藏」配置项 (#727)
Co-authored-by: ayang <473033518@qq.com>
2025-12-01 20:27:24 +08:00
ayangweb
1e24c04da2 docs: update README.md (#729) 2025-11-26 12:21:29 +08:00
27 changed files with 582 additions and 194 deletions

2
Cargo.lock generated
View File

@@ -450,7 +450,7 @@ dependencies = [
[[package]]
name = "bongo-cat"
version = "0.8.2"
version = "0.9.0"
dependencies = [
"fs_extra",
"gilrs",

100
README.md
View File

@@ -2,50 +2,25 @@
<div align="center">
<div>
<a href="https://github.com/ayangweb/BongoCat/releases">
<img
alt="Windows"
src="https://img.shields.io/badge/-Windows-blue?style=flat-square&logo=data:image/svg+xml;base64,PHN2ZyB0PSIxNzI2MzA1OTcxMDA2IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjE1NDgiIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4Ij48cGF0aCBkPSJNNTI3LjI3NTU1MTYxIDk2Ljk3MTAzMDEzdjM3My45OTIxMDY2N2g0OTQuNTEzNjE5NzVWMTUuMDI2NzU3NTN6TTUyNy4yNzU1NTE2MSA5MjguMzIzNTA4MTVsNDk0LjUxMzYxOTc1IDgwLjUyMDI4MDQ5di00NTUuNjc3NDcxNjFoLTQ5NC41MTM2MTk3NXpNNC42NzA0NTEzNiA0NzAuODMzNjgyOTdINDIyLjY3Njg1OTI1VjExMC41NjM2ODE5N2wtNDE4LjAwNjQwNzg5IDY5LjI1Nzc5NzUzek00LjY3MDQ1MTM2IDg0Ni43Njc1OTcwM0w0MjIuNjc2ODU5MjUgOTE0Ljg2MDMxMDEzVjU1My4xNjYzMTcwM0g0LjY3MDQ1MTM2eiIgcC1pZD0iMTU0OSIgZmlsbD0iI2ZmZmZmZiI+PC9wYXRoPjwvc3ZnPg=="
/>
</a>
<a href="https://github.com/ayangweb/BongoCat/releases">
<img
alt="MacOS"
src="https://img.shields.io/badge/-MacOS-black?style=flat-square&logo=apple&logoColor=white"
/>
</a>
<a href="https://github.com/ayangweb/BongoCat/releases">
<img
alt="Linux"
src="https://img.shields.io/badge/-Linux-yellow?style=flat-square&logo=linux&logoColor=white"
/>
</a>
<a href="https://github.com/ayangweb/BongoCat/releases"><img alt="Windows" src="https://img.shields.io/badge/-Windows-blue?style=flat-square&logo=data:image/svg+xml;base64,PHN2ZyB0PSIxNzI2MzA1OTcxMDA2IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjE1NDgiIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4Ij48cGF0aCBkPSJNNTI3LjI3NTU1MTYxIDk2Ljk3MTAzMDEzdjM3My45OTIxMDY2N2g0OTQuNTEzNjE5NzVWMTUuMDI2NzU3NTN6TTUyNy4yNzU1NTE2MSA5MjguMzIzNTA4MTVsNDk0LjUxMzYxOTc1IDgwLjUyMDI4MDQ5di00NTUuNjc3NDcxNjFoLTQ5NC41MTM2MTk3NXpNNC42NzA0NTEzNiA0NzAuODMzNjgyOTdINDIyLjY3Njg1OTI1VjExMC41NjM2ODE5N2wtNDE4LjAwNjQwNzg5IDY5LjI1Nzc5NzUzek00LjY3MDQ1MTM2IDg0Ni43Njc1OTcwM0w0MjIuNjc2ODU5MjUgOTE0Ljg2MDMxMDEzVjU1My4xNjYzMTcwM0g0LjY3MDQ1MTM2eiIgcC1pZD0iMTU0OSIgZmlsbD0iI2ZmZmZmZiI+PC9wYXRoPjwvc3ZnPg==" /></a>
<a href="https://github.com/ayangweb/BongoCat/releases"><img alt="MacOS" src="https://img.shields.io/badge/-MacOS-black?style=flat-square&logo=apple&logoColor=white" /></a>
<a href="https://github.com/ayangweb/BongoCat/releases"><img alt="Linux" src="https://img.shields.io/badge/-Linux-yellow?style=flat-square&logo=linux&logoColor=white" /></a>
</div>
<p>
<a href="./LICENSE">
<img
src="https://img.shields.io/github/license/ayangweb/BongoCat?style=flat-square"
/>
</a>
<a href="https://github.com/ayangweb/BongoCat/releases/latest">
<img
src="https://img.shields.io/github/package-json/v/ayangweb/BongoCat?style=flat-square"
/>
</a>
<a href="https://github.com/ayangweb/BongoCat/releases">
<img
src="https://img.shields.io/github/downloads/ayangweb/BongoCat/total?style=flat-square"
/>
</a>
<a href="./LICENSE"><img src="https://img.shields.io/github/license/ayangweb/BongoCat?style=flat-square" /></a>
<a href="https://github.com/ayangweb/BongoCat/releases/latest"><img src="https://img.shields.io/github/package-json/v/ayangweb/BongoCat?style=flat-square"/></a>
<a href="https://github.com/ayangweb/BongoCat/releases"><img src="https://img.shields.io/github/downloads/ayangweb/BongoCat/total?style=flat-square"/></a>
</p>
<p>
<a href="https://trendshift.io/developers/8507" target="_blank"><img src="https://trendshift.io/api/badge/developers/8507" alt="ayangweb | Trendshift" width="250" height="55" /></a>
<a href="https://trendshift.io/repositories/14605" target="_blank"><img src="https://trendshift.io/api/badge/repositories/14605" alt="ayangweb%2FBongoCat | Trendshift" width="250" height="55" /></a>
<a href="https://hellogithub.com/repository/7d23863fd4be47b39e816193ded385c9" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=7d23863fd4be47b39e816193ded385c9&claim_uid=5ihRVIuTYBmSGtQ&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=7d23863fd4be47b39e816193ded385c9&claim_uid=5ihRVIuTYBmSGtQ&theme=neutral" />
<img alt="Star History Chart" src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=7d23863fd4be47b39e816193ded385c9&claim_uid=5ihRVIuTYBmSGtQ&theme=neutral" />
<img alt="Star History Chart" src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=7d23863fd4be47b39e816193ded385c9&claim_uid=5ihRVIuTYBmSGtQ&theme=neutral" width="250" height="55" />
</picture>
</a>
</p>
@@ -96,13 +71,56 @@
## 社区交流
<a href="https://qm.qq.com/q/AS3gNv2Vzy">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://i0.hdslb.com/bfs/openplatform/5ad8e4278c525cca6d3b4426c30b6d299d8a9654.png" />
<source media="(prefers-color-scheme: light)" srcset="https://i0.hdslb.com/bfs/openplatform/599680ad67bc9f9f876f76069c2239e9a85bb54d.png" />
<img alt="QQ Group" src="https://i0.hdslb.com/bfs/openplatform/599680ad67bc9f9f876f76069c2239e9a85bb54d.png" height="250" />
</picture>
</a>
<table>
<thead>
<tr>
<th>QQ 群 1</th>
<th>QQ 群 2</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<a href="https://qm.qq.com/q/AS3gNv2Vzy">
<picture>
<source
media="(prefers-color-scheme: dark)"
srcset="https://i0.hdslb.com/bfs/openplatform/8ecdc4982ab01b59d7731fcca3ec26631a274560.png"
/>
<source
media="(prefers-color-scheme: light)"
srcset="https://i0.hdslb.com/bfs/openplatform/09f56580397063e1819c4c2ed63d07dee12720e1.png"
/>
<img
alt="QQ Group 1"
src="https://i0.hdslb.com/bfs/openplatform/09f56580397063e1819c4c2ed63d07dee12720e1.png"
height="250"
/>
</picture>
</a>
</td>
<td>
<a href="https://qm.qq.com/q/TmltLAod2O">
<picture>
<source
media="(prefers-color-scheme: dark)"
srcset="https://i0.hdslb.com/bfs/openplatform/473c522487ff33e0f32b15466aeb0734f17161c8.png"
/>
<source
media="(prefers-color-scheme: light)"
srcset="https://i0.hdslb.com/bfs/openplatform/d5ae8c5af6ae1d0a1f066705ee822d1287384cf6.png"
/>
<img
alt="QQ Group 2"
src="https://i0.hdslb.com/bfs/openplatform/d5ae8c5af6ae1d0a1f066705ee822d1287384cf6.png"
height="250"
/>
</picture>
</a>
</td>
</tr>
</tbody>
</table>
## 赞赏

View File

@@ -1,7 +1,7 @@
{
"name": "bongo-cat",
"type": "module",
"version": "0.8.2",
"version": "0.9.0",
"private": true,
"author": {
"name": "ayangweb",
@@ -39,6 +39,7 @@
"dayjs": "^1.11.13",
"es-toolkit": "^1.38.0",
"is-url": "^1.2.4",
"json5": "^2.2.3",
"nanoid": "^5.1.5",
"pinia": "^3.0.3",
"pixi-live2d-display": "^0.4.0",

10
pnpm-lock.yaml generated
View File

@@ -62,6 +62,9 @@ importers:
is-url:
specifier: ^1.2.4
version: 1.2.4
json5:
specifier: ^2.2.3
version: 2.2.3
nanoid:
specifier: ^5.1.5
version: 5.1.5
@@ -3227,6 +3230,11 @@ packages:
json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
json5@2.2.3:
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
engines: {node: '>=6'}
hasBin: true
jsonc-eslint-parser@2.4.0:
resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -8031,6 +8039,8 @@ snapshots:
json-stable-stringify-without-jsonify@1.0.1: {}
json5@2.2.3: {}
jsonc-eslint-parser@2.4.0:
dependencies:
acorn: 8.14.1

View File

@@ -1,6 +1,6 @@
[package]
name = "bongo-cat"
version = "0.8.2"
version = "0.9.0"
description = "A Tauri App"
authors = [ "ayangweb" ]
edition = "2024"

View File

@@ -57,7 +57,7 @@ pub fn platform(
"window_did_resize" => {
window_move_event();
if let Ok(size) = main_window.inner_size() {
if let Ok(size) = main_window.outer_size() {
let _ = main_window.emit_to(target, WINDOW_RESIZED_EVENT, size);
}
}

View File

@@ -21,7 +21,8 @@
"transparent": true,
"decorations": false,
"acceptFirstMouse": true,
"skipTaskbar": true
"skipTaskbar": true,
"maximizable": false
},
{
"label": "preference",

View File

@@ -7,11 +7,12 @@
"languages": [
"English",
"Vietnamese",
"SimpChinese"
"SimpChinese",
"PortugueseBR"
],
"installMode": "both",
"displayLanguageSelector": true
}
}
}
}
}

View File

@@ -3,6 +3,10 @@ html {
color-scheme: light;
body {
--uno: transition-opacity-300;
}
&.dark {
color-scheme: dark;
}

View File

@@ -53,7 +53,7 @@ useTauriListen<boolean>(LISTEN_KEY.UPDATE_APP, () => {
message.loading({
key: MESSAGE_KEY,
duration: 0,
content: t('components.proShortcut.updateApp.hints.checkingUpdates'),
content: t('components.updateApp.hints.checkingUpdates'),
})
})
@@ -93,7 +93,7 @@ async function checkUpdate(visibleMessage = false) {
message.destroy(MESSAGE_KEY)
} else if (visibleMessage) {
message.success({ key: MESSAGE_KEY, content: t('components.proShortcut.updateApp.hints.alreadyLatest') })
message.success({ key: MESSAGE_KEY, content: t('components.updateApp.hints.alreadyLatest') })
}
} catch (error) {
if (!visibleMessage) return
@@ -140,15 +140,15 @@ async function handleOk() {
<template>
<Modal
v-model:open="state.open"
:cancel-text="$t('components.proShortcut.updateApp.buttons.updateLater')"
:cancel-text="$t('components.updateApp.buttons.updateLater')"
centered
:closable="false"
:mask-closable="false"
:title="$t('components.proShortcut.updateApp.title')"
:title="$t('components.updateApp.title')"
@ok="handleOk"
>
<template #okText>
{{ state.downloading ? downloadProgress : $t('components.proShortcut.updateApp.buttons.updateNow') }}
{{ state.downloading ? downloadProgress : $t('components.updateApp.buttons.updateNow') }}
</template>
<Flex
@@ -157,7 +157,7 @@ async function handleOk() {
vertical
>
<Flex align="center">
<span>更新版本</span>
<span>{{ $t('components.updateApp.labels.updateVersion') }}</span>
<span>
<span>{{ state.update?.currentVersion }} 👉 </span>
<a
@@ -169,12 +169,12 @@ async function handleOk() {
</Flex>
<Flex align="center">
<span>更新时间</span>
<span>{{ $t('components.updateApp.labels.updateTime') }}</span>
<span>{{ state.update?.date }}</span>
</Flex>
<Flex vertical>
<span>更新日志</span>
<span>{{ $t('components.updateApp.labels.changelog') }}</span>
<VueMarkdown
class="update-note max-h-40 overflow-auto"

View File

@@ -1,8 +1,6 @@
import type { CursorPoint } from '@/utils/monitor'
import { invoke } from '@tauri-apps/api/core'
import { isEqual, mapValues } from 'es-toolkit'
import { ref } from 'vue'
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'
import { cursorPosition } from '@tauri-apps/api/window'
import { INVOKE_KEY, LISTEN_KEY } from '../constants'
@@ -11,6 +9,7 @@ import { useTauriListen } from './useTauriListen'
import { useCatStore } from '@/stores/cat'
import { useModelStore } from '@/stores/model'
import { inBetween } from '@/utils/is'
import { isWindows } from '@/utils/platform'
interface MouseButtonEvent {
@@ -18,6 +17,11 @@ interface MouseButtonEvent {
value: string
}
export interface CursorPoint {
x: number
y: number
}
interface MouseMoveEvent {
kind: 'MouseMove'
value: CursorPoint
@@ -32,7 +36,6 @@ type DeviceEvent = MouseButtonEvent | MouseMoveEvent | KeyboardEvent
export function useDevice() {
const modelStore = useModelStore()
const lastCursorPoint = ref<CursorPoint>({ x: 0, y: 0 })
const releaseTimers = new Map<string, NodeJS.Timeout>()
const catStore = useCatStore()
const { handlePress, handleRelease, handleMouseChange, handleMouseMove } = useModel()
@@ -60,14 +63,25 @@ export function useDevice() {
return nextKey
}
const processMouseMove = (point: CursorPoint) => {
const roundedValue = mapValues(point, Math.round)
const handleCursorMove = async () => {
const cursorPoint = await cursorPosition()
if (isEqual(lastCursorPoint.value, roundedValue)) return
handleMouseMove(cursorPoint)
lastCursorPoint.value = roundedValue
if (catStore.window.hideOnHover) {
const appWindow = getCurrentWebviewWindow()
const position = await appWindow.outerPosition()
const { width, height } = await appWindow.innerSize()
return handleMouseMove(point)
const isInWindow = inBetween(cursorPoint.x, position.x, position.x + width)
&& inBetween(cursorPoint.y, position.y, position.y + height)
document.body.style.setProperty('opacity', isInWindow ? '0' : 'unset')
if (!catStore.window.passThrough) {
appWindow.setIgnoreCursorEvents(isInWindow)
}
}
}
const handleAutoRelease = (key: string, delay = 100) => {
@@ -117,7 +131,7 @@ export function useDevice() {
case 'MouseRelease':
return handleMouseChange(value, false)
case 'MouseMove':
return processMouseMove(value)
return handleCursorMove()
}
})

View File

@@ -1,4 +1,4 @@
import type { CursorPoint } from '@/utils/monitor'
import type { PhysicalPosition } from '@tauri-apps/api/dpi'
import { LogicalSize } from '@tauri-apps/api/dpi'
import { resolveResource, sep } from '@tauri-apps/api/path'
@@ -107,12 +107,12 @@ export function useModel() {
live2d.setParameterValue(id, pressed)
}
async function handleMouseMove(point: CursorPoint) {
const monitor = await getCursorMonitor(point)
async function handleMouseMove(cursorPoint: PhysicalPosition) {
const monitor = await getCursorMonitor(cursorPoint)
if (!monitor) return
const { size, position, cursorPoint } = monitor
const { size, position } = monitor
const xRatio = (cursorPoint.x - position.x) / size.width
const yRatio = (cursorPoint.y - position.y) / size.height

View File

@@ -0,0 +1,47 @@
import { PhysicalPosition } from '@tauri-apps/api/dpi'
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'
import { onMounted, ref, watch } from 'vue'
import { useCatStore } from '@/stores/cat'
import { getCursorMonitor } from '@/utils/monitor'
const appWindow = getCurrentWebviewWindow()
export function useWindowPosition() {
const catStore = useCatStore()
const isMounted = ref(false)
const setWindowPosition = async () => {
const monitor = await getCursorMonitor()
if (!monitor) return
const windowSize = await appWindow.outerSize()
switch (catStore.window.position) {
case 'topLeft':
return appWindow.setPosition(new PhysicalPosition(0, 0))
case 'topRight':
return appWindow.setPosition(new PhysicalPosition(monitor.size.width - windowSize.width, 0))
case 'bottomLeft':
return appWindow.setPosition(new PhysicalPosition(0, monitor.size.height - windowSize.height))
default:
return appWindow.setPosition(new PhysicalPosition(monitor.size.width - windowSize.width, monitor.size.height - windowSize.height))
}
}
onMounted(async () => {
await setWindowPosition()
isMounted.value = true
appWindow.onScaleChanged(setWindowPosition)
})
watch(() => catStore.window.position, setWindowPosition)
return {
isMounted,
setWindowPosition,
}
}

View File

@@ -21,4 +21,5 @@ export const LANGUAGE = {
ZH_CN: 'zh-CN',
EN_US: 'en-US',
VI_VN: 'vi-VN',
PT_BR: 'pt-BR',
} as const

View File

@@ -2,7 +2,7 @@
"pages": {
"main": {
"hints": {
"redrawing": "Resizing..."
"redrawing": "Redrawing..."
}
},
"preference": {
@@ -12,38 +12,48 @@
"labels": {
"modelSettings": "Model Settings",
"mirrorMode": "Mirror Mode",
"singleMode": "Show Last Key Only",
"singleMode": "Single Key Mode",
"mouseMirror": "Mouse Mirror",
"windowSettings": "Window Settings",
"passThrough": "Window Penetration",
"alwaysOnTop": "Always On Top",
"passThrough": "Pass Through",
"alwaysOnTop": "Always on Top",
"windowSize": "Window Size",
"windowRadius": "Window Radius",
"opacity": "Opacity",
"autoReleaseDelay": "Auto Release Delay"
"autoReleaseDelay": "Auto Release Delay",
"hideOnHover": "Hide on Hover",
"position": "Window Position"
},
"hints": {
"mirrorMode": "When enabled, the model will be horizontally flipped.",
"singleMode": "When enabled, only the last key pressed on each hand is displayed (prevents showing multiple hands when pressing multiple keys at once).",
"mouseMirror": "When enabled, the mouse will mirror follow the hand movement.",
"passThrough": "When enabled, the window will not affect operations on other applications.",
"alwaysOnTop": "When enabled, the window will always stay above other applications.",
"windowSize": "Move the mouse to the edge of the window or hold Shift and right-drag to resize.",
"autoReleaseDelay": "Due to Windows not capturing the release events of certain system-level keys, they will be automatically treated as released after a timeout."
"mirrorMode": "When enabled, the model will be mirrored horizontally.",
"singleMode": "When enabled, only the last pressed key is displayed for each hand.",
"mouseMirror": "When enabled, the mouse will mirror the hand movement.",
"passThrough": "When enabled, clicks pass through the window without affecting it.",
"alwaysOnTop": "When enabled, the window stays above all other windows.",
"windowSize": "Move mouse to window edge, or hold Shift and right-drag to resize.",
"autoReleaseDelay": "On Windows, some system keys cannot capture release events and will auto-release after timeout.",
"hideOnHover": "When enabled, the window hides when mouse hovers over it.",
"position": "Takes effect after the app starts, or when this parameter, window size, model, or screen resolution changes."
},
"options": {
"topLeft": "Top Left",
"topRight": "Top Right",
"bottomLeft": "Bottom Left",
"bottomRight": "Bottom Right"
}
},
"general": {
"title": "General",
"labels": {
"appSettings": "Application settings",
"launchOnStartup": "Launch at startup",
"showTaskbarIcon": "Show taskbar icon",
"appearanceSettings": "Appearance settings",
"themeMode": "Theme",
"language": "Languages",
"updateSettings": "Update settings",
"autoCheckUpdate": "Check for updates automatically",
"permissionsSettings": "Permission Settings",
"appSettings": "Application Settings",
"launchOnStartup": "Launch on Startup",
"showTaskbarIcon": "Show Taskbar Icon",
"appearanceSettings": "Appearance Settings",
"themeMode": "Theme Mode",
"language": "Language",
"updateSettings": "Update Settings",
"autoCheckUpdate": "Auto Check for Updates",
"permissionsSettings": "Permissions Settings",
"inputMonitoringPermission": "Input Monitoring Permission"
},
"options": {
@@ -52,9 +62,9 @@
"darkMode": "Dark"
},
"hints": {
"showTaskbarIcon": "Once enabled, you can capture the window via OBS Studio.",
"inputMonitoringPermission": "Enable input monitoring permission to receive system keyboard and mouse events to respond to your actions.",
"inputMonitoringPermissionGuide": "If the permission is already enabled, first select it and click the \"-\" button to remove it. Then add it again manually and restart the application to ensure the permission takes effect."
"showTaskbarIcon": "When enabled, the window can be captured via OBS Studio.",
"inputMonitoringPermission": "Enable input monitoring to receive keyboard and mouse events from the system.",
"inputMonitoringPermissionGuide": "If the permission is already enabled, select it and click the \"-\" button to remove it, then manually add it again and restart the app."
},
"status": {
"authorized": "Authorized",
@@ -68,34 +78,34 @@
"model": {
"title": "Model",
"labels": {
"deleteModel": "Delete model"
"deleteModel": "Delete Model"
},
"hints": {
"deleteSuccess": "Deleted successfully",
"deleteSuccess": "Deleted Successfully",
"deleteModel": "Are you sure you want to delete this model?",
"importSuccess": "Import successful",
"clickOrDragToImport": "Click or drag to import"
"importSuccess": "Imported Successfully",
"clickOrDragToImport": "Click or drag here to import"
},
"tooltips": {
"createModel": "Create model",
"convertModel": "Convert model",
"moreModels": "More models"
"createModel": "Create Model",
"convertModel": "Convert Model",
"moreModels": "More Models"
}
},
"shortcut": {
"title": "Shortcuts",
"labels": {
"toggleCat": "Show/Hide Cat",
"togglePreferences": "Open Preferences",
"toggleCat": "Toggle Cat",
"togglePreferences": "Toggle Preferences",
"mirrorMode": "Mirror Mode",
"passThrough": "Window Penetration",
"alwaysOnTop": "Always On Top"
"passThrough": "Pass Through",
"alwaysOnTop": "Always on Top"
},
"hints": {
"toggleCat": "Toggle the cat window's visibility.",
"togglePreferences": "Toggle the preferences window visibility.",
"toggleCat": "Toggle the visibility of the cat window.",
"togglePreferences": "Toggle the visibility of the preferences window.",
"mirrorMode": "Toggle the cat's mirror mode.",
"passThrough": "Toggle whether the cat window is click-through.",
"passThrough": "Toggle whether the cat window is pass-through.",
"alwaysOnTop": "Toggle whether the cat window stays on top."
}
},
@@ -108,13 +118,13 @@
"openSource": "Open Source"
},
"hints": {
"appInfo": "Copy app information to include in bug reports.",
"copySuccess": "Copied successfully"
"appInfo": "Copy app information and provide it to bug issue.",
"copySuccess": "Copied Successfully"
},
"buttons": {
"checkUpdate": "Check for updates",
"checkUpdate": "Check for Updates",
"copy": "Copy",
"feedbackIssues": "Report Issue",
"feedbackIssues": "Feedback Issues",
"viewLog": "View Logs"
}
}
@@ -123,24 +133,24 @@
"components": {
"proShortcut": {
"hints": {
"pressRecordShortcut": "Press keys to record shortcut",
"pressRecordShortcut": "Press to record shortcut",
"clickRecordShortcut": "Click to record shortcut"
}
},
"updateApp": {
"title": "New Version Found 🥳",
"labels": {
"updateVersion": "Update Version: ",
"updateTime": "Update Time: ",
"changelog": "Changelog: "
},
"updateApp": {
"title": "New version found 🥳",
"labels": {
"updateVersion": "Version: ",
"updateTime": "Update time: ",
"changelog": "Changelog: "
},
"hints": {
"checkingUpdates": "Checking for updates...",
"alreadyLatest": "You are already on the latest version 🎉"
},
"buttons": {
"updateNow": "Update Now",
"updateLater": "Update later"
}
"hints": {
"checkingUpdates": "Checking for updates...",
"alreadyLatest": "Already on the latest version 🎉"
},
"buttons": {
"updateNow": "Update Now",
"updateLater": "Update Later"
}
}
},
@@ -150,16 +160,23 @@
"preference": "Preferences...",
"hideCat": "Hide Cat",
"showCat": "Show Cat",
"passThrough": "Window Penetration",
"passThrough": "Pass Through",
"windowSize": "Window Size",
"opacity": "Opacity"
}
},
"useTray": {
"checkUpdate": "Check for updates",
"openSource": "Source Code",
"restartApp": "Restart",
"quitApp": "Exit"
"checkUpdate": "Check for Updates",
"openSource": "Open Source",
"restartApp": "Restart App",
"quitApp": "Quit App"
}
},
"utils": {
"live2d": {
"hints": {
"notFound": "Model master configuration file not found, please ensure the model files are complete."
}
}
}
}

View File

@@ -2,11 +2,13 @@ import type { Language } from '@/stores/general'
import type { Locale as AntdLocale } from 'ant-design-vue/es/locale'
import antdEnUS from 'ant-design-vue/locale/en_US'
import antdPtBR from 'ant-design-vue/locale/pt_BR'
import antdViVN from 'ant-design-vue/locale/vi_VN'
import antdZhCN from 'ant-design-vue/locale/zh_CN'
import { createI18n } from 'vue-i18n'
import enUS from './en-US.json'
import ptBR from './pt-BR.json'
import viVN from './vi-VN.json'
import zhCN from './zh-CN.json'
@@ -20,6 +22,7 @@ export const i18n = createI18n({
[LANGUAGE.ZH_CN]: zhCN,
[LANGUAGE.EN_US]: enUS,
[LANGUAGE.VI_VN]: viVN,
[LANGUAGE.PT_BR]: ptBR,
},
})
@@ -28,6 +31,7 @@ export function getAntdLocale(language: Language = LANGUAGE.EN_US) {
[LANGUAGE.ZH_CN]: antdZhCN,
[LANGUAGE.EN_US]: antdEnUS,
[LANGUAGE.VI_VN]: antdViVN,
[LANGUAGE.PT_BR]: antdPtBR,
}
return antdLanguage[language]

182
src/locales/pt-BR.json Normal file
View File

@@ -0,0 +1,182 @@
{
"pages": {
"main": {
"hints": {
"redrawing": "Redimensionando..."
}
},
"preference": {
"title": "Preferências",
"cat": {
"title": "Gato",
"labels": {
"modelSettings": "Configurações do Modelo",
"mirrorMode": "Modo Espelho",
"singleMode": "Mostrar Apenas Última Tecla",
"mouseMirror": "Espelho do Mouse",
"windowSettings": "Configurações da Janela",
"passThrough": "Janela Transparente",
"alwaysOnTop": "Sempre no Topo",
"windowSize": "Tamanho da Janela",
"windowRadius": "Raio da Janela",
"opacity": "Opacidade",
"autoReleaseDelay": "Atraso de Liberação Automática",
"hideOnHover": "Ocultar ao Passar o Mouse",
"position": "Posição da Janela"
},
"hints": {
"mirrorMode": "Quando ativado, o modelo será invertido horizontalmente.",
"singleMode": "Quando ativado, apenas a última tecla pressionada em cada mão é exibida (evita mostrar múltiplas mãos ao pressionar várias teclas ao mesmo tempo).",
"mouseMirror": "Quando ativado, o mouse espelhará o movimento da mão.",
"passThrough": "Quando ativado, a janela não afetará operações em outros aplicativos.",
"alwaysOnTop": "Quando ativado, a janela sempre ficará acima de outros aplicativos.",
"windowSize": "Mova o mouse para a borda da janela ou segure Shift e arraste com o botão direito para redimensionar.",
"autoReleaseDelay": "Devido ao Windows não capturar eventos de liberação de certas teclas de nível do sistema, elas serão automaticamente tratadas como liberadas após um tempo limite.",
"hideOnHover": "Quando ativado, a janela será ocultada quando o mouse passar sobre ela.",
"position": "Entra em vigor após inicializar o aplicativo ou quando este parâmetro, o tamanho da janela, o modelo ou a resolução de tela é alterado."
},
"options": {
"topLeft": "Canto Superior Esquerdo",
"topRight": "Canto Superior Direito",
"bottomLeft": "Canto Inferior Esquerdo",
"bottomRight": "Canto Inferior Direito"
}
},
"general": {
"title": "Geral",
"labels": {
"appSettings": "Configurações do aplicativo",
"launchOnStartup": "Iniciar na inicialização",
"showTaskbarIcon": "Mostrar ícone na barra de tarefas",
"appearanceSettings": "Configurações de aparência",
"themeMode": "Tema",
"language": "Idiomas",
"updateSettings": "Configurações de atualização",
"autoCheckUpdate": "Verificar atualizações automaticamente",
"permissionsSettings": "Configurações de Permissões",
"inputMonitoringPermission": "Permissão de Monitoramento de Entrada"
},
"options": {
"auto": "Sistema",
"lightMode": "Claro",
"darkMode": "Escuro"
},
"hints": {
"showTaskbarIcon": "Uma vez ativado, você pode capturar a janela via OBS Studio.",
"inputMonitoringPermission": "Ative a permissão de monitoramento de entrada para receber eventos de teclado e mouse do sistema para responder às suas ações.",
"inputMonitoringPermissionGuide": "Se a permissão já estiver ativada, primeiro selecione-a e clique no botão \"-\" para removê-la. Em seguida, adicione-a novamente manualmente e reinicie o aplicativo para garantir que a permissão entre em vigor."
},
"status": {
"authorized": "Autorizado",
"authorize": "Ir para Ativar"
},
"buttons": {
"openNow": "Abrir Agora",
"openLater": "Abrir Mais Tarde"
}
},
"model": {
"title": "Modelo",
"labels": {
"deleteModel": "Excluir modelo"
},
"hints": {
"deleteSuccess": "Excluído com sucesso",
"deleteModel": "Tem certeza de que deseja excluir este modelo?",
"importSuccess": "Importação bem-sucedida",
"clickOrDragToImport": "Clique ou arraste para importar"
},
"tooltips": {
"createModel": "Criar modelo",
"convertModel": "Converter modelo",
"moreModels": "Mais modelos"
}
},
"shortcut": {
"title": "Atalhos",
"labels": {
"toggleCat": "Mostrar/Ocultar Gato",
"togglePreferences": "Abrir Preferências",
"mirrorMode": "Modo Espelho",
"passThrough": "Janela Transparente",
"alwaysOnTop": "Sempre no Topo"
},
"hints": {
"toggleCat": "Alternar a visibilidade da janela do gato.",
"togglePreferences": "Alternar a visibilidade da janela de preferências.",
"mirrorMode": "Alternar o modo espelho do gato.",
"passThrough": "Alternar se a janela do gato é clicável.",
"alwaysOnTop": "Alternar se a janela do gato permanece no topo."
}
},
"about": {
"title": "Sobre",
"labels": {
"aboutApp": "Sobre o Aplicativo",
"appLog": "Logs do Aplicativo",
"appInfo": "Informações do Aplicativo",
"openSource": "Código Aberto"
},
"hints": {
"appInfo": "Copiar informações do aplicativo para incluir em relatórios de bugs.",
"copySuccess": "Copiado com sucesso"
},
"buttons": {
"checkUpdate": "Verificar atualizações",
"copy": "Copiar",
"feedbackIssues": "Reportar Problema",
"viewLog": "Ver Logs"
}
}
}
},
"components": {
"proShortcut": {
"hints": {
"pressRecordShortcut": "Pressione as teclas para gravar atalho",
"clickRecordShortcut": "Clique para gravar atalho"
}
},
"updateApp": {
"title": "Nova versão encontrada 🥳",
"labels": {
"updateVersion": "Versão: ",
"updateTime": "Hora da atualização: ",
"changelog": "Registro de alterações: "
},
"hints": {
"checkingUpdates": "Verificando atualizações...",
"alreadyLatest": "Você já está na versão mais recente 🎉"
},
"buttons": {
"updateNow": "Atualizar Agora",
"updateLater": "Atualizar mais tarde"
}
}
},
"composables": {
"useSharedMenu": {
"labels": {
"preference": "Preferências...",
"hideCat": "Ocultar Gato",
"showCat": "Mostrar Gato",
"passThrough": "Janela Transparente",
"windowSize": "Tamanho da Janela",
"opacity": "Opacidade"
}
},
"useTray": {
"checkUpdate": "Verificar atualizações",
"openSource": "Código Fonte",
"restartApp": "Reiniciar",
"quitApp": "Sair"
}
},
"utils": {
"live2d": {
"hints": {
"notFound": "Arquivo de configuração principal do modelo não encontrado. Verifique se os arquivos do modelo estão completos."
}
}
}
}

View File

@@ -20,7 +20,9 @@
"windowSize": "Kích thước",
"windowRadius": "Độ bo tròn cửa sổ",
"opacity": "Độ mờ",
"autoReleaseDelay": "Độ trễ tự động nhả phím"
"autoReleaseDelay": "Độ trễ tự động nhả phím",
"hideOnHover": "Ẩn khi di chuột",
"position": "Vị trí cửa sổ"
},
"hints": {
"mirrorMode": "Bật để lật ngang mô hình.",
@@ -29,7 +31,15 @@
"passThrough": "Bật để cửa sổ không ảnh hưởng đến thao tác trên ứng dụng khác.",
"alwaysOnTop": "Bật để cửa sổ luôn nằm trên ứng dụng khác.",
"windowSize": "Di chuyển chuột đến mép cửa sổ hoặc giữ Shift và kéo chuột phải để thay đổi kích thước.",
"autoReleaseDelay": "Do Windows không bắt được sự kiện nhả của một số phím hệ thống, các phím đó sẽ được tự động xem như đã nhả sau khi hết thời gian chờ."
"autoReleaseDelay": "Do Windows không bắt được sự kiện nhả của một số phím hệ thống, các phím đó sẽ được tự động xem như đã nhả sau khi hết thời gian chờ.",
"hideOnHover": "Khi bật, cửa sổ sẽ ẩn khi chuột di chuyển vào.",
"position": "Có hiệu lực sau khi khởi động ứng dụng hoặc khi tham số này, kích thước cửa sổ, mô hình hoặc độ phân giải màn hình thay đổi."
},
"options": {
"topLeft": "Góc trên cùng bên trái",
"topRight": "Góc trên cùng bên phải",
"bottomLeft": "Góc dưới cùng bên trái",
"bottomRight": "Góc dưới cùng bên phải"
}
},
"general": {
@@ -125,22 +135,22 @@
"hints": {
"pressRecordShortcut": "Nhấn phím/tổ hợp phím để ghi",
"clickRecordShortcut": "Click để ghi phím tắt"
}
},
"updateApp": {
"title": "Đã tìm thấy phiên bản mới 🥳",
"labels": {
"updateVersion": "Phiên bản: ",
"updateTime": "Thời gian cập nhật: ",
"changelog": "Nhật ký thay đổi: "
},
"updateApp": {
"title": "Đã tìm thấy phiên bản mới 🥳",
"labels": {
"updateVersion": "Phiên bản: ",
"updateTime": "Thời gian cập nhật: ",
"changelog": "Nhật ký thay đổi: "
},
"hints": {
"checkingUpdates": "Đang kiểm tra cập nhật...",
"alreadyLatest": "Bạn đang dùng phiên bản mới nhất 🎉"
},
"buttons": {
"updateNow": "Cập nhật ngay",
"updateLater": "Để sau"
}
"hints": {
"checkingUpdates": "Đang kiểm tra cập nhật...",
"alreadyLatest": "Bạn đang dùng phiên bản mới nhất 🎉"
},
"buttons": {
"updateNow": "Cập nhật ngay",
"updateLater": "Để sau"
}
}
},
@@ -161,5 +171,12 @@
"restartApp": "Khởi động lại",
"quitApp": "Thoát"
}
},
"utils": {
"live2d": {
"hints": {
"notFound": "Không tìm thấy tệp cấu hình chính của mô hình, vui lòng xác nhận các tệp mô hình có đầy đủ không."
}
}
}
}

View File

@@ -20,7 +20,9 @@
"windowSize": "窗口尺寸",
"windowRadius": "窗口圆角",
"opacity": "不透明度",
"autoReleaseDelay": "按键自动释放延迟"
"autoReleaseDelay": "按键自动释放延迟",
"hideOnHover": "鼠标移入隐藏",
"position": "窗口位置"
},
"hints": {
"mirrorMode": "启用后,模型将水平镜像翻转。",
@@ -29,7 +31,15 @@
"passThrough": "启用后,窗口不影响对其他应用程序的操作。",
"alwaysOnTop": "启用后,窗口始终显示在其他应用程序上方。",
"windowSize": "将鼠标移至窗口边缘,或按住 Shift 并右键拖动,也可以调整窗口大小。",
"autoReleaseDelay": "由于 Windows 下部分系统级按键无法捕获释放事件,超时后将自动视为已释放。"
"autoReleaseDelay": "由于 Windows 下部分系统级按键无法捕获释放事件,超时后将自动视为已释放。",
"hideOnHover": "启用后,鼠标悬停在窗口上时,窗口会隐藏。",
"position": "应用启动后,或当此参数、窗口尺寸、模型、电脑分辨率发生变化时生效。"
},
"options": {
"topLeft": "左上角",
"topRight": "右上角",
"bottomLeft": "左下角",
"bottomRight": "右下角"
}
},
"general": {
@@ -125,22 +135,22 @@
"hints": {
"pressRecordShortcut": "按下录制快捷键",
"clickRecordShortcut": "点击录制快捷键"
}
},
"updateApp": {
"title": "发现新版本🥳",
"labels": {
"updateVersion": "更新版本:",
"updateTime": "更新时间:",
"changelog": "更新日志:"
},
"updateApp": {
"title": "发现新版本🥳",
"labels": {
"updateVersion": "更新版本:",
"updateTime": "更新时间:",
"changelog": "更新日志:"
},
"hints": {
"checkingUpdates": "正在检查更新...",
"alreadyLatest": "当前已是最新版本🎉"
},
"buttons": {
"updateNow": "立即更新",
"updateLater": "稍后更新"
}
"hints": {
"checkingUpdates": "正在检查更新...",
"alreadyLatest": "当前已是最新版本🎉"
},
"buttons": {
"updateNow": "立即更新",
"updateLater": "稍后更新"
}
}
},
@@ -161,5 +171,12 @@
"restartApp": "重启应用",
"quitApp": "退出应用"
}
},
"utils": {
"live2d": {
"hints": {
"notFound": "未找到模型主配置文件,请确认模型文件是否完整。"
}
}
}
}

View File

@@ -14,6 +14,7 @@ import { useDevice } from '@/composables/useDevice'
import { useGamepad } from '@/composables/useGamepad'
import { useModel } from '@/composables/useModel'
import { useSharedMenu } from '@/composables/useSharedMenu'
import { useWindowPosition } from '@/composables/useWindowPosition'
import { hideWindow, setAlwaysOnTop, setTaskbarVisibility, showWindow } from '@/plugins/window'
import { useCatStore } from '@/stores/cat'
import { useGeneralStore } from '@/stores/general.ts'
@@ -32,6 +33,7 @@ const generalStore = useGeneralStore()
const resizing = ref(false)
const backgroundImagePath = ref<string>()
const { stickActive } = useGamepad()
const { isMounted, setWindowPosition } = useWindowPosition()
onMounted(startListening)
@@ -40,6 +42,8 @@ onUnmounted(handleDestroy)
const debouncedResize = useDebounceFn(async () => {
await handleResize()
await setWindowPosition()
resizing.value = false
}, 100)
@@ -52,7 +56,7 @@ useEventListener('resize', () => {
watch(() => modelStore.currentModel, async (model) => {
if (!model) return
handleLoad()
await handleLoad()
const path = join(model.path, 'resources', 'background.png')
@@ -76,6 +80,8 @@ watch(() => modelStore.currentModel, async (model) => {
modelStore.supportKeys[fileName] = join(groupDir, file.name)
}
}
setWindowPosition()
}, { deep: true, immediate: true })
watch([() => catStore.window.scale, modelSize], async ([scale, modelSize]) => {
@@ -145,6 +151,7 @@ function handleMouseMove(event: MouseEvent) {
<template>
<div
v-show="isMounted"
class="relative size-screen overflow-hidden children:(absolute size-full)"
:class="{ '-scale-x-100': catStore.model.mirror }"
:style="{
@@ -174,7 +181,7 @@ function handleMouseMove(event: MouseEvent) {
v-show="resizing"
class="flex items-center justify-center bg-black"
>
<span class="text-center text-10vw text-white">
<span class="text-center text-[10vw] text-white">
{{ $t('pages.main.hints.redrawing') }}
</span>
</div>

View File

@@ -0,0 +1,30 @@
<script setup lang="ts">
import { Select, SelectOption } from 'ant-design-vue'
import ProListItem from '@/components/pro-list-item/index.vue'
import { useCatStore } from '@/stores/cat'
const catStore = useCatStore()
</script>
<template>
<ProListItem
:description="$t('pages.preference.cat.hints.position')"
:title="$t('pages.preference.cat.labels.position')"
>
<Select v-model:value="catStore.window.position">
<SelectOption value="bottomRight">
{{ $t('pages.preference.cat.options.bottomRight') }}
</SelectOption>
<SelectOption value="bottomLeft">
{{ $t('pages.preference.cat.options.bottomLeft') }}
</SelectOption>
<SelectOption value="topLeft">
{{ $t('pages.preference.cat.options.topLeft') }}
</SelectOption>
<SelectOption value="topRight">
{{ $t('pages.preference.cat.options.topRight') }}
</SelectOption>
</Select>
</ProListItem>
</template>

View File

@@ -1,6 +1,8 @@
<script setup lang="ts">
import { InputNumber, Slider, Switch } from 'ant-design-vue'
import Position from './components/position/index.vue'
import ProList from '@/components/pro-list/index.vue'
import ProListItem from '@/components/pro-list-item/index.vue'
import { useCatStore } from '@/stores/cat'
@@ -46,6 +48,8 @@ const catStore = useCatStore()
</ProList>
<ProList :title="$t('pages.preference.cat.labels.windowSettings')">
<Position />
<ProListItem
:description="$t('pages.preference.cat.hints.passThrough')"
:title="$t('pages.preference.cat.labels.passThrough')"
@@ -60,6 +64,13 @@ const catStore = useCatStore()
<Switch v-model:checked="catStore.window.alwaysOnTop" />
</ProListItem>
<ProListItem
:description="$t('pages.preference.cat.hints.hideOnHover')"
:title="$t('pages.preference.cat.labels.hideOnHover')"
>
<Switch v-model:checked="catStore.window.hideOnHover" />
</ProListItem>
<ProListItem
:description="$t('pages.preference.cat.hints.windowSize')"
:title="$t('pages.preference.cat.labels.windowSize')"
@@ -68,6 +79,7 @@ const catStore = useCatStore()
v-model:value="catStore.window.scale"
addon-after="%"
class="w-28"
:max="500"
:min="1"
/>
</ProListItem>

View File

@@ -55,6 +55,9 @@ watch(() => generalStore.app.autostart, async (value) => {
<Select.Option value="vi-VN">
Tiếng Việt
</Select.Option>
<Select.Option value="pt-BR">
Português
</Select.Option>
</Select>
</ProListItem>
</ProList>

View File

@@ -15,6 +15,8 @@ export interface CatStore {
scale: number
opacity: number
radius: number
hideOnHover: boolean
position: 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'
}
}
@@ -59,6 +61,8 @@ export const useCatStore = defineStore('cat', () => {
scale: 100,
opacity: 100,
radius: 0,
hideOnHover: false,
position: 'bottomRight',
})
const init = () => {

View File

@@ -3,3 +3,7 @@ export function isImage(value: string) {
return regex.test(value)
}
export function inBetween(value: number, minimum: number, maximum: number) {
return value >= minimum && value <= maximum
}

View File

@@ -3,11 +3,14 @@ import type { Cubism4InternalModel } from 'pixi-live2d-display'
import { convertFileSrc } from '@tauri-apps/api/core'
import { readDir, readTextFile } from '@tauri-apps/plugin-fs'
import JSON5 from 'json5'
import { Cubism4ModelSettings, Live2DModel } from 'pixi-live2d-display'
import { Application, Ticker } from 'pixi.js'
import { join } from './path'
import { i18n } from '@/locales'
Live2DModel.registerTicker(Ticker)
class Live2d {
@@ -39,12 +42,12 @@ class Live2d {
const modelFile = files.find(file => file.name.endsWith('.model3.json'))
if (!modelFile) {
throw new Error('未找到模型主配置文件,请确认模型文件是否完整。')
throw new Error(i18n.global.t('utils.live2d.hints.notFound'))
}
const modelPath = join(path, modelFile.name)
const modelJSON = JSON.parse(await readTextFile(modelPath))
const modelJSON = JSON5.parse(await readTextFile(modelPath))
const modelSettings = new Cubism4ModelSettings({
...modelJSON,
@@ -71,7 +74,11 @@ class Live2d {
}
public destroy() {
if (!this.model) return
this.model?.destroy()
this.model = null
}
public resizeModel(modelSize: ModelSize) {

View File

@@ -1,33 +1,20 @@
import type { PhysicalPosition } from '@tauri-apps/api/window'
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'
import { monitorFromPoint } from '@tauri-apps/api/window'
import { mapValues } from 'es-toolkit'
import { cursorPosition, monitorFromPoint } from '@tauri-apps/api/window'
import { isMac } from './platform'
export async function getCursorMonitor(cursorPoint?: PhysicalPosition) {
cursorPoint ??= await cursorPosition()
export interface CursorPoint {
x: number
y: number
}
const appWindow = getCurrentWebviewWindow()
export async function getCursorMonitor(point: CursorPoint) {
let cursorPoint = point
const scaleFactor = await appWindow.scaleFactor()
if (isMac) {
const appWindow = getCurrentWebviewWindow()
const scaleFactor = await appWindow.scaleFactor()
cursorPoint = mapValues(cursorPoint, value => value * scaleFactor)
}
const { x, y } = point
const { x, y } = cursorPoint.toLogical(scaleFactor)
const monitor = await monitorFromPoint(x, y)
if (!monitor) return
return {
...monitor,
cursorPoint,
}
return monitor
}