diff --git a/packages/stage-pages/src/pages/settings/connection/ConnectionSettings.vue b/packages/stage-pages/src/pages/settings/connection/ConnectionSettings.vue
index b8192b78d..f85d30bc3 100644
--- a/packages/stage-pages/src/pages/settings/connection/ConnectionSettings.vue
+++ b/packages/stage-pages/src/pages/settings/connection/ConnectionSettings.vue
@@ -1,12 +1,12 @@
diff --git a/packages/stage-shared/src/composables/index.ts b/packages/stage-shared/src/composables/index.ts
new file mode 100644
index 000000000..7c880de94
--- /dev/null
+++ b/packages/stage-shared/src/composables/index.ts
@@ -0,0 +1 @@
+export * from './use-local-storage-manual-reset'
diff --git a/packages/stage-shared/src/composables/use-local-storage-manual-reset.ts b/packages/stage-shared/src/composables/use-local-storage-manual-reset.ts
new file mode 100644
index 000000000..339fecef5
--- /dev/null
+++ b/packages/stage-shared/src/composables/use-local-storage-manual-reset.ts
@@ -0,0 +1,20 @@
+import type { ManualResetRefReturn, UseStorageOptions } from '@vueuse/core'
+import type { MaybeRefOrGetter, WatchOptions } from 'vue'
+
+import { refManualReset, useLocalStorage } from '@vueuse/core'
+import { unref, watch } from 'vue'
+
+export function useLocalStorageManualReset(key: MaybeRefOrGetter, initialValue: MaybeRefOrGetter, options?: UseStorageOptions & WatchOptions): ManualResetRefReturn {
+ const value = unref(initialValue)
+ const localStorageState = useLocalStorage(key, value, options)
+ const state = refManualReset(localStorageState)
+
+ const { resume, pause } = watch(state, newValue => localStorageState.value = newValue, options)
+ watch(localStorageState, (newValue) => {
+ pause()
+ state.value = newValue
+ resume()
+ }, options)
+
+ return state
+}
diff --git a/packages/stage-ui-live2d/src/components/scenes/Live2D.vue b/packages/stage-ui-live2d/src/components/scenes/Live2D.vue
index 3b9076489..ba6df516f 100644
--- a/packages/stage-ui-live2d/src/components/scenes/Live2D.vue
+++ b/packages/stage-ui-live2d/src/components/scenes/Live2D.vue
@@ -40,17 +40,13 @@ withDefaults(defineProps<{
})
const componentState = defineModel<'pending' | 'loading' | 'mounted'>('state', { default: 'pending' })
-
const componentStateCanvas = defineModel<'pending' | 'loading' | 'mounted'>('canvasState', { default: 'pending' })
const componentStateModel = defineModel<'pending' | 'loading' | 'mounted'>('modelState', { default: 'pending' })
const live2dCanvasRef = ref>()
const live2d = useLive2d()
-const {
- scale,
- position,
-} = storeToRefs(live2d)
+const { scale, position } = storeToRefs(live2d)
watch([componentStateModel, componentStateCanvas], () => {
componentState.value = (componentStateModel.value === 'mounted' && componentStateCanvas.value === 'mounted')
diff --git a/packages/stage-ui-live2d/src/stores/live2d.ts b/packages/stage-ui-live2d/src/stores/live2d.ts
index ccd75de04..2278115e4 100644
--- a/packages/stage-ui-live2d/src/stores/live2d.ts
+++ b/packages/stage-ui-live2d/src/stores/live2d.ts
@@ -1,4 +1,5 @@
-import { refManualReset, useBroadcastChannel, useLocalStorage } from '@vueuse/core'
+import { useLocalStorageManualReset } from '@proj-airi/stage-shared/composables'
+import { useBroadcastChannel } from '@vueuse/core'
import { defineStore } from 'pinia'
import { computed, ref, watch } from 'vue'
@@ -56,18 +57,18 @@ export const useLive2d = defineStore('live2d', () => {
}
})
- const position = refManualReset<{ x: number, y: number }>(useLocalStorage('settings/live2d/position', { x: 0, y: 0 })) // position is relative to the center of the screen, units are %
+ const position = useLocalStorageManualReset<{ x: number, y: number }>('settings/live2d/position', { x: 0, y: 0 }) // position is relative to the center of the screen, units are %
const positionInPercentageString = computed(() => ({
x: `${position.value.x}%`,
y: `${position.value.y}%`,
}))
- const currentMotion = refManualReset<{ group: string, index?: number }>(() => ({ group: 'Idle', index: 0 }))
- const availableMotions = refManualReset<{ motionName: string, motionIndex: number, fileName: string }[]>(() => [])
- const motionMap = refManualReset>(useLocalStorage('settings/live2d/motion-map', {}))
- const scale = refManualReset(useLocalStorage('settings/live2d/scale', 1))
+ const currentMotion = useLocalStorageManualReset<{ group: string, index?: number }>('settings/live2d/current-motion', () => ({ group: 'Idle', index: 0 }))
+ const availableMotions = useLocalStorageManualReset<{ motionName: string, motionIndex: number, fileName: string }[]>('settings/live2d/available-motions', () => [])
+ const motionMap = useLocalStorageManualReset>('settings/live2d/motion-map', {})
+ const scale = useLocalStorageManualReset('settings/live2d/scale', 1)
// Live2D model parameters
- const modelParameters = refManualReset>(useLocalStorage('settings/live2d/parameters', defaultModelParameters))
+ const modelParameters = useLocalStorageManualReset>('settings/live2d/parameters', defaultModelParameters)
function resetState() {
position.reset()
diff --git a/packages/stage-ui/src/components/scenarios/settings/model-settings/index.vue b/packages/stage-ui/src/components/scenarios/settings/model-settings/index.vue
index 8cb602da9..09a6f2c05 100644
--- a/packages/stage-ui/src/components/scenarios/settings/model-settings/index.vue
+++ b/packages/stage-ui/src/components/scenarios/settings/model-settings/index.vue
@@ -85,8 +85,16 @@ watch(selectedModel, async () => {
Select Model
-
-
+
+
diff --git a/packages/stage-ui/src/components/scenarios/settings/model-settings/live2d.vue b/packages/stage-ui/src/components/scenarios/settings/model-settings/live2d.vue
index ae498d9f8..5fc576e31 100644
--- a/packages/stage-ui/src/components/scenarios/settings/model-settings/live2d.vue
+++ b/packages/stage-ui/src/components/scenarios/settings/model-settings/live2d.vue
@@ -19,7 +19,13 @@ defineEmits<{
const { t } = useI18n()
const settings = useSettings()
-const { live2dDisableFocus, live2dIdleAnimationEnabled, live2dAutoBlinkEnabled, live2dForceAutoBlinkEnabled, live2dShadowEnabled } = storeToRefs(settings)
+const {
+ live2dDisableFocus,
+ live2dIdleAnimationEnabled,
+ live2dAutoBlinkEnabled,
+ live2dForceAutoBlinkEnabled,
+ live2dShadowEnabled,
+} = storeToRefs(settings)
const live2d = useLive2d()
const {
diff --git a/packages/stage-ui/src/components/scenes/Stage.vue b/packages/stage-ui/src/components/scenes/Stage.vue
index adef48467..a9270199a 100644
--- a/packages/stage-ui/src/components/scenes/Stage.vue
+++ b/packages/stage-ui/src/components/scenes/Stage.vue
@@ -527,8 +527,9 @@ defineExpose({
{
- const serverCmd = refManualReset(useLocalStorage('settings/mcp/server-cmd', ''))
- const serverArgs = refManualReset(useLocalStorage('settings/mcp/server-args', ''))
- const connected = refManualReset(useLocalStorage('mcp/connected', false)) // use local storage to sync between windows
+ const serverCmd = useLocalStorageManualReset('settings/mcp/server-cmd', '')
+ const serverArgs = useLocalStorageManualReset('settings/mcp/server-args', '')
+ const connected = useLocalStorageManualReset('mcp/connected', false) // use local storage to sync between windows
function resetState() {
serverCmd.reset()
diff --git a/packages/stage-ui/src/stores/modules/airi-card.ts b/packages/stage-ui/src/stores/modules/airi-card.ts
index caf5edc83..2308774b3 100644
--- a/packages/stage-ui/src/stores/modules/airi-card.ts
+++ b/packages/stage-ui/src/stores/modules/airi-card.ts
@@ -1,6 +1,6 @@
import type { Card, ccv3 } from '@proj-airi/ccc'
-import { refManualReset, useLocalStorage } from '@vueuse/core'
+import { useLocalStorageManualReset } from '@proj-airi/stage-shared/composables'
import { nanoid } from 'nanoid'
import { defineStore, storeToRefs } from 'pinia'
import { computed, watch } from 'vue'
@@ -57,8 +57,8 @@ export interface AiriCard extends Card {
export const useAiriCardStore = defineStore('airi-card', () => {
const { t } = useI18n()
- const cards = refManualReset