mirror of
https://github.com/moeru-ai/airi.git
synced 2026-08-14 08:52:42 +00:00
fix(stage-*): improve cursor tracking (#1842)
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
useElectronRelativeMouse,
|
||||
} from '@proj-airi/electron-vueuse'
|
||||
import { IS_DEV } from '@proj-airi/stage-shared'
|
||||
import { useSettingsLive2d } from '@proj-airi/stage-ui-live2d'
|
||||
import { useModelStore, useThreeSceneIsTransparentAtPoint } from '@proj-airi/stage-ui-three'
|
||||
import { HoloCoupon } from '@proj-airi/stage-ui/components'
|
||||
import {
|
||||
@@ -41,7 +42,6 @@ import { modelSettingsRuntimeSnapshotChannelName } from '../../shared/model-sett
|
||||
import { useChatSyncStore } from '../stores/chat-sync'
|
||||
import { useControlsIslandStore } from '../stores/controls-island'
|
||||
import { useStageWindowLifecycleStore } from '../stores/stage-window-lifecycle'
|
||||
import { useWindowStore } from '../stores/window'
|
||||
import { shouldSampleStageTransparency } from '../utils/stage-three-transparency'
|
||||
|
||||
const controlsIslandRef = ref<InstanceType<typeof ControlsIsland>>()
|
||||
@@ -112,8 +112,6 @@ const isAroundWindowBorderFor250Ms = refDebounced(isAroundWindowBorder, 250)
|
||||
|
||||
const setIgnoreMouseEvents = useElectronEventaInvoke(electron.window.setIgnoreMouseEvents)
|
||||
|
||||
const { live2dLookAtX, live2dLookAtY } = storeToRefs(useWindowStore())
|
||||
|
||||
const { pause, resume } = watch(isTransparent, (transparent) => {
|
||||
shouldFadeOnCursorWithin.value = fadeOnHoverEnabled.value && !transparent
|
||||
}, { immediate: true })
|
||||
@@ -457,6 +455,17 @@ watch([stream, () => vadLoaded.value], async ([s, loaded]) => {
|
||||
})
|
||||
|
||||
// Assistant caption is broadcast from Stage.vue via the same channel
|
||||
|
||||
const { live2dEyeTrackingSource } = storeToRefs(useSettingsLive2d())
|
||||
live2dEyeTrackingSource.value = computed(() => ({
|
||||
x: relativeMouseX.value,
|
||||
y: relativeMouseY.value,
|
||||
}))
|
||||
const { trackingSource } = storeToRefs(useModelStore())
|
||||
trackingSource.value = computed(() => ({
|
||||
x: relativeMouseX.value,
|
||||
y: relativeMouseY.value,
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -492,7 +501,6 @@ watch([stream, () => vadLoaded.value], async ([s, loaded]) => {
|
||||
h-full w-full
|
||||
flex-1
|
||||
:paused="stagePaused"
|
||||
:focus-at="{ x: live2dLookAtX, y: live2dLookAtY }"
|
||||
/>
|
||||
<HoloCoupon />
|
||||
<ControlsIsland
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"@proj-airi/stage-pages": "workspace:^",
|
||||
"@proj-airi/stage-shared": "workspace:^",
|
||||
"@proj-airi/stage-ui": "workspace:^",
|
||||
"@proj-airi/stage-ui-live2d": "workspace:^",
|
||||
"@proj-airi/stage-ui-three": "workspace:^",
|
||||
"@proj-airi/stream-kit": "workspace:^",
|
||||
"@proj-airi/ui": "workspace:^",
|
||||
|
||||
@@ -10,6 +10,8 @@ import workletUrl from '@proj-airi/stage-ui/workers/vad/process.worklet?worker&u
|
||||
import { BackgroundProvider } from '@proj-airi/stage-layouts/components/Backgrounds'
|
||||
import { useBackgroundThemeColor } from '@proj-airi/stage-layouts/composables/theme-color'
|
||||
import { useBackgroundStore } from '@proj-airi/stage-layouts/stores/background'
|
||||
import { useSettingsLive2d } from '@proj-airi/stage-ui-live2d'
|
||||
import { useModelStore } from '@proj-airi/stage-ui-three'
|
||||
import { HoloCoupon } from '@proj-airi/stage-ui/components'
|
||||
import { WidgetStage } from '@proj-airi/stage-ui/components/scenes'
|
||||
import { useAudioRecorder } from '@proj-airi/stage-ui/composables/audio/audio-recorder'
|
||||
@@ -29,7 +31,6 @@ function handleSettingsOpen(open: boolean) {
|
||||
paused.value = open
|
||||
}
|
||||
|
||||
const positionCursor = useMouse()
|
||||
const breakpoints = useBreakpoints(breakpointsTailwind)
|
||||
const isMobile = breakpoints.smaller('md')
|
||||
|
||||
@@ -147,6 +148,18 @@ watch([stream, () => vadLoaded.value], async ([s, loaded]) => {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const { live2dEyeTrackingSource } = storeToRefs(useSettingsLive2d())
|
||||
const { x: mouseX, y: mouseY } = useMouse()
|
||||
live2dEyeTrackingSource.value = computed(() => ({
|
||||
x: mouseX.value,
|
||||
y: mouseY.value,
|
||||
}))
|
||||
const { trackingSource } = storeToRefs(useModelStore())
|
||||
trackingSource.value = computed(() => ({
|
||||
x: mouseX.value,
|
||||
y: mouseY.value,
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -167,10 +180,6 @@ watch([stream, () => vadLoaded.value], async ([s, loaded]) => {
|
||||
<WidgetStage
|
||||
flex-1 min-w="1/2"
|
||||
:paused="paused"
|
||||
:focus-at="{
|
||||
x: positionCursor.x.value,
|
||||
y: positionCursor.y.value,
|
||||
}"
|
||||
/>
|
||||
<InteractiveArea v-if="!isMobile" h="85dvh" absolute right-4 flex flex-1 flex-col max-w="500px" min-w="30%" />
|
||||
<MobileInteractiveArea v-if="isMobile" @settings-open="handleSettingsOpen" />
|
||||
|
||||
@@ -114,6 +114,22 @@ analytics:
|
||||
description: Turn this off to opt out of analytics collection.
|
||||
disabled:
|
||||
title: Analytics disabled for this development build
|
||||
model-select:
|
||||
panel-callout:
|
||||
support-status-header: We support both 2D and 3D models
|
||||
support-status: >
|
||||
Click {select-button} to select or import models.
|
||||
Currently, {zip} (for Live2D models) and {vrm} (for VRM models) are supported.
|
||||
model-type-example: >
|
||||
Neuro-sama uses 2D model driven by framework developed by Live2D Inc,
|
||||
while Grok Ani (in Grok Companion) uses 3D model driven by VRM / MMD open formats.
|
||||
select-model:
|
||||
title: Model Selector
|
||||
button: Select model
|
||||
select: Pick
|
||||
import: Import
|
||||
remove: Remove
|
||||
confirm: Confirm
|
||||
live2d:
|
||||
change-model:
|
||||
from-file: Load from File
|
||||
@@ -138,19 +154,60 @@ live2d:
|
||||
change-to-vrm: Click here to switch to the 3D avatar setting (VRM)
|
||||
theme-color-from-model:
|
||||
title: Extract colors from model
|
||||
description: >
|
||||
Extract prominent colors from current rendering canvas.
|
||||
Click on the color to apply it to AIRI.
|
||||
Try to extract multiple times, with the model in different poses,
|
||||
if the colors are not desirable.
|
||||
button-extract:
|
||||
title: Extract
|
||||
focus:
|
||||
title: Disable model mouse tracking
|
||||
button-disable:
|
||||
title: Disable model mouse tracking
|
||||
render-scale:
|
||||
title: Render scale
|
||||
fps:
|
||||
title: FPS
|
||||
description: Limit rendering frame rate
|
||||
options:
|
||||
unlimited: ∞
|
||||
animation:
|
||||
title: Animation
|
||||
focus:
|
||||
title: Enable mouse tracking
|
||||
description: Track current mouse position. May conflict with force idle eye animation
|
||||
offset: Eye offset(%)
|
||||
force-idle-eye-animation:
|
||||
title: Force eye animation when idle
|
||||
description: Randomly look around when idle
|
||||
auto-blink:
|
||||
title: Auto blink
|
||||
description: Use default blinking animation of the model
|
||||
force-auto-blink:
|
||||
title: Force auto blink
|
||||
description: Force blinking animation with external timer
|
||||
idle-motion:
|
||||
title: Idle animation
|
||||
no-motion: No animation available
|
||||
placeholder: Select animation
|
||||
disable-motion: Disabled
|
||||
disable-motion-description: Disable idle animation
|
||||
parameters:
|
||||
title: Model Parameters
|
||||
render-scale:
|
||||
title: Render scale
|
||||
fps:
|
||||
title: FPS
|
||||
description: Limit rendering frame rate
|
||||
options:
|
||||
unlimited: ∞
|
||||
shadow: Drop shadow
|
||||
reset-parameters: Reset to default parameters
|
||||
clear-model-cache: Clear model cache
|
||||
expressions:
|
||||
title: Expressions
|
||||
override-toggle: Expression system
|
||||
sdk-preset-preserved-notice: >
|
||||
SDK expression manager and eye blink are preserved when disabled.
|
||||
no-expression: No expressions available for this model.
|
||||
llm-integration-wip: LLM tool integration is not yet wired up.
|
||||
save-default: Save expression defaults
|
||||
reset: Reset all expressions
|
||||
expose-to-llm-toggle: Expose to LLM
|
||||
expose-to-llm-options:
|
||||
none: None
|
||||
all: All
|
||||
custom: Custom
|
||||
microphone: Microphone
|
||||
models: Model
|
||||
pages:
|
||||
|
||||
@@ -131,17 +131,21 @@ live2d:
|
||||
title: Extraer colores del modelo
|
||||
button-extract:
|
||||
title: Extraer
|
||||
focus:
|
||||
title: Desactivar seguimiento del ratón del modelo
|
||||
button-disable:
|
||||
title: Desactivar seguimiento del ratón del modelo
|
||||
render-scale:
|
||||
title: Escala de renderizado
|
||||
fps:
|
||||
title: FPS
|
||||
description: Limitar velocidad de fotogramas
|
||||
options:
|
||||
unlimited: '∞'
|
||||
animation:
|
||||
title: Animación
|
||||
focus:
|
||||
title: Habilitar el seguimiento del cursor
|
||||
parameters:
|
||||
title: Parámetros del modelo
|
||||
render-scale:
|
||||
title: Escala de renderizado
|
||||
fps:
|
||||
title: FPS
|
||||
description: Limitar velocidad de fotogramas
|
||||
options:
|
||||
unlimited: '∞'
|
||||
expressions:
|
||||
title: Expresión
|
||||
microphone: Micrófono
|
||||
models: Modelo
|
||||
pages:
|
||||
|
||||
@@ -131,17 +131,21 @@ live2d:
|
||||
title: Extraire les couleurs du modèle
|
||||
button-extract:
|
||||
title: Extraire
|
||||
focus:
|
||||
title: Désactiver le suivi du modèle par la souris
|
||||
button-disable:
|
||||
title: Désactiver le suivi souris du modèle
|
||||
render-scale:
|
||||
title: Échelle de rendu
|
||||
fps:
|
||||
title: FPS
|
||||
description: Limiter la fréquence d'image du rendu
|
||||
options:
|
||||
unlimited: '∞'
|
||||
animation:
|
||||
title: Animation
|
||||
focus:
|
||||
title: Activer le suivi du curseur
|
||||
parameters:
|
||||
title: Paramètres du modèle
|
||||
render-scale:
|
||||
title: Échelle de rendu
|
||||
fps:
|
||||
title: FPS
|
||||
description: Limiter la fréquence d'image du rendu
|
||||
options:
|
||||
unlimited: '∞'
|
||||
expressions:
|
||||
title: Expressions
|
||||
microphone: Microphone
|
||||
models: Modèle
|
||||
pages:
|
||||
|
||||
@@ -131,17 +131,21 @@ live2d:
|
||||
title: モデルから色を抽出
|
||||
button-extract:
|
||||
title: 抽出
|
||||
focus:
|
||||
title: モデルのマウストラッキングを無効にする
|
||||
button-disable:
|
||||
title: モデルのマウストラッキングを無効にする
|
||||
render-scale:
|
||||
title: レンダリングスケール
|
||||
fps:
|
||||
title: FPS
|
||||
description: レンダリングフレームレートを制限
|
||||
options:
|
||||
unlimited: '∞'
|
||||
animation:
|
||||
title: アニメーション
|
||||
focus:
|
||||
title: カーソル追跡を有効化
|
||||
parameters:
|
||||
title: モデルパラメータ
|
||||
render-scale:
|
||||
title: レンダリングスケール
|
||||
fps:
|
||||
title: FPS
|
||||
description: レンダリングフレームレートを制限
|
||||
options:
|
||||
unlimited: '∞'
|
||||
expressions:
|
||||
title: 表情
|
||||
microphone: マイク
|
||||
models: モデル
|
||||
pages:
|
||||
|
||||
@@ -131,10 +131,10 @@ live2d:
|
||||
title: 모델로부터 색상 추출
|
||||
button-extract:
|
||||
title: 추출
|
||||
focus:
|
||||
title: 마우스 포인터 추적 비활성화
|
||||
button-disable:
|
||||
title: Disable model mouse tracking
|
||||
animation:
|
||||
title: 애니메이션
|
||||
focus:
|
||||
title: 커서 추적 사용
|
||||
render-scale:
|
||||
title: Render scale
|
||||
fps:
|
||||
|
||||
@@ -131,10 +131,10 @@ live2d:
|
||||
title: Извлечь цвета из модели
|
||||
button-extract:
|
||||
title: Извлечь
|
||||
focus:
|
||||
title: Отключить слежение за мышью
|
||||
button-disable:
|
||||
title: Отключить отслеживание мыши модели
|
||||
animation:
|
||||
title: анимация
|
||||
focus:
|
||||
title: Включить отслеживание курсора
|
||||
render-scale:
|
||||
title: Масштаб рендеринга
|
||||
fps:
|
||||
|
||||
@@ -131,10 +131,10 @@ live2d:
|
||||
title: Trích xuất màu từ mô hình
|
||||
button-extract:
|
||||
title: Trích xuất
|
||||
focus:
|
||||
title: Tắt theo dõi chuột cho mô hình
|
||||
button-disable:
|
||||
title: Disable model mouse tracking
|
||||
animation:
|
||||
title: Diễn hoạt
|
||||
focus:
|
||||
title: Bật theo dõi con trỏ
|
||||
render-scale:
|
||||
title: Render scale
|
||||
fps:
|
||||
|
||||
@@ -105,6 +105,22 @@ analytics:
|
||||
description: 关闭任何非必要使用数据及崩溃分析的采集与上传
|
||||
disabled:
|
||||
title: 此开发版本的使用数据及崩溃分析功能已禁用
|
||||
model-select:
|
||||
panel-callout:
|
||||
support-status-header: 我们支持2D和3D模型
|
||||
support-status: >
|
||||
点击 {select-button} 以选择或导入模型。
|
||||
目前我们支持从 {zip} 导入Live2D模型,和从 {vrm} 导入VRM模型。
|
||||
model-type-example: >
|
||||
Neuro-sama 采用来自Live2D Inc.公司的技术所制作的2D模型,
|
||||
而 Grok Ani (来自 Grok Companion)使用 VRM / MMD 相关开源技术制作的3D模型。
|
||||
select-model:
|
||||
title: 模型选择
|
||||
button: 选择模型
|
||||
select: 选择
|
||||
import: 导入
|
||||
remove: 移除
|
||||
confirm: 确认
|
||||
live2d:
|
||||
change-model:
|
||||
from-file: 从文件加载
|
||||
@@ -129,19 +145,58 @@ live2d:
|
||||
change-to-vrm: 切换至3D虚拟形象设定页面(VRM)
|
||||
theme-color-from-model:
|
||||
title: 从模型提取主题颜色
|
||||
description: >
|
||||
从当前模型画布中提取主题色,点击颜色即可将其应用至 AIRI 界面。
|
||||
若没有获取到想要的颜色,可改变模型动作,多次尝试提取。
|
||||
button-extract:
|
||||
title: 提取
|
||||
focus:
|
||||
title: 禁用模型鼠标跟踪
|
||||
button-disable:
|
||||
title: 禁用 Live2D 模型的鼠标追踪
|
||||
render-scale:
|
||||
title: 渲染精度
|
||||
fps:
|
||||
title: 帧率
|
||||
description: 限制渲染帧率
|
||||
options:
|
||||
unlimited: '∞'
|
||||
animation:
|
||||
title: 动画
|
||||
focus:
|
||||
title: 启用鼠标追踪
|
||||
description: 让模型看向当前光标所在位置。可能与强制眼球动画冲突。
|
||||
offset: 眼部偏移量(%)
|
||||
force-idle-eye-animation:
|
||||
title: 强制眼球动画
|
||||
description: 待机时随机看向屏幕各处
|
||||
auto-blink:
|
||||
title: 使用眨眼动画
|
||||
description: 使用模型默认眨眼动画
|
||||
force-auto-blink:
|
||||
title: 强制眨眼动画
|
||||
description: 强制模型眨眼,模型无眨眼动画时可用。
|
||||
idle-motion:
|
||||
title: 待机动作
|
||||
no-motion: 无可用动作
|
||||
placeholder: 选择待机动作
|
||||
disable-motion: 禁用
|
||||
disable-motion-description: 禁用待机动作
|
||||
parameters:
|
||||
title: 模型参数
|
||||
render-scale:
|
||||
title: 渲染精度
|
||||
fps:
|
||||
title: 帧率
|
||||
description: 限制渲染帧率
|
||||
options:
|
||||
unlimited: ∞
|
||||
shadow: 阴影
|
||||
reset-parameters: 重置所有参数至默认
|
||||
clear-model-cache: 清除模型缓存
|
||||
expressions:
|
||||
title: 表情
|
||||
override-toggle: 启用表情
|
||||
sdk-preset-preserved-notice: >
|
||||
SDK提供的表情和眼部动作被禁用后会保留
|
||||
no-expression: 当前模型无可用表情
|
||||
llm-integration-wip: 表情系统暂未接入语言模型
|
||||
save-default: 保存为默认表情
|
||||
reset: 重置表情状态
|
||||
expose-to-llm-toggle: 允许LLM使用模型表情
|
||||
expose-to-llm-options:
|
||||
none: 无
|
||||
all: 全部
|
||||
custom: 仅指定
|
||||
microphone: 麦克风
|
||||
models: 模型
|
||||
pages:
|
||||
|
||||
@@ -131,10 +131,10 @@ live2d:
|
||||
title: 從模型中提取顏色
|
||||
button-extract:
|
||||
title: 提取
|
||||
focus:
|
||||
title: 停用模型滑鼠追蹤
|
||||
button-disable:
|
||||
title: Disable model mouse tracking
|
||||
animation:
|
||||
title: 動畫
|
||||
focus:
|
||||
title: 啟用游標追蹤
|
||||
render-scale:
|
||||
title: Render scale
|
||||
fps:
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"./composables/live2d": "./src/composables/live2d/index.ts",
|
||||
"./constants/emotions": "./src/constants/emotions.ts",
|
||||
"./stores": "./src/stores/index.ts",
|
||||
"./stores/live2d": "./src/stores/live2d.ts",
|
||||
"./stores/model-parameters": "./src/stores/model-parameters.ts",
|
||||
"./stores/expression-store": "./src/stores/expression-store.ts",
|
||||
"./tools/expression-tools": "./src/tools/expression-tools.ts",
|
||||
"./utils/eye-motions": "./src/utils/eye-motions.ts",
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { Screen } from '@proj-airi/ui'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
import SliderControls from '../ViewControls/SliderControls.vue'
|
||||
import Live2DCanvas from './live2d/Canvas.vue'
|
||||
import Live2DModel from './live2d/Model.vue'
|
||||
|
||||
import { useEyeTracking, useSettingsLive2d } from '../../composables/live2d'
|
||||
|
||||
import '../../utils/live2d-zip-loader'
|
||||
import '../../utils/live2d-opfs-registration'
|
||||
|
||||
@@ -16,31 +19,14 @@ withDefaults(defineProps<{
|
||||
paused?: boolean
|
||||
mouthOpenSize?: number
|
||||
nowSpeaking?: boolean
|
||||
focusAt?: { x: number, y: number }
|
||||
disableFocusAt?: boolean
|
||||
themeColorsHue?: number
|
||||
themeColorsHueDynamic?: boolean
|
||||
live2dIdleAnimationEnabled?: boolean
|
||||
live2dAutoBlinkEnabled?: boolean
|
||||
live2dForceAutoBlinkEnabled?: boolean
|
||||
live2dExpressionEnabled?: boolean
|
||||
live2dShadowEnabled?: boolean
|
||||
live2dMaxFps?: number
|
||||
live2dRenderScale?: number
|
||||
}>(), {
|
||||
paused: false,
|
||||
focusAt: () => ({ x: 0, y: 0 }),
|
||||
mouthOpenSize: 0,
|
||||
nowSpeaking: false,
|
||||
themeColorsHue: 220.44,
|
||||
themeColorsHueDynamic: false,
|
||||
live2dIdleAnimationEnabled: true,
|
||||
live2dAutoBlinkEnabled: true,
|
||||
live2dForceAutoBlinkEnabled: false,
|
||||
live2dExpressionEnabled: true,
|
||||
live2dShadowEnabled: true,
|
||||
live2dMaxFps: 0,
|
||||
live2dRenderScale: 2,
|
||||
})
|
||||
|
||||
const componentState = defineModel<'pending' | 'loading' | 'mounted'>('state', { default: 'pending' })
|
||||
@@ -48,6 +34,27 @@ const componentStateCanvas = defineModel<'pending' | 'loading' | 'mounted'>('can
|
||||
const componentStateModel = defineModel<'pending' | 'loading' | 'mounted'>('modelState', { default: 'pending' })
|
||||
|
||||
const live2dCanvasRef = ref<InstanceType<typeof Live2DCanvas>>()
|
||||
const live2dModelRef = ref<InstanceType<typeof Live2DModel>>()
|
||||
|
||||
const {
|
||||
live2dEyeTracking,
|
||||
live2dIdleAnimationEnabled,
|
||||
live2dForceIdleEyeAnimation,
|
||||
live2dAutoBlinkEnabled,
|
||||
live2dForceAutoBlinkEnabled,
|
||||
live2dExpressionEnabled,
|
||||
live2dMaxFps,
|
||||
live2dRenderScale,
|
||||
live2dShadowEnabled,
|
||||
} = storeToRefs(useSettingsLive2d())
|
||||
const mouseFocus = useEyeTracking(
|
||||
() => live2dCanvasRef.value?.canvasElement(),
|
||||
() => ({
|
||||
normalizedScale: live2dModelRef.value?.modelNormalizeParams.scale ?? 1,
|
||||
modelWidth: live2dModelRef.value?.initialModelWidth ?? 1000,
|
||||
modelHeight: live2dModelRef.value?.initialModelHeight ?? 1000,
|
||||
}),
|
||||
)
|
||||
|
||||
watch([componentStateModel, componentStateCanvas], () => {
|
||||
componentState.value = (componentStateModel.value === 'mounted' && componentStateCanvas.value === 'mounted')
|
||||
@@ -81,6 +88,7 @@ defineExpose({
|
||||
max-h="100dvh"
|
||||
>
|
||||
<Live2DModel
|
||||
ref="live2dModelRef"
|
||||
v-model:state="componentStateModel"
|
||||
:model-src="modelSrc"
|
||||
:model-id="modelId"
|
||||
@@ -90,11 +98,12 @@ defineExpose({
|
||||
:width="width"
|
||||
:height="height"
|
||||
:paused="paused"
|
||||
:focus-at="focusAt"
|
||||
:disable-focus-at="disableFocusAt"
|
||||
:focus-at="mouseFocus"
|
||||
:eye-tracking="live2dEyeTracking"
|
||||
:theme-colors-hue="themeColorsHue"
|
||||
:theme-colors-hue-dynamic="themeColorsHueDynamic"
|
||||
:live2d-idle-animation-enabled="live2dIdleAnimationEnabled"
|
||||
:live2d-force-idle-eye-animation="live2dForceIdleEyeAnimation"
|
||||
:live2d-auto-blink-enabled="live2dAutoBlinkEnabled"
|
||||
:live2d-force-auto-blink-enabled="live2dForceAutoBlinkEnabled"
|
||||
:live2d-expression-enabled="live2dExpressionEnabled"
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { PixiLive2DInternalModel } from '../../../composables/live2d'
|
||||
|
||||
import { listenBeatSyncBeatSignal } from '@proj-airi/stage-shared/beat-sync'
|
||||
import { useTheme } from '@proj-airi/ui'
|
||||
import { breakpointsTailwind, until, useBreakpoints } from '@vueuse/core'
|
||||
import { until } from '@vueuse/core'
|
||||
import { animate } from 'animejs'
|
||||
import { formatHex } from 'culori'
|
||||
import { Mutex } from 'es-toolkit'
|
||||
@@ -26,8 +26,9 @@ import {
|
||||
useMotionUpdatePluginIdleFocus,
|
||||
useMotionUpdatePluginLipSync,
|
||||
} from '../../../composables/live2d'
|
||||
import { useFitModel } from '../../../composables/live2d/fit-model'
|
||||
import { Emotion, EmotionNeutralMotionName } from '../../../constants/emotions'
|
||||
import { useL2dViewControl, useLive2d } from '../../../stores/live2d'
|
||||
import { useL2dViewControl, useLive2dParams } from '../../../stores'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
modelSrc?: string
|
||||
@@ -40,10 +41,11 @@ const props = withDefaults(defineProps<{
|
||||
height: number
|
||||
paused?: boolean
|
||||
focusAt?: { x: number, y: number }
|
||||
disableFocusAt?: boolean
|
||||
eyeTracking?: boolean
|
||||
themeColorsHue?: number
|
||||
themeColorsHueDynamic?: boolean
|
||||
live2dIdleAnimationEnabled?: boolean
|
||||
live2dForceIdleEyeAnimation?: boolean
|
||||
live2dAutoBlinkEnabled?: boolean
|
||||
live2dForceAutoBlinkEnabled?: boolean
|
||||
live2dExpressionEnabled?: boolean
|
||||
@@ -53,11 +55,13 @@ const props = withDefaults(defineProps<{
|
||||
nowSpeaking: false,
|
||||
paused: false,
|
||||
focusAt: () => ({ x: 0, y: 0 }),
|
||||
eyeTracking: false,
|
||||
disableFocusAt: false,
|
||||
scale: 1,
|
||||
themeColorsHue: 220.44,
|
||||
themeColorsHueDynamic: false,
|
||||
live2dIdleAnimationEnabled: true,
|
||||
live2dForceIdleEyeAnimation: true,
|
||||
live2dAutoBlinkEnabled: true,
|
||||
live2dForceAutoBlinkEnabled: false,
|
||||
live2dExpressionEnabled: true,
|
||||
@@ -72,16 +76,6 @@ const emits = defineEmits<{
|
||||
const componentState = defineModel<'pending' | 'loading' | 'mounted'>('state', { default: 'pending' })
|
||||
const { position, scale } = useL2dViewControl()
|
||||
|
||||
function parsePropsOffset() {
|
||||
const xOffset = (position.value.x / 100) * props.width
|
||||
const yOffset = -(position.value.y / 100) * props.height
|
||||
|
||||
return {
|
||||
xOffset,
|
||||
yOffset,
|
||||
}
|
||||
}
|
||||
|
||||
const modelSrcRef = toRef(() => props.modelSrc)
|
||||
|
||||
const modelLoading = ref(false)
|
||||
@@ -90,7 +84,10 @@ let isUnmounted = false
|
||||
|
||||
const modelLoadMutex = new Mutex()
|
||||
|
||||
const offset = computed(() => parsePropsOffset())
|
||||
const offset = computed(() => ({
|
||||
x: (position.value.x / 100) * props.width,
|
||||
y: -(position.value.y / 100) * props.height,
|
||||
}))
|
||||
|
||||
const pixiApp = toRef(() => props.app)
|
||||
const paused = toRef(() => props.paused)
|
||||
@@ -103,8 +100,6 @@ const nowSpeaking = toRef(() => props.nowSpeaking)
|
||||
const lastUpdateTime = ref(0)
|
||||
|
||||
const { isDark: dark } = useTheme()
|
||||
const breakpoints = useBreakpoints(breakpointsTailwind)
|
||||
const isMobile = computed(() => breakpoints.between('sm', 'md').value || breakpoints.smaller('sm').value)
|
||||
const dropShadowFilter = shallowRef(new DropShadowFilter({
|
||||
alpha: 0.2,
|
||||
blur: 0,
|
||||
@@ -114,37 +109,25 @@ const dropShadowFilter = shallowRef(new DropShadowFilter({
|
||||
|
||||
let resizeAnimation: ReturnType<typeof animate> | undefined
|
||||
|
||||
function computeScaleAndPosition() {
|
||||
let offsetFactor = 2.2
|
||||
if (isMobile.value) {
|
||||
offsetFactor = 2.2
|
||||
}
|
||||
const modelNormalizeParams = useFitModel(
|
||||
() => ({ width: props.width, height: props.height }),
|
||||
() => ({ width: initialModelWidth.value, height: initialModelHeight.value }),
|
||||
)
|
||||
|
||||
const heightScale = (props.height * 0.95 / initialModelHeight.value * offsetFactor)
|
||||
const widthScale = (props.width * 0.95 / initialModelWidth.value * offsetFactor)
|
||||
let minScale = Math.min(heightScale, widthScale)
|
||||
|
||||
if (Number.isNaN(minScale) || minScale <= 0) {
|
||||
minScale = 1e-6
|
||||
}
|
||||
|
||||
return {
|
||||
scale: minScale * scale.value,
|
||||
x: (props.width / 2) + offset.value.xOffset,
|
||||
y: props.height + offset.value.yOffset,
|
||||
}
|
||||
}
|
||||
watch([offset, scale, modelNormalizeParams], () => {
|
||||
setScaleAndPosition()
|
||||
})
|
||||
|
||||
function setScaleAndPosition(animated = false) {
|
||||
if (!model.value)
|
||||
return
|
||||
|
||||
const target = computeScaleAndPosition()
|
||||
const normalized = modelNormalizeParams.value
|
||||
|
||||
if (!animated) {
|
||||
model.value.scale.set(target.scale, target.scale)
|
||||
model.value.x = target.x
|
||||
model.value.y = target.y
|
||||
model.value.scale.set(normalized.scale * scale.value, normalized.scale * scale.value)
|
||||
model.value.x = normalized.x + offset.value.x
|
||||
model.value.y = normalized.y + offset.value.y
|
||||
return
|
||||
}
|
||||
|
||||
@@ -157,9 +140,9 @@ function setScaleAndPosition(animated = false) {
|
||||
}
|
||||
|
||||
resizeAnimation = animate(current, {
|
||||
scale: target.scale,
|
||||
x: target.x,
|
||||
y: target.y,
|
||||
scale: normalized.scale * scale.value,
|
||||
x: normalized.x + offset.value.x,
|
||||
y: normalized.y + offset.value.y,
|
||||
duration: 200,
|
||||
ease: 'outQuad',
|
||||
onUpdate: () => {
|
||||
@@ -172,7 +155,7 @@ function setScaleAndPosition(animated = false) {
|
||||
})
|
||||
}
|
||||
|
||||
const live2dStore = useLive2d()
|
||||
const live2dStore = useLive2dParams()
|
||||
const {
|
||||
currentMotion,
|
||||
availableMotions,
|
||||
@@ -183,6 +166,7 @@ const {
|
||||
const themeColorsHue = toRef(() => props.themeColorsHue)
|
||||
const themeColorsHueDynamic = toRef(() => props.themeColorsHueDynamic)
|
||||
const live2dIdleAnimationEnabled = toRef(() => props.live2dIdleAnimationEnabled)
|
||||
const live2dForceIdleEyeAnimation = toRef(() => props.live2dForceIdleEyeAnimation)
|
||||
const live2dAutoBlinkEnabled = toRef(() => props.live2dAutoBlinkEnabled)
|
||||
const live2dForceAutoBlinkEnabled = toRef(() => props.live2dForceAutoBlinkEnabled)
|
||||
const live2dExpressionEnabled = toRef(() => props.live2dExpressionEnabled)
|
||||
@@ -355,6 +339,7 @@ async function loadModel() {
|
||||
motionManager,
|
||||
modelParameters,
|
||||
live2dIdleAnimationEnabled,
|
||||
live2dForceIdleEyeAnimation,
|
||||
live2dAutoBlinkEnabled,
|
||||
live2dForceAutoBlinkEnabled,
|
||||
lastUpdateTime,
|
||||
@@ -512,10 +497,6 @@ async function setMotion(motionName: string, index?: number) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleResize() {
|
||||
setScaleAndPosition(true)
|
||||
}
|
||||
|
||||
const dropShadowColorComputer = ref<HTMLDivElement>()
|
||||
const dropShadowAnimationId = ref(0)
|
||||
|
||||
@@ -536,12 +517,10 @@ function updateDropShadowFilter() {
|
||||
model.value.filters = [dropShadowFilter.value]
|
||||
}
|
||||
|
||||
watch([() => props.width, () => props.height], handleResize)
|
||||
watch(modelSrcRef, async () => await loadModel(), { immediate: true })
|
||||
watch(dark, updateDropShadowFilter, { immediate: true })
|
||||
watch([model, themeColorsHue], updateDropShadowFilter)
|
||||
watch(live2dShadowEnabled, updateDropShadowFilter)
|
||||
watch([offset, scale], () => setScaleAndPosition())
|
||||
|
||||
// TODO: This is hacky!
|
||||
function updateDropShadowFilterLoop() {
|
||||
@@ -749,7 +728,7 @@ watch(live2dExpressionEnabled, (enabled) => {
|
||||
watch(focusAt, (value) => {
|
||||
if (!model.value)
|
||||
return
|
||||
if (props.disableFocusAt)
|
||||
if (!props.eyeTracking)
|
||||
return
|
||||
|
||||
model.value.focus(value.x, value.y)
|
||||
@@ -778,6 +757,9 @@ function listMotionGroups() {
|
||||
defineExpose({
|
||||
setMotion,
|
||||
listMotionGroups,
|
||||
modelNormalizeParams,
|
||||
initialModelHeight,
|
||||
initialModelWidth,
|
||||
})
|
||||
|
||||
import.meta.hot?.dispose(() => {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import type { ComputedRef, MaybeRefOrGetter } from 'vue'
|
||||
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, toValue } from 'vue'
|
||||
|
||||
import { useL2dViewControl } from '../../stores'
|
||||
import { useSettingsLive2d } from './live2d'
|
||||
|
||||
const { live2dRenderScale, live2dModelEyeOffset, live2dEyeTrackingSource } = storeToRefs(useSettingsLive2d())
|
||||
const { scale } = useL2dViewControl()
|
||||
|
||||
/**
|
||||
* Maps the tracking source's position into the model's rendering space. Does not enable tracking by itself.
|
||||
* @param canvas the canvas that draws the avatar
|
||||
* @param model normalized scale and dimensions of the model
|
||||
* @returns a computed ref that maps the tracking source's position into the model's rendering space
|
||||
*/
|
||||
export function useEyeTracking(
|
||||
canvas: MaybeRefOrGetter<HTMLCanvasElement | undefined>,
|
||||
model: MaybeRefOrGetter<{
|
||||
normalizedScale: number
|
||||
modelWidth: number
|
||||
modelHeight: number
|
||||
}>,
|
||||
): ComputedRef<{ x: number, y: number }> {
|
||||
const mouseFocus = computed(() => {
|
||||
const { normalizedScale, modelWidth, modelHeight } = toValue(model)
|
||||
const renderScale = live2dRenderScale.value
|
||||
// does not require further unwrapping for some reason
|
||||
const trackingSource = live2dEyeTrackingSource.value as { x: number, y: number } | null
|
||||
const canvasRect = toValue(canvas)?.getBoundingClientRect()
|
||||
if (!trackingSource || !(canvasRect)) {
|
||||
return { x: 1000, y: 1000 }
|
||||
}
|
||||
const eyeOffset = {
|
||||
x: live2dModelEyeOffset.value.x / 100 * modelWidth * normalizedScale * scale.value,
|
||||
y: live2dModelEyeOffset.value.y / 100 * modelHeight * normalizedScale * scale.value,
|
||||
}
|
||||
return {
|
||||
x: (trackingSource.x - canvasRect.left + eyeOffset.x) * renderScale,
|
||||
y: (trackingSource.y - canvasRect.top + eyeOffset.y) * renderScale,
|
||||
}
|
||||
})
|
||||
|
||||
return mouseFocus
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import type { MaybeRefOrGetter } from 'vue'
|
||||
|
||||
import { isStageWeb } from '@proj-airi/stage-shared'
|
||||
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core'
|
||||
import { computed, toValue } from 'vue'
|
||||
|
||||
const breakpoints = useBreakpoints(breakpointsTailwind)
|
||||
const startingOffsetY = computed(() => {
|
||||
if (isStageWeb()) // showing upper half of the body in landscape, 3/4 in portrait, in web targets
|
||||
return breakpoints.smallerOrEqual('md').value ? 0.75 : 1
|
||||
return 1 // upper half
|
||||
})
|
||||
|
||||
/**
|
||||
* Normalizes the model so that user `scale == 1` means twice the viewport height,
|
||||
* and the model centered horizontally when `position.x == 0`,
|
||||
* showing upper half of the body when `position.y == 0`
|
||||
*/
|
||||
export function useFitModel(
|
||||
canvasDim: MaybeRefOrGetter<{ width: number, height: number }>,
|
||||
modelDim: MaybeRefOrGetter<{ width: number, height: number }>,
|
||||
) {
|
||||
const normalizedParam = computed(() => {
|
||||
const canvas = toValue(canvasDim)
|
||||
const model = toValue(modelDim)
|
||||
|
||||
const heightScale = (canvas.height / model.height * 2)
|
||||
const widthScale = (canvas.width / model.width * 2)
|
||||
let minScale = Math.min(heightScale, widthScale)
|
||||
|
||||
if (Number.isNaN(minScale) || minScale <= 0) {
|
||||
minScale = 1e-6
|
||||
}
|
||||
return {
|
||||
scale: minScale,
|
||||
x: canvas.width / 2,
|
||||
y: canvas.height * startingOffsetY.value,
|
||||
}
|
||||
})
|
||||
|
||||
return normalizedParam
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
export * from './animation'
|
||||
export * from './beat-sync'
|
||||
export * from './expression-controller'
|
||||
export * from './eye-tracking'
|
||||
export * from './live2d'
|
||||
export * from './motion-manager'
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import type { ComputedRef, Ref } from 'vue'
|
||||
|
||||
import { useLocalStorageManualReset, useVersionedLocalStorageManualReset } from '@proj-airi/stage-shared/composables'
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const live2dEyeTracking = useLocalStorageManualReset<boolean>('settings/live2d/eye-tracking', false)
|
||||
/**
|
||||
* A position to perform eye-tracking on.
|
||||
* Should be a position relative to the application window:
|
||||
* - for browser targets, it should be the top-left corner of the viewport
|
||||
* - for tamagotchi targets, it should be the top-left corner of
|
||||
* the application window that renders the model
|
||||
*/
|
||||
const live2dEyeTrackingSource: Ref<ComputedRef<{ x: number, y: number }> | null> = ref(null)
|
||||
/** Offset from model center to the eyes of the model, in percentages of full model width/height */
|
||||
const live2dModelEyeOffset = useLocalStorageManualReset('settings/live2d/model-eye-offset', { x: 0, y: 0 })
|
||||
const live2dIdleAnimationEnabled = useLocalStorageManualReset<boolean>('settings/live2d/idle-animation-enabled', true)
|
||||
/** Force the avatar to look around. May conflict with eye tracking and idle animation. */
|
||||
const live2dForceIdleEyeAnimation = useLocalStorageManualReset<boolean>('settings/live2d/idle-eye-animation-enabled', false)
|
||||
const live2dAutoBlinkEnabled = useVersionedLocalStorageManualReset<boolean>('settings/live2d/auto-blink-enabled', false, {
|
||||
defaultVersion: '2.0.0',
|
||||
satisfiesVersionBy(beforeVersion, afterVersion) {
|
||||
if (beforeVersion === afterVersion) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
})
|
||||
const live2dForceAutoBlinkEnabled = useVersionedLocalStorageManualReset<boolean>('settings/live2d/force-auto-blink-enabled', true, {
|
||||
defaultVersion: '2.0.0',
|
||||
satisfiesVersionBy(beforeVersion, afterVersion) {
|
||||
if (beforeVersion === afterVersion) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
})
|
||||
const live2dExpressionEnabled = useLocalStorageManualReset<boolean>('settings/live2d/expression-enabled', false)
|
||||
const live2dShadowEnabled = useLocalStorageManualReset<boolean>('settings/live2d/shadow-enabled', true)
|
||||
const live2dMaxFps = useLocalStorageManualReset<number>('settings/live2d/max-fps', 0)
|
||||
const live2dRenderScale = useLocalStorageManualReset<number>('settings/live2d/render-scale', 2)
|
||||
|
||||
function resetState() {
|
||||
live2dEyeTracking.reset()
|
||||
live2dModelEyeOffset.reset()
|
||||
live2dIdleAnimationEnabled.reset()
|
||||
live2dForceIdleEyeAnimation.reset()
|
||||
live2dAutoBlinkEnabled.reset()
|
||||
live2dForceAutoBlinkEnabled.reset()
|
||||
live2dExpressionEnabled.reset()
|
||||
live2dShadowEnabled.reset()
|
||||
live2dMaxFps.reset()
|
||||
live2dRenderScale.reset()
|
||||
}
|
||||
|
||||
export const useSettingsLive2d = defineStore('settings-live2d', () => {
|
||||
return {
|
||||
live2dEyeTracking,
|
||||
live2dEyeTrackingSource,
|
||||
live2dModelEyeOffset,
|
||||
live2dIdleAnimationEnabled,
|
||||
live2dForceIdleEyeAnimation,
|
||||
live2dAutoBlinkEnabled,
|
||||
live2dForceAutoBlinkEnabled,
|
||||
live2dExpressionEnabled,
|
||||
live2dShadowEnabled,
|
||||
live2dMaxFps,
|
||||
live2dRenderScale,
|
||||
resetState,
|
||||
}
|
||||
})
|
||||
@@ -28,6 +28,7 @@ export type MotionManagerPluginContext = MotionManagerUpdateContext & {
|
||||
motionManager: PixiLive2DInternalModel['motionManager']
|
||||
modelParameters: Ref<any>
|
||||
live2dIdleAnimationEnabled: Ref<boolean>
|
||||
live2dForceIdleEyeAnimation: Ref<boolean>
|
||||
live2dAutoBlinkEnabled: Ref<boolean>
|
||||
live2dForceAutoBlinkEnabled: Ref<boolean>
|
||||
isIdleMotion: boolean
|
||||
@@ -42,6 +43,7 @@ export interface UseLive2DMotionManagerUpdateOptions {
|
||||
motionManager: PixiLive2DInternalModel['motionManager']
|
||||
modelParameters: Ref<any>
|
||||
live2dIdleAnimationEnabled: Ref<boolean>
|
||||
live2dForceIdleEyeAnimation: Ref<boolean>
|
||||
live2dAutoBlinkEnabled: Ref<boolean>
|
||||
live2dForceAutoBlinkEnabled: Ref<boolean>
|
||||
lastUpdateTime: Ref<number>
|
||||
@@ -53,6 +55,7 @@ export function useLive2DMotionManagerUpdate(options: UseLive2DMotionManagerUpda
|
||||
motionManager,
|
||||
modelParameters,
|
||||
live2dIdleAnimationEnabled,
|
||||
live2dForceIdleEyeAnimation,
|
||||
live2dAutoBlinkEnabled,
|
||||
live2dForceAutoBlinkEnabled,
|
||||
lastUpdateTime,
|
||||
@@ -95,6 +98,7 @@ export function useLive2DMotionManagerUpdate(options: UseLive2DMotionManagerUpda
|
||||
motionManager,
|
||||
modelParameters,
|
||||
live2dIdleAnimationEnabled,
|
||||
live2dForceIdleEyeAnimation,
|
||||
live2dAutoBlinkEnabled,
|
||||
live2dForceAutoBlinkEnabled,
|
||||
isIdleMotion,
|
||||
@@ -206,8 +210,8 @@ export function useMotionUpdatePluginIdleDisable(idleEyeFocus = useLive2DIdleEye
|
||||
if (!ctx.live2dIdleAnimationEnabled.value && ctx.isIdleMotion) {
|
||||
ctx.motionManager.stopAllMotions()
|
||||
|
||||
// Still update eye focus and blink even if idle motion is stopped
|
||||
idleEyeFocus.update(ctx.internalModel, ctx.now)
|
||||
if (ctx.live2dForceIdleEyeAnimation.value)
|
||||
idleEyeFocus.update(ctx.internalModel, ctx.now)
|
||||
if (ctx.internalModel.eyeBlink != null) {
|
||||
ctx.internalModel.eyeBlink.updateParameters(ctx.model, ctx.timeDelta / 1000)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from './expression-store'
|
||||
export * from './live2d'
|
||||
export * from './model-parameters'
|
||||
export * from './view-control'
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ export const defaultModelParameters = {
|
||||
breath: 0,
|
||||
}
|
||||
|
||||
export const useLive2d = defineStore('live2d', () => {
|
||||
export const useLive2dParams = defineStore('live2d', () => {
|
||||
const { post, data } = useBroadcastChannel<BroadcastChannelEvents, BroadcastChannelEvents>({ name: 'airi-stores-stage-ui-live2d' })
|
||||
const shouldUpdateViewHooks = ref(new Set<() => void>())
|
||||
|
||||
@@ -72,7 +72,7 @@ export function useL2dViewControl() {
|
||||
return {
|
||||
/** model position relative to the center of the screen, in pixels */
|
||||
position,
|
||||
/** model scaling in percentages. `100` means no scaling. */
|
||||
/** model scaling in percentages. `1` means no scaling. */
|
||||
scale,
|
||||
/** reset the given control to its default value. */
|
||||
set,
|
||||
|
||||
@@ -17,7 +17,6 @@ import type {
|
||||
Texture,
|
||||
WebGLRenderer,
|
||||
} from 'three'
|
||||
import type { Ref, WatchStopHandle } from 'vue'
|
||||
|
||||
import type {
|
||||
VrmDisposeHookContext,
|
||||
@@ -32,7 +31,7 @@ import type { ManagedVrmInstance } from './vrm-instance-cache'
|
||||
|
||||
import { VRMUtils } from '@pixiv/three-vrm'
|
||||
import { useLoop, useTresContext } from '@tresjs/core'
|
||||
import { until, useMouse } from '@vueuse/core'
|
||||
import { until } from '@vueuse/core'
|
||||
import {
|
||||
AnimationMixer,
|
||||
Box3,
|
||||
@@ -40,11 +39,9 @@ import {
|
||||
Mesh,
|
||||
MeshPhysicalMaterial,
|
||||
MeshStandardMaterial,
|
||||
Plane,
|
||||
Raycaster,
|
||||
|
||||
SRGBColorSpace,
|
||||
Vector2,
|
||||
Vector3,
|
||||
} from 'three'
|
||||
import {
|
||||
@@ -60,6 +57,7 @@ import {
|
||||
|
||||
} from 'vue'
|
||||
|
||||
import { useEyeTracking } from '../../composables/eye-tracking'
|
||||
import {
|
||||
createIblProbeController,
|
||||
injectDiffuseIBL,
|
||||
@@ -121,9 +119,9 @@ const props = withDefaults(defineProps<{
|
||||
|
||||
modelOffset: Vec3
|
||||
modelRotationY: number
|
||||
lookAtTarget: Vec3
|
||||
trackingMode: string
|
||||
eyeHeight: number
|
||||
screenBoundingBox: () => { top: number, left: number, width: number, height: number }
|
||||
cameraPosition: Vec3
|
||||
|
||||
camera: PerspectiveCamera
|
||||
@@ -141,7 +139,6 @@ const emit = defineEmits<{
|
||||
(e: 'loadingProgress', value: number): void
|
||||
(e: 'loadStart', value: 'initial-load' | 'model-reload' | 'model-switch'): void
|
||||
(e: 'sceneBootstrap', value: SceneBootstrap): void
|
||||
(e: 'lookAtTarget', value: Vec3): void
|
||||
|
||||
(e: 'error', value: unknown): void
|
||||
(e: 'loaded', value: string): void
|
||||
@@ -161,10 +158,7 @@ const {
|
||||
|
||||
modelOffset,
|
||||
modelRotationY,
|
||||
lookAtTarget,
|
||||
trackingMode,
|
||||
eyeHeight,
|
||||
cameraPosition,
|
||||
|
||||
camera,
|
||||
} = toRefs(props)
|
||||
@@ -176,12 +170,7 @@ const vrmGroup = shallowRef<Group>()
|
||||
const modelLoaded = ref<boolean>(false)
|
||||
let loadSequence = 0
|
||||
// for eye tracking modes
|
||||
const { x: mouseX, y: mouseY } = useMouse()
|
||||
const raycaster = new Raycaster()
|
||||
const mouse = new Vector2()
|
||||
const mouseTarget = shallowRef<Vec3>()
|
||||
let stopMouseWatch: WatchStopHandle | undefined
|
||||
let stopCameraWatch: WatchStopHandle | undefined
|
||||
|
||||
// Animation related ref
|
||||
const vrmAnimationMixer = ref<AnimationMixer>()
|
||||
@@ -454,7 +443,6 @@ function bindManagedVrmInstanceRenderLoop() {
|
||||
})
|
||||
const blinkAndSaccadeMs = measureFrameStep(tracingEnabled, () => {
|
||||
blink.update(activeVrm, delta)
|
||||
idleEyeSaccades.update(activeVrm, lookAtTarget, delta)
|
||||
})
|
||||
const emoteMs = measureFrameStep(tracingEnabled, () => {
|
||||
vrmEmote.value?.update(delta)
|
||||
@@ -563,40 +551,7 @@ function componentCleanUp(
|
||||
}
|
||||
}
|
||||
|
||||
// look at mouse
|
||||
function lookAtMouse(
|
||||
mouseX: number,
|
||||
mouseY: number,
|
||||
camera: Ref<PerspectiveCamera>,
|
||||
): Vec3 {
|
||||
mouse.x = (mouseX / window.innerWidth) * 2 - 1
|
||||
mouse.y = -(mouseY / window.innerHeight) * 2 + 1
|
||||
|
||||
// Raycast from the mouse position
|
||||
raycaster.setFromCamera(mouse, camera.value)
|
||||
|
||||
// Create a plane in front of the camera
|
||||
const cameraDirection = new Vector3()
|
||||
camera.value.getWorldDirection(cameraDirection) // Get camera's forward direction
|
||||
|
||||
const plane = new Plane()
|
||||
plane.setFromNormalAndCoplanarPoint(
|
||||
cameraDirection,
|
||||
camera.value.position.clone().add(cameraDirection.multiplyScalar(1)), // 1 unit in front of the camera
|
||||
)
|
||||
|
||||
const intersection = new Vector3()
|
||||
raycaster.ray.intersectPlane(plane, intersection)
|
||||
return { x: intersection.x, y: intersection.y, z: intersection.z }
|
||||
}
|
||||
|
||||
function defaultTookAt(eyeHeight: number): Vec3 {
|
||||
return {
|
||||
x: 0,
|
||||
y: eyeHeight,
|
||||
z: -100,
|
||||
}
|
||||
}
|
||||
const defaultTookAt = computed(() => new Vector3(0, eyeHeight.value, -100))
|
||||
|
||||
function computeBoundingBox(vrmScene: Object3D) {
|
||||
const box = new Box3()
|
||||
@@ -661,7 +616,7 @@ function buildSceneBootstrap(activeVrm: VRM, cacheHit: boolean): SceneBootstrap
|
||||
cameraDistance: cameraPosition.distanceTo(modelCenter),
|
||||
cameraPosition: { x: cameraPosition.x, y: cameraPosition.y, z: cameraPosition.z },
|
||||
eyeHeight: eyePositionY,
|
||||
lookAtTarget: defaultTookAt(eyePositionY),
|
||||
lookAtTarget: defaultTookAt.value,
|
||||
modelOffset: {
|
||||
x: bootstrapRoot.position.x,
|
||||
y: bootstrapRoot.position.y,
|
||||
@@ -940,6 +895,12 @@ async function loadModel() {
|
||||
}
|
||||
}
|
||||
|
||||
const focusPos = useEyeTracking(() => ({
|
||||
camera: camera.value,
|
||||
raycaster,
|
||||
defaultLookAt: defaultTookAt.value,
|
||||
}), props.screenBoundingBox)
|
||||
|
||||
onMounted(async () => {
|
||||
// watch if the model needs to be reloaded
|
||||
// Registered BEFORE the initial load to avoid missing src changes
|
||||
@@ -1023,32 +984,9 @@ onMounted(async () => {
|
||||
})
|
||||
airiIblProbe?.update(mode, skyBoxIntensity.value, nprIrrSH.value ?? null)
|
||||
}, { immediate: true })
|
||||
// update eye tracking mode
|
||||
watch(trackingMode, (newMode) => {
|
||||
stopCameraWatch?.()
|
||||
stopCameraWatch = undefined
|
||||
stopMouseWatch?.()
|
||||
stopMouseWatch = undefined
|
||||
if (newMode === 'camera') {
|
||||
stopCameraWatch = watch(cameraPosition, (newPosition) => {
|
||||
// watch to update look at target to camera
|
||||
emit('lookAtTarget', newPosition)
|
||||
}, { immediate: true, deep: true })
|
||||
}
|
||||
else if (newMode === 'mouse') {
|
||||
stopMouseWatch = watch([mouseX, mouseY], ([newX, newY]) => {
|
||||
mouseTarget.value = lookAtMouse(newX, newY, camera)
|
||||
// watch to update look at target to mouse
|
||||
emit('lookAtTarget', mouseTarget.value)
|
||||
}, { immediate: true, deep: true })
|
||||
}
|
||||
else {
|
||||
emit('lookAtTarget', defaultTookAt(eyeHeight.value))
|
||||
}
|
||||
watch(focusPos, (newPos) => {
|
||||
idleEyeSaccades.instantUpdate(vrm.value, newPos)
|
||||
}, { immediate: true })
|
||||
watch(lookAtTarget, (newTarget) => {
|
||||
idleEyeSaccades.instantUpdate(vrm.value, newTarget)
|
||||
}, { deep: true })
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
|
||||
@@ -133,7 +133,8 @@ const vrmFrameRuntimeHook = shallowRef<VrmFrameRuntimeHook>()
|
||||
|
||||
const camera = shallowRef(new PerspectiveCamera())
|
||||
const controlsRef = shallowRef<InstanceType<typeof OrbitControls>>()
|
||||
const tresCanvasRef = shallowRef<TresContext>()
|
||||
const tresContextRef = shallowRef<TresContext>()
|
||||
const screenRef = ref<InstanceType<typeof Screen>>()
|
||||
const skyBoxEnvRef = ref<InstanceType<typeof SkyBox>>()
|
||||
const dirLightRef = ref<InstanceType<typeof DirectionalLight>>()
|
||||
const stageThreeRuntimeTraceContext = getStageThreeRuntimeTraceContext()
|
||||
@@ -276,10 +277,10 @@ function applySceneBootstrap(value: SceneBootstrap) {
|
||||
}
|
||||
|
||||
const { readRenderTargetRegionAtClientPoint, disposeRenderTarget } = useRenderTargetRegionAtClientPoint({
|
||||
getRenderer: () => tresCanvasRef.value?.renderer.instance as WebGLRenderer | undefined,
|
||||
getScene: () => tresCanvasRef.value?.scene.value,
|
||||
getRenderer: () => tresContextRef.value?.renderer.instance as WebGLRenderer | undefined,
|
||||
getScene: () => tresContextRef.value?.scene.value,
|
||||
getCamera: () => camera.value,
|
||||
getCanvas: () => tresCanvasRef.value?.renderer.instance.domElement,
|
||||
getCanvas: () => tresContextRef.value?.renderer.instance.domElement,
|
||||
})
|
||||
|
||||
/*
|
||||
@@ -447,7 +448,7 @@ function onVRMSceneBootstrap(value: SceneBootstrap) {
|
||||
pendingSceneBootstrap.value = value
|
||||
}
|
||||
|
||||
function onVRMModelLookAtTarget(value: Vec3) {
|
||||
function onVRMModelLookAtTarget(value: Vector3) {
|
||||
lookAtTarget.value.x = value.x
|
||||
lookAtTarget.value.y = value.y
|
||||
lookAtTarget.value.z = value.z
|
||||
@@ -482,7 +483,7 @@ function onSkyBoxReady(EnvPayload: {
|
||||
|
||||
// === Tres Canvas ===
|
||||
function onTresReady(context: TresContext) {
|
||||
tresCanvasRef.value = context
|
||||
tresContextRef.value = context
|
||||
canvasReady.value = true
|
||||
emitSceneSubtreeTrace('tresCanvasRef', 'attached')
|
||||
setScenePhaseWithTrace(resolveScenePhaseAfterBinding(), 'tres:ready')
|
||||
@@ -492,7 +493,7 @@ function onTresRender() {
|
||||
if (!isStageThreeRuntimeTraceEnabled())
|
||||
return
|
||||
|
||||
const renderer = tresCanvasRef.value?.renderer.instance
|
||||
const renderer = tresContextRef.value?.renderer.instance
|
||||
if (!renderer)
|
||||
return
|
||||
|
||||
@@ -515,11 +516,11 @@ onMounted(() => {
|
||||
|
||||
onUnmounted(() => {
|
||||
invalidateBindingRevision()
|
||||
if (tresCanvasRef.value)
|
||||
if (tresContextRef.value)
|
||||
emitSceneSubtreeTrace('tresCanvasRef', 'detached')
|
||||
|
||||
canvasReady.value = false
|
||||
tresCanvasRef.value = undefined
|
||||
tresContextRef.value = undefined
|
||||
activeModelSrc.value = undefined
|
||||
pendingSceneBootstrap.value = undefined
|
||||
resetSceneBindingTransactions()
|
||||
@@ -669,6 +670,8 @@ function updateDirLightTarget(newRotation: { x: number, y: number, z: number })
|
||||
directionalLightTarget.value = { x: target.x, y: target.y, z: target.z }
|
||||
}
|
||||
|
||||
const getScreenBBox = () => screenRef.value?.containerRef?.getBoundingClientRect() ?? { top: 0, left: 0, width: 500, height: 500 }
|
||||
|
||||
watch(directionalLightRotation, (newRotation) => {
|
||||
updateDirLightTarget(newRotation)
|
||||
}, { deep: true })
|
||||
@@ -685,17 +688,17 @@ defineExpose({
|
||||
applyVrmFrameRuntimeHook()
|
||||
},
|
||||
canvasElement: () => {
|
||||
return tresCanvasRef.value?.renderer.instance.domElement
|
||||
return tresContextRef.value?.renderer.instance.domElement
|
||||
},
|
||||
camera: () => camera.value,
|
||||
renderer: () => tresCanvasRef.value?.renderer.instance,
|
||||
renderer: () => tresContextRef.value?.renderer.instance,
|
||||
scene: () => modelRef.value?.scene,
|
||||
readRenderTargetRegionAtClientPoint,
|
||||
captureFrame: async () => {
|
||||
if (!tresCanvasRef.value)
|
||||
if (!tresContextRef.value)
|
||||
return null
|
||||
|
||||
const { renderer, scene } = tresCanvasRef.value
|
||||
const { renderer, scene } = tresContextRef.value
|
||||
renderer.instance.render(scene.value, camera.value)
|
||||
|
||||
return new Promise<Blob | null>((resolve) => {
|
||||
@@ -706,7 +709,7 @@ defineExpose({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Screen v-slot="{ width, height }" relative>
|
||||
<Screen ref="screenRef" v-slot="{ width, height }" relative>
|
||||
<div top="50%" translate-y="[-50%]" fixed z-15 px-3>
|
||||
<SliderControls />
|
||||
</div>
|
||||
@@ -779,6 +782,7 @@ defineExpose({
|
||||
:eye-height="eyeHeight"
|
||||
:camera-position="cameraPosition"
|
||||
:camera="camera"
|
||||
:screen-bounding-box="getScreenBBox"
|
||||
@loading-progress="(val: number) => emit('loadModelProgress', val)"
|
||||
@load-start="onVRMModelLoadStart"
|
||||
@scene-bootstrap="onVRMSceneBootstrap"
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import type { PerspectiveCamera, Raycaster } from 'three'
|
||||
import type { MaybeRefOrGetter } from 'vue'
|
||||
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { Vector2, Vector3 } from 'three'
|
||||
import { computed, toValue } from 'vue'
|
||||
|
||||
import { useModelStore } from '../stores/model-store'
|
||||
|
||||
interface ThreeWorldContext {
|
||||
raycaster: Raycaster
|
||||
camera: PerspectiveCamera
|
||||
defaultLookAt: Vector3
|
||||
}
|
||||
|
||||
const { trackingSource, trackingMode, cameraPosition } = storeToRefs(useModelStore())
|
||||
|
||||
// look at mouse
|
||||
export function useEyeTracking(
|
||||
context: MaybeRefOrGetter<ThreeWorldContext>,
|
||||
screenBoundingBox: MaybeRefOrGetter<{ top: number, left: number, height: number, width: number }>,
|
||||
) {
|
||||
const focusPos = computed<Vector3>(() => {
|
||||
if (trackingMode.value === 'camera')
|
||||
return new Vector3(cameraPosition.value.x, cameraPosition.value.y, cameraPosition.value.z)
|
||||
const ctx = toValue(context)
|
||||
if (trackingMode.value === 'none' || !trackingSource.value)
|
||||
return ctx.defaultLookAt
|
||||
const screen = toValue(screenBoundingBox)
|
||||
if (trackingMode.value === 'mouse') {
|
||||
const trackingPos = trackingSource.value as unknown as { x: number, y: number }
|
||||
// from tracking origin to camera center(screen center)
|
||||
const castedPos = castScreenToCam(
|
||||
ctx,
|
||||
new Vector2(
|
||||
((trackingPos.x - screen.left) / screen.width) * 2 - 1,
|
||||
-((trackingPos.y - screen.top) / screen.height) * 2 + 1,
|
||||
),
|
||||
)
|
||||
return castedPos
|
||||
}
|
||||
return ctx.defaultLookAt
|
||||
})
|
||||
|
||||
return focusPos
|
||||
}
|
||||
|
||||
function castScreenToCam(ctx: ThreeWorldContext, point: Vector2): Vector3 {
|
||||
ctx.raycaster.setFromCamera(point, ctx.camera)
|
||||
const nearPlaneDistance = ctx.camera.near
|
||||
const direction = ctx.raycaster.ray.direction.clone().normalize().multiplyScalar(8)
|
||||
const pointOnNearPlane = ctx.raycaster.ray.origin.clone()
|
||||
.add(direction.multiplyScalar(nearPlaneDistance))
|
||||
return pointOnNearPlane
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Vector3 } from 'three'
|
||||
import type { ComputedRef, Ref } from 'vue'
|
||||
|
||||
import { useBroadcastChannel, useLocalStorage } from '@vueuse/core'
|
||||
import { defineStore } from 'pinia'
|
||||
@@ -82,6 +83,18 @@ const vrmViewUpdateRuntimeInstanceId = Math.random().toString(36).slice(2, 10)
|
||||
let vrmViewUpdateMessageSequence = 0
|
||||
const { modelOffset, set: setViewControl } = useThreeViewControl()
|
||||
const { cameraDistance, cameraFOV, cameraPosition } = useThreeCamera()
|
||||
|
||||
const modelRotationY = useLocalStorage('settings/stage-ui-three/modelRotationY', 0)
|
||||
const trackingMode = useLocalStorage('settings/stage-ui-three/trackingMode', 'none' as 'camera' | 'mouse' | 'none')
|
||||
/**
|
||||
* A position to perform eye-tracking on.
|
||||
* Should be a position relative to the application window:
|
||||
* - for browser targets, it should be the top-left corner of the viewport
|
||||
* - for tamagotchi targets, it should be the top-left corner of
|
||||
* the application window that renders the model
|
||||
*/
|
||||
const trackingSource: Ref<ComputedRef<{ x: number, y: number }> | null> = ref(null)
|
||||
|
||||
export const useModelStore = defineStore('modelStore', () => {
|
||||
const { post, data } = useBroadcastChannel<BroadcastChannelEvents, BroadcastChannelEvents>({ name: 'airi-stores-stage-ui-three-vrm' })
|
||||
const shouldUpdateViewHooks = ref(new Set<() => void>())
|
||||
@@ -144,11 +157,6 @@ export const useModelStore = defineStore('modelStore', () => {
|
||||
const modelOrigin = useLocalStorage('settings/stage-ui-three/modelOrigin', { x: 0, y: 0, z: 0 })
|
||||
const eyeHeight = useLocalStorage('settings/stage-ui-three/eyeHeight', 0)
|
||||
|
||||
// === User scene settings ===
|
||||
// These values are intended to survive model reloads and direct edits from settings UI.
|
||||
const modelRotationY = useLocalStorage('settings/stage-ui-three/modelRotationY', 0)
|
||||
const trackingMode = useLocalStorage('settings/stage-ui-three/trackingMode', 'none' as 'camera' | 'mouse' | 'none')
|
||||
|
||||
// === View state ===
|
||||
/** current runtime pose. may be recalculated when a new model bootstrap is applied. */
|
||||
const lookAtTarget = useLocalStorage('settings/stage-ui-three/lookAtTarget', { x: 0, y: 0, z: 0 })
|
||||
@@ -237,6 +245,7 @@ export const useModelStore = defineStore('modelStore', () => {
|
||||
|
||||
lookAtTarget,
|
||||
trackingMode,
|
||||
trackingSource,
|
||||
eyeHeight,
|
||||
renderScale,
|
||||
multisampling,
|
||||
|
||||
@@ -136,6 +136,7 @@ function stopDrag() {
|
||||
</div>
|
||||
<div />
|
||||
<label
|
||||
v-if="props.xConfig !== undefined"
|
||||
h-fit w-full inline-flex items-center rounded-md px="1.5" py="0.5"
|
||||
:class="[isDragging === 'x' ? 'bg-red-100/50 dark:bg-red-900/50' : 'bg-neutral-100 dark:bg-neutral-900', props.disabled ? 'opacity-60' : '']"
|
||||
transition="colors duration-200 ease-in-out"
|
||||
@@ -149,9 +150,9 @@ function stopDrag() {
|
||||
:value="xNormalized"
|
||||
type="number"
|
||||
:disabled="props.disabled"
|
||||
:min="props.xConfig?.min"
|
||||
:max="props.xConfig?.max"
|
||||
:step="0.0001"
|
||||
:min="props.xConfig.min"
|
||||
:max="props.xConfig.max"
|
||||
:step="props.xConfig.step"
|
||||
max-w-4lh w-full appearance-none bg-transparent text-right font-mono outline-none
|
||||
class="[&::-webkit-inner-spin-button]:(m-0 appearance-none)"
|
||||
@change="(e) => handleChange('x', e)"
|
||||
@@ -159,6 +160,7 @@ function stopDrag() {
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
v-if="props.yConfig !== undefined"
|
||||
h-fit inline-flex items-center rounded-md px="1.5" py="0.5"
|
||||
:class="[isDragging === 'y' ? 'bg-blue-100/50 dark:bg-blue-900/50' : 'bg-neutral-100 dark:bg-neutral-900', props.disabled ? 'opacity-60' : '']"
|
||||
transition="colors duration-200 ease-in-out"
|
||||
@@ -172,9 +174,9 @@ function stopDrag() {
|
||||
:value="yNormalized"
|
||||
type="number"
|
||||
:disabled="props.disabled"
|
||||
:min="props.yConfig?.min"
|
||||
:max="props.yConfig?.max"
|
||||
:step="0.0001"
|
||||
:min="props.yConfig.min"
|
||||
:max="props.yConfig.max"
|
||||
:step="props.yConfig.step"
|
||||
max-w-4lh w-full appearance-none bg-transparent text-right font-mono outline-none
|
||||
class="[&::-webkit-inner-spin-button]:(m-0 appearance-none)"
|
||||
@change="(e) => handleChange('y', e)"
|
||||
@@ -182,6 +184,7 @@ function stopDrag() {
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
v-if="props.zConfig !== undefined"
|
||||
h-fit inline-flex items-center rounded-md px="1.5" py="0.5"
|
||||
:class="[isDragging === 'z' ? 'bg-green-100/50 dark:bg-green-900/50' : 'bg-neutral-100 dark:bg-neutral-900', props.disabled ? 'opacity-60' : '']"
|
||||
transition="colors duration-200 ease-in-out"
|
||||
@@ -195,9 +198,9 @@ function stopDrag() {
|
||||
:value="zNormalized"
|
||||
type="number"
|
||||
:disabled="props.disabled"
|
||||
:min="props.zConfig?.min"
|
||||
:max="props.zConfig?.max"
|
||||
:step="0.0001"
|
||||
:min="props.zConfig.min"
|
||||
:max="props.zConfig.max"
|
||||
:step="props.zConfig.step"
|
||||
max-w-4lh w-full appearance-none bg-transparent text-right font-mono outline-none
|
||||
class="[&::-webkit-inner-spin-button]:(m-0 appearance-none)"
|
||||
@change="(e) => handleChange('z', e)"
|
||||
|
||||
+7
-5
@@ -9,6 +9,7 @@ import { useFileDialog } from '@vueuse/core'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { DropdownMenuContent, DropdownMenuItem, DropdownMenuPortal, DropdownMenuRoot, DropdownMenuTrigger, EditableArea, EditableEditTrigger, EditableInput, EditablePreview, EditableRoot, EditableSubmitTrigger } from 'reka-ui'
|
||||
import { ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Live2DReportModal from './Live2DReportModal.vue'
|
||||
|
||||
@@ -24,6 +25,7 @@ const emits = defineEmits<{
|
||||
|
||||
const displayModelStore = useDisplayModelsStore()
|
||||
const { displayModelsFromIndexedDBLoading, displayModels } = storeToRefs(displayModelStore)
|
||||
const { t } = useI18n()
|
||||
|
||||
function handleRemoveModel(model: DisplayModel) {
|
||||
displayModelStore.removeDisplayModel(model.id)
|
||||
@@ -146,7 +148,7 @@ spineDialog.onChange(handleAddSpineModel)
|
||||
|
||||
<div flex items-center>
|
||||
<div w-full flex-1 text-xl>
|
||||
Model Selector
|
||||
{{ t('settings.model-select.select-model.title') }}
|
||||
</div>
|
||||
<div>
|
||||
<DropdownMenuRoot>
|
||||
@@ -157,7 +159,7 @@ spineDialog.onChange(handleAddSpineModel)
|
||||
aria-label="Options for Display Models"
|
||||
>
|
||||
<div i-solar:add-circle-bold />
|
||||
<div>Add</div>
|
||||
<div>{{ t('settings.model-select.select-model.import') }}</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuContent
|
||||
@@ -256,7 +258,7 @@ spineDialog.onChange(handleAddSpineModel)
|
||||
>
|
||||
<button flex items-center gap-1 outline-none @click="handleRemoveModel(model)">
|
||||
<div i-solar:trash-bin-minimalistic-bold-duotone />
|
||||
<div>Remove</div>
|
||||
<div>{{ t('settings.model-select.select-model.remove') }}</div>
|
||||
</button>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
@@ -320,14 +322,14 @@ spineDialog.onChange(handleAddSpineModel)
|
||||
</div>
|
||||
</div>
|
||||
<Button class="hidden md:block" variant="secondary" @click="handlePick(model)">
|
||||
Pick
|
||||
{{ t('settings.model-select.select-model.select') }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button class="block md:hidden" @click="handleMobilePick()">
|
||||
Confirm
|
||||
{{ t('settings.model-select.select-model.confirm') }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { ModelSettingsRuntimeSnapshot } from './runtime'
|
||||
|
||||
import { defaultModelParameters, useExpressionStore, useLive2d } from '@proj-airi/stage-ui-live2d'
|
||||
import { defaultModelParameters, useExpressionStore, useLive2dParams, useSettingsLive2d } from '@proj-airi/stage-ui-live2d'
|
||||
import { OPFSCache } from '@proj-airi/stage-ui-live2d/utils/opfs-loader'
|
||||
import { Button, Checkbox, FieldCheckbox, FieldCombobox, FieldRange, SelectTab } from '@proj-airi/ui'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { useSettings } from '../../../../stores/settings'
|
||||
import { PropertyPoint } from '../../../data-pane'
|
||||
import { Section } from '../../../layouts'
|
||||
import { ColorPalette } from '../../../widgets'
|
||||
|
||||
@@ -25,9 +25,10 @@ defineEmits<{
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const settings = useSettings()
|
||||
const settings = useSettingsLive2d()
|
||||
const {
|
||||
live2dDisableFocus,
|
||||
live2dEyeTracking,
|
||||
live2dModelEyeOffset,
|
||||
live2dIdleAnimationEnabled,
|
||||
live2dAutoBlinkEnabled,
|
||||
live2dForceAutoBlinkEnabled,
|
||||
@@ -35,9 +36,10 @@ const {
|
||||
live2dShadowEnabled,
|
||||
live2dMaxFps,
|
||||
live2dRenderScale,
|
||||
live2dForceIdleEyeAnimation,
|
||||
} = storeToRefs(settings)
|
||||
|
||||
const live2d = useLive2d()
|
||||
const live2d = useLive2dParams()
|
||||
const {
|
||||
scale,
|
||||
position,
|
||||
@@ -65,15 +67,25 @@ function isGroupActive(group: { parameters: { parameterId: string, value: number
|
||||
const selectedRuntimeMotion = ref<string>('')
|
||||
const runtimeMotions = ref<Array<{ name: string, displayPath: string, group: string, index: number }>>([])
|
||||
const canExtractColors = computed(() => props.runtimeSnapshot.canCapturePreview)
|
||||
const runtimeMotionOptions = computed(() => runtimeMotions.value.map(motion => ({
|
||||
label: motion.name,
|
||||
value: motion.displayPath,
|
||||
description: motion.displayPath,
|
||||
})))
|
||||
const runtimeMotionOptions = computed(() => {
|
||||
const options = runtimeMotions.value.map(motion => ({
|
||||
label: motion.name,
|
||||
value: motion.displayPath,
|
||||
description: motion.displayPath,
|
||||
}))
|
||||
if (options.length > 0) {
|
||||
options.unshift({
|
||||
label: t('settings.live2d.animation.idle-motion.disable-motion'),
|
||||
value: '<motion disabled>',
|
||||
description: t('settings.live2d.animation.idle-motion.disable-motion-description'),
|
||||
})
|
||||
}
|
||||
return options
|
||||
})
|
||||
const fpsOptions = computed(() => [
|
||||
{ value: 0, label: t('settings.live2d.fps.options.unlimited') },
|
||||
{ value: 60, label: '60' },
|
||||
{ value: 30, label: '30' },
|
||||
{ value: 60, label: '60' },
|
||||
{ value: 0, label: t('settings.live2d.parameters.fps.options.unlimited') },
|
||||
])
|
||||
|
||||
watch(() => live2d.availableMotions, (motions) => {
|
||||
@@ -87,11 +99,11 @@ watch(() => live2d.availableMotions, (motions) => {
|
||||
console.info('Available motions:', runtimeMotions.value)
|
||||
}, { immediate: true })
|
||||
|
||||
const llmModeOptions = [
|
||||
{ value: 'none', label: 'None' },
|
||||
{ value: 'all', label: 'All' },
|
||||
{ value: 'custom', label: 'Custom' },
|
||||
]
|
||||
const llmModeOptions = computed(() => [
|
||||
{ value: 'none', label: t('settings.live2d.expressions.expose-to-llm-options.none') },
|
||||
{ value: 'all', label: t('settings.live2d.expressions.expose-to-llm-options.all') },
|
||||
{ value: 'custom', label: t('settings.live2d.expressions.expose-to-llm-options.custom') },
|
||||
])
|
||||
|
||||
// Get available runtime motions from the model
|
||||
onMounted(() => {
|
||||
@@ -126,6 +138,7 @@ function handleMotionSelect(selectedMotionPath: string | number | undefined) {
|
||||
|
||||
const motion = runtimeMotions.value.find(item => item.displayPath === selectedMotionPath)
|
||||
if (!motion) {
|
||||
live2dIdleAnimationEnabled.value = false
|
||||
return
|
||||
}
|
||||
|
||||
@@ -245,6 +258,9 @@ function handleMotionSelect(selectedMotionPath: string | number | undefined) {
|
||||
size="sm"
|
||||
:expand="false"
|
||||
>
|
||||
<p text="neutral-500 dark:neutral-400">
|
||||
{{ t('settings.live2d.theme-color-from-model.description') }}
|
||||
</p>
|
||||
<ColorPalette class="mb-4 mt-2" :colors="palette.map(hex => ({ hex, name: hex }))" mx-auto />
|
||||
<Button variant="secondary" :disabled="!canExtractColors" @click="$emit('extractColorsFromModel')">
|
||||
{{ t('settings.live2d.theme-color-from-model.button-extract.title') }}
|
||||
@@ -291,7 +307,7 @@ function handleMotionSelect(selectedMotionPath: string | number | undefined) {
|
||||
</a>
|
||||
</Section> -->
|
||||
<Section
|
||||
title="Parameters"
|
||||
:title="t('settings.live2d.animation.title')"
|
||||
icon="i-solar:settings-bold-duotone"
|
||||
:class="[
|
||||
'rounded-xl',
|
||||
@@ -302,64 +318,96 @@ function handleMotionSelect(selectedMotionPath: string | number | undefined) {
|
||||
:expand="false"
|
||||
>
|
||||
<FieldCheckbox
|
||||
v-model="live2dDisableFocus"
|
||||
:label="t('settings.live2d.focus.button-disable.title')"
|
||||
v-model="live2dEyeTracking"
|
||||
:label="t('settings.live2d.animation.focus.title')"
|
||||
:description="t('settings.live2d.animation.focus.description')"
|
||||
placement="right"
|
||||
/>
|
||||
<div v-if="live2dEyeTracking" class="grid grid-cols-4">
|
||||
<PropertyPoint
|
||||
v-model:x="live2dModelEyeOffset.x"
|
||||
v-model:y="live2dModelEyeOffset.y"
|
||||
|
||||
:x-config="{ min: -100, max: 100, step: 0.01, label: 'X', formatValue: (val: number) => val?.toFixed(2) }"
|
||||
:y-config="{ min: -100, max: 100, step: 0.01, label: 'Y', formatValue: (val: number) => val?.toFixed(2) }"
|
||||
>
|
||||
<template #label>
|
||||
<p class="text-xs text-neutral-500 dark:text-neutral-400">
|
||||
{{ t('settings.live2d.animation.focus.offset') }}
|
||||
</p>
|
||||
</template>
|
||||
</PropertyPoint>
|
||||
</div>
|
||||
<FieldCheckbox
|
||||
v-model="live2dForceIdleEyeAnimation"
|
||||
:label="t('settings.live2d.animation.force-idle-eye-animation.title')"
|
||||
:description="t('settings.live2d.animation.force-idle-eye-animation.description')"
|
||||
placement="right"
|
||||
/>
|
||||
<FieldCheckbox
|
||||
v-model="live2dAutoBlinkEnabled"
|
||||
:label="t('settings.live2d.animation.auto-blink.title')"
|
||||
:description="t('settings.live2d.animation.auto-blink.description')"
|
||||
placement="right"
|
||||
/>
|
||||
<FieldCheckbox
|
||||
v-model="live2dForceAutoBlinkEnabled"
|
||||
:label="t('settings.live2d.animation.force-auto-blink.title')"
|
||||
:description="t('settings.live2d.animation.force-auto-blink.description')"
|
||||
placement="right"
|
||||
/>
|
||||
<FieldCombobox
|
||||
v-model="selectedRuntimeMotion"
|
||||
:label="t('settings.live2d.animation.idle-motion.title')"
|
||||
:options="runtimeMotionOptions"
|
||||
:placeholder="t('settings.live2d.animation.idle-motion.placeholder')"
|
||||
:select-class="['w-full']"
|
||||
:content-min-width="256"
|
||||
@update:model-value="handleMotionSelect"
|
||||
>
|
||||
<template #empty>
|
||||
{{ t('settings.live2d.animation.idle-motion.no-motion') }}
|
||||
</template>
|
||||
</FieldCombobox>
|
||||
</Section>
|
||||
<Section
|
||||
:title="t('settings.live2d.parameters.title')"
|
||||
icon="i-solar:settings-bold-duotone"
|
||||
:class="[
|
||||
'rounded-xl',
|
||||
'bg-white/80 dark:bg-black/75',
|
||||
'backdrop-blur-lg',
|
||||
]"
|
||||
size="sm"
|
||||
:expand="false"
|
||||
>
|
||||
<FieldRange
|
||||
v-model="live2dRenderScale"
|
||||
as="div"
|
||||
:min="0.5"
|
||||
:max="2"
|
||||
:step="0.25"
|
||||
:label="t('settings.live2d.render-scale.title')"
|
||||
:label="t('settings.live2d.parameters.render-scale.title')"
|
||||
/>
|
||||
|
||||
<FieldCombobox
|
||||
v-model="selectedRuntimeMotion"
|
||||
label="Idle Animation"
|
||||
:options="runtimeMotionOptions"
|
||||
placeholder="Select Motion"
|
||||
:select-class="['w-full']"
|
||||
:content-min-width="256"
|
||||
@update:model-value="handleMotionSelect"
|
||||
>
|
||||
<template #empty>
|
||||
No motions available
|
||||
</template>
|
||||
</FieldCombobox>
|
||||
|
||||
<label class="flex flex-col gap-4">
|
||||
<div :class="['flex items-center gap-2', 'flex-row']">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-1 text-sm font-medium">
|
||||
<slot name="label">
|
||||
{{ t('settings.live2d.fps.title') }}
|
||||
</slot>
|
||||
</div>
|
||||
<div class="text-xs text-neutral-500 dark:text-neutral-400">
|
||||
<slot name="description">
|
||||
{{ t('settings.live2d.fps.description') }}
|
||||
</slot>
|
||||
</div>
|
||||
<label class="flex flex-wrap gap-4">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-1 text-sm font-medium">
|
||||
<slot name="label">
|
||||
{{ t('settings.live2d.parameters.fps.title') }}
|
||||
</slot>
|
||||
</div>
|
||||
<div class="text-xs text-neutral-500 dark:text-neutral-400">
|
||||
<slot name="description">
|
||||
{{ t('settings.live2d.parameters.fps.description') }}
|
||||
</slot>
|
||||
</div>
|
||||
<SelectTab v-model="live2dMaxFps" :options="fpsOptions" size="sm" :class="['shrink-0']" />
|
||||
</div>
|
||||
<SelectTab v-model="live2dMaxFps" :options="fpsOptions" size="sm" :class="['shrink-0']" />
|
||||
</label>
|
||||
|
||||
<div mt-4 flex items-center justify-between>
|
||||
<span text-sm text-neutral-600 dark:text-neutral-400>Auto Blink</span>
|
||||
<Checkbox v-model="live2dAutoBlinkEnabled" />
|
||||
</div>
|
||||
|
||||
<div mt-3 flex items-center justify-between>
|
||||
<span text-sm text-neutral-600 dark:text-neutral-400>Force Auto Blink (fallback timer)</span>
|
||||
<Checkbox v-model="live2dForceAutoBlinkEnabled" />
|
||||
</div>
|
||||
|
||||
<div mt-4 flex items-center justify-between>
|
||||
<span text-sm text-neutral-600 dark:text-neutral-400>Shadow</span>
|
||||
<span text-sm>{{ t('settings.live2d.parameters.shadow') }}</span>
|
||||
<Checkbox v-model="live2dShadowEnabled" />
|
||||
</div>
|
||||
|
||||
@@ -368,7 +416,7 @@ function handleMotionSelect(selectedMotionPath: string | number | undefined) {
|
||||
class="mt-4 w-full"
|
||||
@click="resetToDefaultParameters"
|
||||
>
|
||||
Reset To Default Parameters
|
||||
{{ t('settings.live2d.parameters.reset-parameters') }}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@@ -378,7 +426,7 @@ function handleMotionSelect(selectedMotionPath: string | number | undefined) {
|
||||
:loading="clearingCache"
|
||||
@click="clearModelCache"
|
||||
>
|
||||
Clear Model Cache
|
||||
{{ t('settings.live2d.clear-model-cache') }}
|
||||
</Button>
|
||||
|
||||
<!-- Head Rotation -->
|
||||
@@ -632,7 +680,7 @@ function handleMotionSelect(selectedMotionPath: string | number | undefined) {
|
||||
</FieldRange>
|
||||
</Section>
|
||||
<Section
|
||||
title="Expressions"
|
||||
:title="t('settings.live2d.expressions.title')"
|
||||
icon="i-solar:face-scan-circle-bold-duotone"
|
||||
:class="[
|
||||
'rounded-xl',
|
||||
@@ -643,15 +691,15 @@ function handleMotionSelect(selectedMotionPath: string | number | undefined) {
|
||||
:expand="false"
|
||||
>
|
||||
<div flex items-center justify-between>
|
||||
<span text-sm text-neutral-600 dark:text-neutral-400>Expression System</span>
|
||||
<span text-sm text-neutral-600 dark:text-neutral-400>{{ t('settings.live2d.expressions.override-toggle') }}</span>
|
||||
<Checkbox v-model="live2dExpressionEnabled" />
|
||||
</div>
|
||||
<div v-if="!live2dExpressionEnabled" py-2 text-xs text-neutral-500 dark:text-neutral-400>
|
||||
SDK expression manager and eye blink are preserved when disabled.
|
||||
{{ t('settings.live2d.expressions.sdk-preset-preserved-notice') }}
|
||||
</div>
|
||||
<template v-else-if="expressionGroups.size === 0">
|
||||
<div py-2 text-sm text-neutral-500 dark:text-neutral-400>
|
||||
No expressions available for this model.
|
||||
{{ t('settings.live2d.expressions.no-expression') }}
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
@@ -670,8 +718,8 @@ function handleMotionSelect(selectedMotionPath: string | number | undefined) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div mt-4 flex items-center gap-3>
|
||||
<span whitespace-nowrap text-sm text-neutral-600 dark:text-neutral-400>Expose to LLM</span>
|
||||
<div mt-4 flex flex-wrap items-center gap-3>
|
||||
<span whitespace-nowrap text-sm text-neutral-600 dark:text-neutral-400>{{ t('settings.live2d.expressions.expose-to-llm-toggle') }}</span>
|
||||
<SelectTab
|
||||
:model-value="expressionStore.llmMode"
|
||||
:options="llmModeOptions"
|
||||
@@ -680,7 +728,7 @@ function handleMotionSelect(selectedMotionPath: string | number | undefined) {
|
||||
/>
|
||||
</div>
|
||||
<span v-if="expressionStore.llmMode !== 'none'" text-xs text-neutral-500 dark:text-neutral-400>
|
||||
LLM tool integration is not yet wired up.
|
||||
{{ t('settings.live2d.expressions.llm-integration-wip') }}
|
||||
</span>
|
||||
|
||||
<!-- Custom per-expression LLM toggles (only when mode = 'custom') -->
|
||||
@@ -701,10 +749,10 @@ function handleMotionSelect(selectedMotionPath: string | number | undefined) {
|
||||
<!-- Action buttons -->
|
||||
<div mt-4 flex gap-2>
|
||||
<Button variant="secondary" @click="expressionStore.saveDefaults()">
|
||||
Save Expression Defaults
|
||||
{{ t('settings.live2d.expressions.save-default') }}
|
||||
</Button>
|
||||
<Button variant="secondary" @click="expressionStore.resetAll()">
|
||||
Reset All Expressions
|
||||
{{ t('settings.live2d.expressions.reset') }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { ModelSettingsRuntimeSnapshot } from './runtime'
|
||||
import { Button, Callout } from '@proj-airi/ui'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Godot from './godot.vue'
|
||||
import Live2D from './live2d.vue'
|
||||
@@ -28,6 +29,7 @@ defineEmits<{
|
||||
(e: 'extractColorsFromModel'): void
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
const modelSelectorOpen = ref(false)
|
||||
const settingsStore = useSettings()
|
||||
const airiCardStore = useAiriCardStore()
|
||||
@@ -57,21 +59,26 @@ async function handleModelPick(selectedModel: DisplayModel | undefined) {
|
||||
...settingsClassList,
|
||||
]"
|
||||
>
|
||||
<Callout label="We support both 2D and 3D models">
|
||||
<Callout :label="t('settings.model-select.panel-callout.support-status-header')">
|
||||
<i18n-t keypath="settings.model-select.panel-callout.support-status" tag="p">
|
||||
<template #select-button>
|
||||
<strong>{{ t('settings.model-select.select-model.button') }}</strong>
|
||||
</template>
|
||||
<template #zip>
|
||||
<code>.zip</code>
|
||||
</template>
|
||||
<template #vrm>
|
||||
<code>.vrm</code>
|
||||
</template>
|
||||
</i18n-t>
|
||||
<p>
|
||||
Click <strong>Select Model</strong> to import different formats of
|
||||
models into catalog, currently, <code>.zip</code> (Live2D, Spine) and <code>.vrm</code> (VRM) are supported.
|
||||
</p>
|
||||
<p>
|
||||
Neuro-sama uses 2D model driven by Live2D Inc. developed framework.
|
||||
Grok Ani uses 3D model that is driven by VRM / MMD open formats.
|
||||
Spine 2D models are supported via Esoteric Software's Spine runtime.
|
||||
{{ t('settings.model-select.panel-callout.model-type-example') }}
|
||||
</p>
|
||||
</Callout>
|
||||
<div :class="['flex flex-wrap items-center gap-2']">
|
||||
<ModelSelectorDialog v-model:show="modelSelectorOpen" :selected-model="currentSelectedDisplayModel" @pick="handleModelPick">
|
||||
<Button variant="secondary">
|
||||
Select Model
|
||||
{{ t('settings.model-select.select-model.button') }}
|
||||
</Button>
|
||||
</ModelSelectorDialog>
|
||||
<slot name="actions" />
|
||||
|
||||
+16
-17
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { ModelSettingsRuntimeSnapshot } from './runtime'
|
||||
|
||||
import { Live2DScene } from '@proj-airi/stage-ui-live2d'
|
||||
import { Live2DScene, useSettingsLive2d } from '@proj-airi/stage-ui-live2d'
|
||||
import { SpineScene } from '@proj-airi/stage-ui-spine'
|
||||
import { ThreeScene, useModelStore } from '@proj-airi/stage-ui-three'
|
||||
import { useMouse } from '@vueuse/core'
|
||||
@@ -24,7 +24,6 @@ const emit = defineEmits<{
|
||||
(e: 'runtimeSnapshotChanged', value: ModelSettingsRuntimeSnapshot): void
|
||||
}>()
|
||||
|
||||
const positionCursor = useMouse()
|
||||
const settingsStore = useSettings()
|
||||
const modelStore = useModelStore()
|
||||
const live2dSceneRef = ref<{ canvasElement: () => HTMLCanvasElement | undefined }>()
|
||||
@@ -37,18 +36,14 @@ const vrmPreviewStageInstanceId = `model-settings-preview-stage:${Math.random().
|
||||
provide('previewStage', true)
|
||||
|
||||
const {
|
||||
live2dDisableFocus,
|
||||
stageModelSelected,
|
||||
stageModelSelectedUrl,
|
||||
stageModelRenderer,
|
||||
themeColorsHue,
|
||||
themeColorsHueDynamic,
|
||||
live2dIdleAnimationEnabled,
|
||||
live2dAutoBlinkEnabled,
|
||||
live2dForceAutoBlinkEnabled,
|
||||
live2dShadowEnabled,
|
||||
live2dMaxFps,
|
||||
live2dRenderScale,
|
||||
|
||||
} = storeToRefs(settingsStore)
|
||||
const {
|
||||
spinePremultipliedAlpha,
|
||||
spineDefaultMixDuration,
|
||||
spineIdleAnimationEnabled,
|
||||
@@ -156,6 +151,18 @@ watch(runtimeSnapshot, snapshot => emit('runtimeSnapshotChanged', snapshot), { i
|
||||
defineExpose({
|
||||
capturePreviewFrame,
|
||||
})
|
||||
|
||||
const { live2dEyeTrackingSource } = storeToRefs(useSettingsLive2d())
|
||||
const { x: mouseX, y: mouseY } = useMouse()
|
||||
live2dEyeTrackingSource.value = computed(() => ({
|
||||
x: mouseX.value,
|
||||
y: mouseY.value,
|
||||
}))
|
||||
const { trackingSource } = storeToRefs(useModelStore())
|
||||
trackingSource.value = computed(() => ({
|
||||
x: mouseX.value,
|
||||
y: mouseY.value,
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -164,18 +171,10 @@ defineExpose({
|
||||
<Live2DScene
|
||||
ref="live2dSceneRef"
|
||||
v-model:state="live2dComponentState"
|
||||
:focus-at="{ x: positionCursor.x.value, y: positionCursor.y.value }"
|
||||
:model-src="stageModelSelectedUrl"
|
||||
:model-id="stageModelSelected"
|
||||
:disable-focus-at="live2dDisableFocus"
|
||||
:theme-colors-hue="themeColorsHue"
|
||||
:theme-colors-hue-dynamic="themeColorsHueDynamic"
|
||||
:live2d-idle-animation-enabled="live2dIdleAnimationEnabled"
|
||||
:live2d-auto-blink-enabled="live2dAutoBlinkEnabled"
|
||||
:live2d-force-auto-blink-enabled="live2dForceAutoBlinkEnabled"
|
||||
:live2d-shadow-enabled="live2dShadowEnabled"
|
||||
:live2d-max-fps="live2dMaxFps"
|
||||
:live2d-render-scale="live2dRenderScale"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -11,7 +11,7 @@ import { sleep } from '@moeru/std'
|
||||
import { createLive2DLipSync } from '@proj-airi/model-driver-lipsync'
|
||||
import { wlipsyncProfile } from '@proj-airi/model-driver-lipsync/shared/wlipsync'
|
||||
import { createPlaybackManager, createSpeechPipeline, normalizeActPayload } from '@proj-airi/pipelines-audio'
|
||||
import { Live2DScene, useLive2d } from '@proj-airi/stage-ui-live2d'
|
||||
import { Live2DScene, useLive2dParams } from '@proj-airi/stage-ui-live2d'
|
||||
import { SpineScene } from '@proj-airi/stage-ui-spine'
|
||||
import { ThreeScene } from '@proj-airi/stage-ui-three'
|
||||
import { animations } from '@proj-airi/stage-ui-three/assets/vrm'
|
||||
@@ -25,6 +25,7 @@ import { generateSpeech } from '@xsai/generate-speech'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
|
||||
import { useSettingsLive2d } from '../../../../stage-ui-live2d/src/composables/live2d/live2d'
|
||||
import { useAuthProviderSync } from '../../composables/use-auth-provider-sync'
|
||||
import { useDuckDb } from '../../composables/use-duck-db'
|
||||
import { useIOTraceBridge } from '../../composables/use-io-trace-bridge'
|
||||
@@ -45,7 +46,6 @@ import { useSpeechRuntimeStore } from '../../stores/speech-runtime'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
paused?: boolean
|
||||
focusAt: { x: number, y: number }
|
||||
}>(), { paused: false, scale: 1 })
|
||||
|
||||
const componentState = defineModel<'pending' | 'loading' | 'mounted'>('state', { default: 'pending' })
|
||||
@@ -61,18 +61,18 @@ const settingsStore = useSettings()
|
||||
const {
|
||||
stageModelRenderer,
|
||||
stageViewControlsEnabled,
|
||||
live2dDisableFocus,
|
||||
stageModelSelectedUrl,
|
||||
stageModelSelected,
|
||||
themeColorsHue,
|
||||
themeColorsHueDynamic,
|
||||
live2dIdleAnimationEnabled,
|
||||
live2dAutoBlinkEnabled,
|
||||
live2dForceAutoBlinkEnabled,
|
||||
live2dExpressionEnabled,
|
||||
|
||||
} = storeToRefs(settingsStore)
|
||||
const {
|
||||
live2dShadowEnabled,
|
||||
live2dMaxFps,
|
||||
live2dRenderScale,
|
||||
} = storeToRefs(useSettingsLive2d())
|
||||
const {
|
||||
spinePremultipliedAlpha,
|
||||
spineDefaultMixDuration,
|
||||
spineIdleAnimationEnabled,
|
||||
@@ -91,7 +91,7 @@ const chatHookCleanups: Array<() => void> = []
|
||||
|
||||
const providersStore = useProvidersStore()
|
||||
useAuthProviderSync()
|
||||
const live2dStore = useLive2d()
|
||||
const live2dStore = useLive2dParams()
|
||||
const showStage = ref(true)
|
||||
const viewUpdateCleanups: Array<() => void> = []
|
||||
|
||||
@@ -129,7 +129,7 @@ const speechRuntimeStore = useSpeechRuntimeStore()
|
||||
const backgroundStore = useBackgroundStore()
|
||||
const { activeBackgroundUrl } = storeToRefs(backgroundStore)
|
||||
|
||||
const { currentMotion } = storeToRefs(useLive2d())
|
||||
const { currentMotion } = storeToRefs(useLive2dParams())
|
||||
|
||||
const emotionsQueue = createQueue<EmotionPayload>({
|
||||
handlers: [
|
||||
@@ -867,17 +867,11 @@ defineExpose({
|
||||
h-full w-full flex-1
|
||||
:model-src="stageModelSelectedUrl"
|
||||
:model-id="stageModelSelected"
|
||||
:focus-at="focusAt"
|
||||
:mouth-open-size="mouthOpenSize"
|
||||
:now-speaking="nowSpeaking"
|
||||
:paused="paused"
|
||||
:disable-focus-at="live2dDisableFocus"
|
||||
:theme-colors-hue="themeColorsHue"
|
||||
:theme-colors-hue-dynamic="themeColorsHueDynamic"
|
||||
:live2d-idle-animation-enabled="live2dIdleAnimationEnabled"
|
||||
:live2d-auto-blink-enabled="live2dAutoBlinkEnabled"
|
||||
:live2d-force-auto-blink-enabled="live2dForceAutoBlinkEnabled"
|
||||
:live2d-expression-enabled="live2dExpressionEnabled"
|
||||
:live2d-shadow-enabled="live2dShadowEnabled"
|
||||
:live2d-max-fps="live2dMaxFps"
|
||||
:live2d-render-scale="live2dRenderScale"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ChatSessionsExport } from '../types/chat-session'
|
||||
|
||||
import { isStageTamagotchi } from '@proj-airi/stage-shared'
|
||||
import { useLive2d } from '@proj-airi/stage-ui-live2d'
|
||||
import { useLive2dParams, useSettingsLive2d } from '@proj-airi/stage-ui-live2d'
|
||||
import { useModelStore } from '@proj-airi/stage-ui-three'
|
||||
|
||||
import { useChatOrchestratorStore } from '../stores/chat'
|
||||
@@ -27,7 +27,8 @@ export function useDataMaintenance() {
|
||||
const providersStore = useProvidersStore()
|
||||
const settingsStore = useSettings()
|
||||
const audioSettingsStore = useSettingsAudioDevice()
|
||||
const live2dStore = useLive2d()
|
||||
const live2dParamsStore = useLive2dParams()
|
||||
const live2dSettingsStore = useSettingsLive2d()
|
||||
const threeStore = useModelStore()
|
||||
const hearingStore = useHearingStore()
|
||||
const speechStore = useSpeechStore()
|
||||
@@ -85,7 +86,8 @@ export function useDataMaintenance() {
|
||||
async function resetSettingsState() {
|
||||
await settingsStore.resetState()
|
||||
audioSettingsStore.resetState()
|
||||
live2dStore.resetState()
|
||||
live2dParamsStore.resetState()
|
||||
live2dSettingsStore.resetState()
|
||||
threeStore.resetModelStore()
|
||||
mcpStore.resetState()
|
||||
onboardingStore.resetSetupState()
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useSettingsAnalytics } from './analytics'
|
||||
import { useSettingsControlsIsland } from './controls-island'
|
||||
import { useSettingsDeveloper } from './developer'
|
||||
import { useSettingsGeneral } from './general'
|
||||
import { useSettingsLive2d } from './live2d'
|
||||
import { useSettingsSpine } from './spine'
|
||||
import { useSettingsStageModel } from './stage-model'
|
||||
import { useSettingsTheme } from './theme'
|
||||
@@ -16,7 +15,6 @@ export * from './beat-sync'
|
||||
export * from './controls-island'
|
||||
export * from './developer'
|
||||
export * from './general'
|
||||
export * from './live2d'
|
||||
export * from './spine'
|
||||
export * from './stage-model'
|
||||
export * from './theme'
|
||||
@@ -34,7 +32,6 @@ export const useSettings = defineStore('settings', () => {
|
||||
const general = useSettingsGeneral()
|
||||
const analytics = useSettingsAnalytics()
|
||||
const stageModel = useSettingsStageModel()
|
||||
const live2d = useSettingsLive2d()
|
||||
const spine = useSettingsSpine()
|
||||
const theme = useSettingsTheme()
|
||||
const controlsIsland = useSettingsControlsIsland()
|
||||
@@ -44,7 +41,6 @@ export const useSettings = defineStore('settings', () => {
|
||||
await stageModel.resetState()
|
||||
analytics.resetState()
|
||||
general.resetState()
|
||||
live2d.resetState()
|
||||
spine.resetState()
|
||||
theme.resetState()
|
||||
controlsIsland.resetState()
|
||||
@@ -55,7 +51,6 @@ export const useSettings = defineStore('settings', () => {
|
||||
const generalRefs = storeToRefs(general)
|
||||
const analyticsRefs = storeToRefs(analytics)
|
||||
const stageModelRefs = storeToRefs(stageModel)
|
||||
const live2dRefs = storeToRefs(live2d)
|
||||
const spineRefs = storeToRefs(spine)
|
||||
const themeRefs = storeToRefs(theme)
|
||||
const controlsIslandRefs = storeToRefs(controlsIsland)
|
||||
@@ -76,16 +71,6 @@ export const useSettings = defineStore('settings', () => {
|
||||
stageModelSelectedDisplayModel: stageModelRefs.stageModelSelectedDisplayModel,
|
||||
stageViewControlsEnabled: stageModelRefs.stageViewControlsEnabled,
|
||||
|
||||
// Live2D settings
|
||||
live2dDisableFocus: live2dRefs.live2dDisableFocus,
|
||||
live2dIdleAnimationEnabled: live2dRefs.live2dIdleAnimationEnabled,
|
||||
live2dAutoBlinkEnabled: live2dRefs.live2dAutoBlinkEnabled,
|
||||
live2dForceAutoBlinkEnabled: live2dRefs.live2dForceAutoBlinkEnabled,
|
||||
live2dExpressionEnabled: live2dRefs.live2dExpressionEnabled,
|
||||
live2dShadowEnabled: live2dRefs.live2dShadowEnabled,
|
||||
live2dMaxFps: live2dRefs.live2dMaxFps,
|
||||
live2dRenderScale: live2dRefs.live2dRenderScale,
|
||||
|
||||
// Spine settings
|
||||
spinePremultipliedAlpha: spineRefs.spinePremultipliedAlpha,
|
||||
spineDefaultMixDuration: spineRefs.spineDefaultMixDuration,
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import { useLocalStorageManualReset, useVersionedLocalStorageManualReset } from '@proj-airi/stage-shared/composables'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useSettingsLive2d = defineStore('settings-live2d', () => {
|
||||
const live2dDisableFocus = useLocalStorageManualReset<boolean>('settings/live2d/disable-focus', false)
|
||||
const live2dIdleAnimationEnabled = useLocalStorageManualReset<boolean>('settings/live2d/idle-animation-enabled', true)
|
||||
const live2dAutoBlinkEnabled = useVersionedLocalStorageManualReset<boolean>('settings/live2d/auto-blink-enabled', false, {
|
||||
defaultVersion: '2.0.0',
|
||||
satisfiesVersionBy(beforeVersion, afterVersion) {
|
||||
if (beforeVersion === afterVersion) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
})
|
||||
const live2dForceAutoBlinkEnabled = useVersionedLocalStorageManualReset<boolean>('settings/live2d/force-auto-blink-enabled', true, {
|
||||
defaultVersion: '2.0.0',
|
||||
satisfiesVersionBy(beforeVersion, afterVersion) {
|
||||
if (beforeVersion === afterVersion) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
})
|
||||
const live2dExpressionEnabled = useLocalStorageManualReset<boolean>('settings/live2d/expression-enabled', false)
|
||||
const live2dShadowEnabled = useLocalStorageManualReset<boolean>('settings/live2d/shadow-enabled', true)
|
||||
const live2dMaxFps = useLocalStorageManualReset<number>('settings/live2d/max-fps', 0)
|
||||
const live2dRenderScale = useLocalStorageManualReset<number>('settings/live2d/render-scale', 2)
|
||||
|
||||
function resetState() {
|
||||
live2dDisableFocus.reset()
|
||||
live2dIdleAnimationEnabled.reset()
|
||||
live2dAutoBlinkEnabled.reset()
|
||||
live2dForceAutoBlinkEnabled.reset()
|
||||
live2dExpressionEnabled.reset()
|
||||
live2dShadowEnabled.reset()
|
||||
live2dMaxFps.reset()
|
||||
live2dRenderScale.reset()
|
||||
}
|
||||
|
||||
return {
|
||||
live2dDisableFocus,
|
||||
live2dIdleAnimationEnabled,
|
||||
live2dAutoBlinkEnabled,
|
||||
live2dForceAutoBlinkEnabled,
|
||||
live2dExpressionEnabled,
|
||||
live2dShadowEnabled,
|
||||
live2dMaxFps,
|
||||
live2dRenderScale,
|
||||
resetState,
|
||||
}
|
||||
})
|
||||
@@ -63,6 +63,10 @@ onMounted(async () => {
|
||||
|
||||
containerElementBounding.update()
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
containerRef,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
Generated
+239
-24
@@ -1477,7 +1477,7 @@ importers:
|
||||
version: 3.0.2(electron@41.2.1)
|
||||
'@electron-toolkit/tsconfig':
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0(@types/node@25.6.0)
|
||||
version: 2.0.0(@types/node@24.12.2)
|
||||
'@electron-toolkit/utils':
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0(electron@41.2.1)
|
||||
@@ -1516,7 +1516,7 @@ importers:
|
||||
version: 3.1.0
|
||||
'@intlify/unplugin-vue-i18n':
|
||||
specifier: ^11.0.7
|
||||
version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
|
||||
version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
|
||||
'@modelcontextprotocol/sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6)
|
||||
@@ -1552,10 +1552,10 @@ importers:
|
||||
version: link:../../packages/ui-transitions
|
||||
'@proj-airi/unplugin-fetch':
|
||||
specifier: 'catalog:'
|
||||
version: 0.2.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
version: 0.2.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
'@proj-airi/unplugin-live2d-sdk':
|
||||
specifier: ^0.1.7
|
||||
version: 0.1.7(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
version: 0.1.7(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
'@types/audioworklet':
|
||||
specifier: 'catalog:'
|
||||
version: 0.0.97
|
||||
@@ -1582,7 +1582,7 @@ importers:
|
||||
version: 2.10.3
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^6.0.6
|
||||
version: 6.0.6(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))
|
||||
version: 6.0.6(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/volar':
|
||||
specifier: ^3.1.2
|
||||
version: 3.1.2(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3))
|
||||
@@ -1615,7 +1615,7 @@ importers:
|
||||
version: 6.8.3
|
||||
electron-vite:
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
version: 5.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
get-port-please:
|
||||
specifier: 'catalog:'
|
||||
version: 3.2.0
|
||||
@@ -1636,31 +1636,31 @@ importers:
|
||||
version: 2.2.6
|
||||
unocss-preset-scrollbar:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)))
|
||||
version: 4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)))
|
||||
unplugin-info:
|
||||
specifier: ^1.3.2
|
||||
version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
unplugin-yaml:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0(@nuxt/kit@3.20.2(magicast@0.5.2))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
version: 4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
vite:
|
||||
specifier: 'catalog:'
|
||||
version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
version: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
vite-bundle-visualizer:
|
||||
specifier: ^1.2.1
|
||||
version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@4.60.1)
|
||||
vite-plugin-mkcert:
|
||||
specifier: 'catalog:'
|
||||
version: 2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
version: 2.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
vite-plugin-vue-devtools:
|
||||
specifier: ^8.1.1
|
||||
version: 8.1.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))
|
||||
version: 8.1.1(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))
|
||||
vite-plugin-vue-layouts:
|
||||
specifier: ^0.11.0
|
||||
version: 0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
|
||||
version: 0.11.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
|
||||
vue-macros:
|
||||
specifier: ^3.1.2
|
||||
version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3))
|
||||
version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3))
|
||||
vue-tsc:
|
||||
specifier: ^3.2.6
|
||||
version: 3.2.6(typescript@5.9.3)
|
||||
@@ -1736,6 +1736,9 @@ importers:
|
||||
'@proj-airi/stage-ui':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/stage-ui
|
||||
'@proj-airi/stage-ui-live2d':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/stage-ui-live2d
|
||||
'@proj-airi/stage-ui-three':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/stage-ui-three
|
||||
@@ -19957,9 +19960,9 @@ snapshots:
|
||||
dependencies:
|
||||
electron: 41.2.1
|
||||
|
||||
'@electron-toolkit/tsconfig@2.0.0(@types/node@25.6.0)':
|
||||
'@electron-toolkit/tsconfig@2.0.0(@types/node@24.12.2)':
|
||||
dependencies:
|
||||
'@types/node': 25.6.0
|
||||
'@types/node': 24.12.2
|
||||
|
||||
'@electron-toolkit/utils@4.0.0(electron@41.2.1)':
|
||||
dependencies:
|
||||
@@ -20871,6 +20874,31 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1))
|
||||
'@intlify/bundle-utils': 11.0.7(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))
|
||||
'@intlify/shared': 11.3.2
|
||||
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.3.2)(@vue/compiler-dom@3.5.32)(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
|
||||
'@rollup/pluginutils': 5.3.0(rollup@4.60.1)
|
||||
'@typescript-eslint/scope-manager': 8.58.1
|
||||
'@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3)
|
||||
debug: 4.4.3(supports-color@10.2.2)
|
||||
fast-glob: 3.3.3
|
||||
pathe: 2.0.3
|
||||
picocolors: 1.1.1
|
||||
unplugin: 2.3.11
|
||||
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
vue: 3.5.32(typescript@5.9.3)
|
||||
optionalDependencies:
|
||||
vue-i18n: 11.3.2(vue@3.5.32(typescript@5.9.3))
|
||||
transitivePeerDependencies:
|
||||
- '@vue/compiler-dom'
|
||||
- eslint
|
||||
- rollup
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1))
|
||||
@@ -22852,11 +22880,35 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- magicast
|
||||
|
||||
'@proj-airi/unplugin-fetch@0.2.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
|
||||
dependencies:
|
||||
ofetch: 1.5.1
|
||||
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
|
||||
'@proj-airi/unplugin-fetch@0.2.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
|
||||
dependencies:
|
||||
ofetch: 1.5.1
|
||||
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
|
||||
'@proj-airi/unplugin-live2d-sdk@0.1.7(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)':
|
||||
dependencies:
|
||||
ofetch: 1.5.1
|
||||
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
yauzl: 3.3.0
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- '@vitejs/devtools'
|
||||
- esbuild
|
||||
- jiti
|
||||
- less
|
||||
- sass
|
||||
- sass-embedded
|
||||
- stylus
|
||||
- sugarss
|
||||
- terser
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
'@proj-airi/unplugin-live2d-sdk@0.1.7(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)':
|
||||
dependencies:
|
||||
ofetch: 1.5.1
|
||||
@@ -23757,6 +23809,7 @@ snapshots:
|
||||
'@types/node@25.6.0':
|
||||
dependencies:
|
||||
undici-types: 7.19.2
|
||||
optional: true
|
||||
|
||||
'@types/nprogress@0.2.3': {}
|
||||
|
||||
@@ -24422,6 +24475,12 @@ snapshots:
|
||||
vite: 6.4.2(@types/node@25.6.0)(jiti@2.6.1)(less@4.6.4)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
vue: 3.5.32(typescript@5.9.3)
|
||||
|
||||
'@vitejs/plugin-vue@6.0.6(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@rolldown/pluginutils': 1.0.0-rc.13
|
||||
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
vue: 3.5.32(typescript@5.9.3)
|
||||
|
||||
'@vitejs/plugin-vue@6.0.6(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@rolldown/pluginutils': 1.0.0-rc.13
|
||||
@@ -24724,6 +24783,15 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- vue
|
||||
|
||||
'@vue-macros/devtools@3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
|
||||
dependencies:
|
||||
sirv: 3.0.2
|
||||
vue: 3.5.32(typescript@5.9.3)
|
||||
optionalDependencies:
|
||||
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
'@vue-macros/devtools@3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
|
||||
dependencies:
|
||||
sirv: 3.0.2
|
||||
@@ -26865,7 +26933,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
electron-vite@5.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
electron-vite@5.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
'@babel/core': 7.29.0
|
||||
'@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0)
|
||||
@@ -26873,7 +26941,7 @@ snapshots:
|
||||
esbuild: 0.25.12
|
||||
magic-string: 0.30.21
|
||||
picocolors: 1.1.1
|
||||
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -32789,7 +32857,8 @@ snapshots:
|
||||
|
||||
undici-types@7.16.0: {}
|
||||
|
||||
undici-types@7.19.2: {}
|
||||
undici-types@7.19.2:
|
||||
optional: true
|
||||
|
||||
undici@6.24.1: {}
|
||||
|
||||
@@ -32901,11 +32970,6 @@ snapshots:
|
||||
'@unocss/preset-mini': 66.6.8
|
||||
unocss: 66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
|
||||
unocss-preset-scrollbar@4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))):
|
||||
dependencies:
|
||||
'@unocss/preset-mini': 66.6.8
|
||||
unocss: 66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
|
||||
unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@6.4.2(@types/node@25.6.0)(jiti@2.6.1)(less@4.6.4)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
'@unocss/cli': 66.6.8
|
||||
@@ -33002,6 +33066,14 @@ snapshots:
|
||||
unplugin: 2.3.11
|
||||
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
|
||||
unplugin-combine@2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
optionalDependencies:
|
||||
esbuild: 0.27.2
|
||||
rolldown: 1.0.0-rc.16
|
||||
rollup: 4.60.1
|
||||
unplugin: 2.3.11
|
||||
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
|
||||
unplugin-combine@2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
optionalDependencies:
|
||||
esbuild: 0.27.2
|
||||
@@ -33036,6 +33108,19 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
unplugin-info@1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
ci-info: 4.4.0
|
||||
git-url-parse: 16.1.0
|
||||
simple-git: 3.36.0
|
||||
unplugin: 2.3.11
|
||||
optionalDependencies:
|
||||
esbuild: 0.27.2
|
||||
rollup: 4.60.1
|
||||
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
unplugin-info@1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
ci-info: 4.4.0
|
||||
@@ -33145,6 +33230,17 @@ snapshots:
|
||||
rollup: 4.60.1
|
||||
vite: 6.4.2(@types/node@25.6.0)(jiti@2.6.1)(less@4.6.4)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
|
||||
unplugin-yaml@4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.3.0(rollup@4.60.1)
|
||||
unplugin: 3.0.0
|
||||
yaml: 2.8.3
|
||||
optionalDependencies:
|
||||
esbuild: 0.27.2
|
||||
rolldown: 1.0.0-rc.16
|
||||
rollup: 4.60.1
|
||||
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
|
||||
unplugin@2.3.11:
|
||||
dependencies:
|
||||
'@jridgewell/remapping': 2.3.5
|
||||
@@ -33376,12 +33472,22 @@ snapshots:
|
||||
- rollup
|
||||
- supports-color
|
||||
|
||||
vite-dev-rpc@1.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
birpc: 2.9.0
|
||||
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
vite-hot-client: 2.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
|
||||
vite-dev-rpc@1.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
birpc: 2.9.0
|
||||
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
vite-hot-client: 2.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
|
||||
vite-hot-client@2.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
|
||||
vite-hot-client@2.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
@@ -33427,6 +33533,21 @@ snapshots:
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vite-plugin-inspect@11.3.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
ansis: 4.2.0
|
||||
debug: 4.4.3(supports-color@10.2.2)
|
||||
error-stack-parser-es: 1.0.5
|
||||
ohash: 2.0.11
|
||||
open: 10.2.0
|
||||
perfect-debounce: 2.1.0
|
||||
sirv: 3.0.2
|
||||
unplugin-utils: 0.3.1
|
||||
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
vite-dev-rpc: 1.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vite-plugin-inspect@11.3.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
ansis: 4.2.0
|
||||
@@ -33458,6 +33579,13 @@ snapshots:
|
||||
- typescript
|
||||
- ws
|
||||
|
||||
vite-plugin-mkcert@2.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
debug: 4.4.3(supports-color@10.2.2)
|
||||
supports-color: 10.2.2
|
||||
undici: 8.1.0
|
||||
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
|
||||
vite-plugin-mkcert@2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
debug: 4.4.3(supports-color@10.2.2)
|
||||
@@ -33476,6 +33604,20 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vite-plugin-vue-devtools@8.1.1(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)):
|
||||
dependencies:
|
||||
'@vue/devtools-core': 8.1.1(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue/devtools-kit': 8.1.1
|
||||
'@vue/devtools-shared': 8.1.1
|
||||
sirv: 3.0.2
|
||||
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
vite-plugin-inspect: 11.3.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
vite-plugin-vue-inspector: 5.3.2(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
transitivePeerDependencies:
|
||||
- '@nuxt/kit'
|
||||
- supports-color
|
||||
- vue
|
||||
|
||||
vite-plugin-vue-devtools@8.1.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)):
|
||||
dependencies:
|
||||
'@vue/devtools-core': 8.1.1(vue@3.5.32(typescript@5.9.3))
|
||||
@@ -33490,6 +33632,21 @@ snapshots:
|
||||
- supports-color
|
||||
- vue
|
||||
|
||||
vite-plugin-vue-inspector@5.3.2(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
'@babel/core': 7.29.0
|
||||
'@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.29.0)
|
||||
'@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0)
|
||||
'@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0)
|
||||
'@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.29.0)
|
||||
'@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.29.0)
|
||||
'@vue/compiler-dom': 3.5.32
|
||||
kolorist: 1.8.0
|
||||
magic-string: 0.30.21
|
||||
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vite-plugin-vue-inspector@5.3.2(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
'@babel/core': 7.29.0
|
||||
@@ -33505,6 +33662,16 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vite-plugin-vue-layouts@0.11.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)):
|
||||
dependencies:
|
||||
debug: 4.4.3(supports-color@10.2.2)
|
||||
fast-glob: 3.3.3
|
||||
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
vue: 3.5.32(typescript@5.9.3)
|
||||
vue-router: 5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vite-plugin-vue-layouts@0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)):
|
||||
dependencies:
|
||||
debug: 4.4.3(supports-color@10.2.2)
|
||||
@@ -33771,6 +33938,54 @@ snapshots:
|
||||
- vue-tsc
|
||||
- webpack
|
||||
|
||||
vue-macros@3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)):
|
||||
dependencies:
|
||||
'@vue-macros/better-define': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/boolean-prop': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/chain-call': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/common': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/config': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/define-emit': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/define-models': 3.1.2(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/define-prop': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/define-props': 3.1.2(@vue-macros/reactivity-transform@3.1.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/define-props-refs': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/define-render': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/define-slots': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/define-stylex': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/devtools': 3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
'@vue-macros/export-expose': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/export-props': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/export-render': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/hoist-static': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/jsx-directive': 3.1.2(typescript@5.9.3)
|
||||
'@vue-macros/named-template': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/reactivity-transform': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/script-lang': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/setup-block': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/setup-component': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/setup-sfc': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/short-bind': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/short-emits': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/short-vmodel': 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
'@vue-macros/volar': 3.1.2(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3))
|
||||
unplugin: 2.3.11
|
||||
unplugin-combine: 2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
unplugin-vue-define-options: 3.1.2(vue@3.5.32(typescript@5.9.3))
|
||||
vue: 3.5.32(typescript@5.9.3)
|
||||
transitivePeerDependencies:
|
||||
- '@emnapi/core'
|
||||
- '@emnapi/runtime'
|
||||
- '@rspack/core'
|
||||
- '@vueuse/core'
|
||||
- esbuild
|
||||
- rolldown
|
||||
- rollup
|
||||
- typescript
|
||||
- vite
|
||||
- vue-tsc
|
||||
- webpack
|
||||
|
||||
vue-macros@3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)):
|
||||
dependencies:
|
||||
'@vue-macros/better-define': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3))
|
||||
|
||||
Reference in New Issue
Block a user