Skip to content

Commit 0e543b5

Browse files
authored
feat(stage-tamagotchi): add icon size settings for tamagotchi (#940)
1 parent bcafcac commit 0e543b5

File tree

6 files changed

+133
-63
lines changed

6 files changed

+133
-63
lines changed

apps/stage-tamagotchi/src/renderer/components/stage-islands/controls-island/index.vue

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup lang="ts">
22
import { defineInvoke } from '@moeru/eventa'
3-
import { useSettingsAudioDevice } from '@proj-airi/stage-ui/stores/settings'
3+
import { useSettings, useSettingsAudioDevice } from '@proj-airi/stage-ui/stores/settings'
44
import { useTheme } from '@proj-airi/ui'
55
import { useWindowSize } from '@vueuse/core'
66
import { storeToRefs } from 'pinia'
@@ -21,8 +21,10 @@ const { isDark, toggleDark } = useTheme()
2121
const { t } = useI18n()
2222
2323
const settingsAudioDeviceStore = useSettingsAudioDevice()
24+
const settingsStore = useSettings()
2425
const context = useElectronEventaContext()
2526
const { enabled } = storeToRefs(settingsAudioDeviceStore)
27+
const { controlsIslandIconSize } = storeToRefs(settingsStore)
2628
const openSettings = useElectronEventaInvoke(electronOpenSettings)
2729
const openChat = useElectronEventaInvoke(electronOpenChat)
2830
@@ -35,7 +37,22 @@ const LARGE_THRESHOLD = ICON_PLACEHOLDER_PX * BUTTON_COUNT
3537
3638
// Grouped classes for icon / border / padding and combined style class
3739
const adjustStyleClasses = computed(() => {
38-
const isLarge = windowHeight.value > LARGE_THRESHOLD
40+
let isLarge: boolean
41+
42+
// Determine size based on setting
43+
switch (controlsIslandIconSize.value) {
44+
case 'large':
45+
isLarge = true
46+
break
47+
case 'small':
48+
isLarge = false
49+
break
50+
case 'auto':
51+
default:
52+
isLarge = windowHeight.value > LARGE_THRESHOLD
53+
break
54+
}
55+
3956
const icon = isLarge ? 'size-5' : 'size-3'
4057
const border = isLarge ? 'border-2' : 'border-0'
4158
const padding = isLarge ? 'p-2' : 'p-0.5'

packages/i18n/src/locales/en/settings.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ language:
3737
description: >
3838
Change the language of the AIRI interface. This will not affect the language
3939
of the character's responses.
40+
controls-island:
41+
icon-size:
42+
title: Controls Island Icon Size
43+
description: Adjust the size of the controls island icons
44+
auto: Auto (responsive)
45+
large: Large (default)
46+
small: Small
4047
live2d:
4148
change-model:
4249
from-file: Load from File

packages/i18n/src/locales/zh-Hans/settings.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ language:
3434
title: 语言
3535
description: >
3636
切换显示界面的语言
37+
controls-island:
38+
icon-size:
39+
title: 侧边控制栏图标大小
40+
description: 调整侧边控制栏图标的大小
41+
auto: 自动(响应式)
42+
large: 默认
43+
small: 小图标
3744
live2d:
3845
change-model:
3946
from-file: 从文件加载

packages/stage-pages/src/pages/settings/system/general.vue

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ import { FieldCheckbox, FieldSelect, useTheme } from '@proj-airi/ui'
55
import { computed } from 'vue'
66
import { useI18n } from 'vue-i18n'
77
8+
const props = withDefaults(defineProps<{
9+
needscontrolsIslandIconSizeSetting?: boolean
10+
}>(), {
11+
needscontrolsIslandIconSizeSetting: import.meta.env.RUNTIME_ENVIRONMENT === 'electron',
12+
})
13+
14+
const showControlsIsland = computed(() => props.needscontrolsIslandIconSizeSetting)
15+
816
const settings = useSettings()
917
1018
const { t } = useI18n()
@@ -40,7 +48,26 @@ const languages = computed(() => {
4048
transition="all ease-in-out duration-250"
4149
:label="t('settings.language.title')"
4250
:description="t('settings.language.description')"
43-
:options="languages"
51+
:options="languages as Array<{ value: string; label: string }>"
52+
/>
53+
54+
<!-- Controls Island Icon Size -->
55+
<FieldSelect
56+
v-if="showControlsIsland"
57+
v-model="settings.controlsIslandIconSize"
58+
v-motion
59+
:initial="{ opacity: 0, y: 10 }"
60+
:enter="{ opacity: 1, y: 0 }"
61+
:duration="250 + (4 * 10)"
62+
:delay="4 * 50"
63+
transition="all ease-in-out duration-250"
64+
:label="t('settings.controls-island.icon-size.title')"
65+
:description="t('settings.controls-island.icon-size.description')"
66+
:options="[
67+
{ value: 'auto', label: t('settings.controls-island.icon-size.auto') },
68+
{ value: 'large', label: t('settings.controls-island.icon-size.large') },
69+
{ value: 'small', label: t('settings.controls-island.icon-size.small') },
70+
]"
4471
/>
4572

4673
<div

packages/stage-ui/src/stores/settings.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ export const useSettings = defineStore('settings', () => {
115115

116116
const [allowVisibleOnAllWorkspaces, resetAllowVisibleOnAllWorkspaces] = createResettableLocalStorage('settings/allow-visible-on-all-workspaces', true)
117117

118+
const [controlsIslandIconSize, resetControlsIslandIconSize] = createResettableLocalStorage<'auto' | 'large' | 'small'>('settings/controls-island/icon-size', 'auto')
119+
118120
function getLanguage() {
119121
let language = localStorage.getItem('settings/language')
120122

@@ -186,6 +188,7 @@ export const useSettings = defineStore('settings', () => {
186188
resetThemeColorsHueDynamic()
187189

188190
resetAllowVisibleOnAllWorkspaces()
191+
resetControlsIslandIconSize()
189192

190193
await updateStageModel()
191194
}
@@ -212,6 +215,7 @@ export const useSettings = defineStore('settings', () => {
212215
themeColorsHueDynamic,
213216

214217
allowVisibleOnAllWorkspaces,
218+
controlsIslandIconSize,
215219

216220
setThemeColorsHue,
217221
applyPrimaryColorFrom,

packages/ui/src/components/form/combobox/combobox.vue

Lines changed: 68 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
ComboboxItem,
1111
ComboboxItemIndicator,
1212
ComboboxLabel,
13+
ComboboxPortal,
1314
ComboboxRoot,
1415
ComboboxSeparator,
1516
ComboboxTrigger,
@@ -62,69 +63,76 @@ function toDisplayValue(value: T): string {
6263
</ComboboxTrigger>
6364
</ComboboxAnchor>
6465

65-
<ComboboxContent
66-
:avoid-collisions="true"
67-
:class="[
68-
'absolute z-10 w-full mt-1 min-w-[160px] overflow-hidden rounded-xl shadow-sm border will-change-[opacity,transform] max-h-50dvh',
69-
'data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade',
70-
'bg-white dark:bg-neutral-900',
71-
'border-neutral-200 dark:border-neutral-800 border-solid border-2 focus:border-neutral-300 dark:focus:border-neutral-600',
72-
]"
73-
>
74-
<ComboboxViewport class="p-[2px]">
75-
<ComboboxEmpty
76-
:class="[
77-
'font-medium py-2 px-2',
78-
'text-xs text-neutral-700 dark:text-neutral-200',
79-
'transition-colors duration-200 ease-in-out',
80-
]"
81-
/>
66+
<ComboboxPortal>
67+
<ComboboxContent
68+
position="popper"
69+
side="bottom"
70+
align="start"
71+
:side-offset="4"
72+
:avoid-collisions="true"
73+
:class="[
74+
'z-150 w-full min-w-[160px] overflow-hidden rounded-xl shadow-sm border will-change-[opacity,transform]',
75+
'data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade',
76+
'bg-white dark:bg-neutral-900',
77+
'border-neutral-200 dark:border-neutral-800 border-solid border-2 focus:border-neutral-300 dark:focus:border-neutral-600',
78+
]"
79+
:style="{ width: 'var(--reka-combobox-trigger-width)' }"
80+
>
81+
<ComboboxViewport :class="['p-[2px]', 'max-h-50dvh', 'overflow-y-auto']">
82+
<ComboboxEmpty
83+
:class="[
84+
'font-medium py-2 px-2',
85+
'text-xs text-neutral-700 dark:text-neutral-200',
86+
'transition-colors duration-200 ease-in-out',
87+
]"
88+
/>
8289

83-
<template
84-
v-for="(group, index) in options"
85-
:key="group.groupLabel"
86-
>
87-
<ComboboxGroup :class="['overflow-x-hidden']">
88-
<ComboboxSeparator
89-
v-if="index !== 0"
90-
:class="['m-[5px]', 'h-[1px]', 'bg-neutral-400']"
91-
/>
90+
<template
91+
v-for="(group, index) in options"
92+
:key="group.groupLabel"
93+
>
94+
<ComboboxGroup :class="['overflow-x-hidden']">
95+
<ComboboxSeparator
96+
v-if="index !== 0"
97+
:class="['m-[5px]', 'h-[1px]', 'bg-neutral-400']"
98+
/>
9299

93-
<ComboboxLabel
94-
:class="[
95-
'px-[25px] text-xs leading-[25px]',
96-
'text-neutral-500 dark:text-neutral-400',
97-
'transition-colors duration-200 ease-in-out',
98-
]"
99-
>
100-
{{ group.groupLabel }}
101-
</ComboboxLabel>
100+
<ComboboxLabel
101+
:class="[
102+
'px-[25px] text-xs leading-[25px]',
103+
'text-neutral-500 dark:text-neutral-400',
104+
'transition-colors duration-200 ease-in-out',
105+
]"
106+
>
107+
{{ group.groupLabel }}
108+
</ComboboxLabel>
102109

103-
<ComboboxItem
104-
v-for="option in group.children"
105-
:key="option.label"
106-
:text-value="option.label"
107-
:value="option.value"
108-
:class="[
109-
'leading-none rounded-lg flex items-center h-8 pr-[0.5rem] pl-[1.5rem] relative select-none data-[disabled]:pointer-events-none data-[highlighted]:outline-none',
110-
'data-[highlighted]:bg-neutral-100 dark:data-[highlighted]:bg-neutral-800',
111-
'text-sm text-neutral-700 dark:text-neutral-200 data-[disabled]:text-neutral-400 dark:data-[disabled]:text-neutral-600 data-[highlighted]:text-grass1',
112-
'transition-colors duration-200 ease-in-out',
113-
'cursor-pointer',
114-
]"
115-
>
116-
<ComboboxItemIndicator
117-
:class="['absolute', 'left-0', 'w-[25px]', 'inline-flex', 'items-center', 'justify-center', 'opacity-30']"
110+
<ComboboxItem
111+
v-for="option in group.children"
112+
:key="option.label"
113+
:text-value="option.label"
114+
:value="option.value"
115+
:class="[
116+
'leading-normal rounded-lg flex items-center h-8 pr-[0.5rem] pl-[1.5rem] relative select-none data-[disabled]:pointer-events-none data-[highlighted]:outline-none',
117+
'data-[highlighted]:bg-neutral-100 dark:data-[highlighted]:bg-neutral-800',
118+
'text-sm text-neutral-700 dark:text-neutral-200 data-[disabled]:text-neutral-400 dark:data-[disabled]:text-neutral-600 data-[highlighted]:text-grass1',
119+
'transition-colors duration-200 ease-in-out',
120+
'cursor-pointer',
121+
]"
118122
>
119-
<div i-solar:alt-arrow-right-outline />
120-
</ComboboxItemIndicator>
121-
<span :class="['line-clamp-1', 'overflow-hidden', 'text-ellipsis', 'whitespace-nowrap']">
122-
{{ option.label }}
123-
</span>
124-
</ComboboxItem>
125-
</ComboboxGroup>
126-
</template>
127-
</ComboboxViewport>
128-
</ComboboxContent>
123+
<ComboboxItemIndicator
124+
:class="['absolute', 'left-0', 'w-[25px]', 'inline-flex', 'items-center', 'justify-center', 'opacity-30']"
125+
>
126+
<div i-solar:alt-arrow-right-outline />
127+
</ComboboxItemIndicator>
128+
<span :class="['line-clamp-1', 'overflow-hidden', 'text-ellipsis', 'whitespace-nowrap']">
129+
{{ option.label }}
130+
</span>
131+
</ComboboxItem>
132+
</ComboboxGroup>
133+
</template>
134+
</ComboboxViewport>
135+
</ComboboxContent>
136+
</ComboboxPortal>
129137
</ComboboxRoot>
130138
</template>

0 commit comments

Comments
 (0)