fix(stage-web): mobile screensafe area & style issues

This commit is contained in:
Neko Ayaka
2025-07-21 23:33:52 +08:00
parent a8454b4a44
commit f5fd13497c
5 changed files with 23 additions and 24 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<title>AIRI</title>
<meta name="description" content="Open source attempt to create cyber companion">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" sizes="any" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<meta name="theme-color" content="rgb(18,18,18)" id="themeColor" />
@@ -4,7 +4,7 @@ import type { ChatProvider } from '@xsai-ext/shared-providers'
import { useMicVAD } from '@proj-airi/stage-ui/composables'
import { useChatStore, useConsciousnessStore, useProvidersStore, useSettings } from '@proj-airi/stage-ui/stores'
import { BasicTextarea } from '@proj-airi/ui'
import { useDark } from '@vueuse/core'
import { useDark, useResizeObserver, useScreenSafeArea } from '@vueuse/core'
import { storeToRefs } from 'pinia'
import { onMounted, ref, useTemplateRef, watch } from 'vue'
import { useI18n } from 'vue-i18n'
@@ -23,9 +23,12 @@ const messageInput = ref('')
const listening = ref(false)
const isComposing = ref(false)
const screenSafeArea = useScreenSafeArea()
const providersStore = useProvidersStore()
const { activeProvider, activeModel } = storeToRefs(useConsciousnessStore())
useResizeObserver(document.documentElement, () => screenSafeArea.update())
// const { audioInputs } = useDevicesList({ constraints: { audio: true }, requestPermissions: true })
// const { selectedAudioDevice, isAudioInputOn, selectedAudioDeviceId } = storeToRefs(useSettings())
const { isAudioInputOn, selectedAudioDeviceId, themeColorsHueDynamic, stageView, stageViewControlsEnabled } = storeToRefs(useSettings())
@@ -105,6 +108,7 @@ watch([activeProvider, activeModel], async () => {
onMounted(() => {
start()
screenSafeArea.update()
})
</script>
@@ -112,14 +116,14 @@ onMounted(() => {
<div fixed bottom-0 w-full flex flex-col>
<KeepAlive>
<Transition name="fade">
<MobileChatHistory v-if="!stageViewControlsEnabled" max-w="[calc(100%-3.5rem)]" w-full self-start pl-2 />
<MobileChatHistory v-if="!stageViewControlsEnabled" max-w="[calc(100%-3.5rem)]" w-full self-start pl-3 />
</Transition>
</KeepAlive>
<div relative w-full self-end>
<div top="50%" translate-y="[-50%]" fixed z-15 px-2>
<div top="50%" translate-y="[-50%]" fixed z-15 px-3>
<ViewControlInputs ref="viewControlsInputs" :mode="viewControlsActiveMode" />
</div>
<div translate-y="[-100%]" absolute right-0 w-full px-2 pb-2 font-sans>
<div translate-y="[-100%]" absolute right-0 w-full px-3 pb-3 font-sans>
<div flex="~ col" w-full gap-1>
<button border="2 solid neutral-100/60 dark:neutral-800/30" bg="neutral-50/70 dark:neutral-800/70" w-fit flex items-center self-end justify-center rounded-xl p-2 backdrop-blur-md title="About">
<div i-solar:info-circle-outline size-5 text="neutral-500 dark:neutral-400" />
@@ -150,11 +154,11 @@ onMounted(() => {
<ActionViewControls v-model="viewControlsActiveMode" @reset="() => viewControlsInputsRef?.resetOnMode()" />
</div>
</div>
<div bg="white dark:neutral-800" w-full flex gap-1 px-2 py-2>
<div bg="white dark:neutral-800" max-h-100dvh max-w-100dvw w-full flex gap-1 overflow-auto px-3 pt-2 :style="{ paddingBottom: `${Math.max(Number.parseFloat(screenSafeArea.bottom.value.replace('px', '')), 12)}px` }">
<BasicTextarea
v-model="messageInput"
:placeholder="t('stage.message')"
border="solid 2 primary-50 dark:primary-950/10"
border="solid 2 neutral-200/60 dark:neutral-700/60"
text="neutral-500 hover:neutral-600 dark:neutral-100 dark:hover:neutral-200 placeholder:neutral-400 placeholder:hover:neutral-500 placeholder:dark:neutral-300 placeholder:dark:hover:neutral-400"
bg="neutral-100/80 dark:neutral-950/80"
max-h="[10lh]" min-h="[calc(1lh+4px+4px)]"
+2 -12
View File
@@ -1,19 +1,9 @@
<script setup lang="ts">
import { useScreenSafeArea } from '@vueuse/core'
const { top, right, bottom, left } = useScreenSafeArea()
import { RouterView } from 'vue-router'
</script>
<template>
<main
text="gray-700 dark:gray-200" h-full font-cuteen
:style="{
paddingTop: `${top}px`,
paddingRight: `${right}px`,
paddingBottom: `${bottom}px`,
paddingLeft: `${left}px`,
}"
>
<main text="gray-700 dark:gray-200" font-cuteen h-full>
<RouterView />
</main>
</template>
@@ -297,7 +297,7 @@ onMounted(() => {
<!-- Provider Selection -->
<template v-else-if="step === 2">
<div h-full flex flex-col gap-4>
<div bg="white dark:#181818" sticky top-0 z-100 flex flex-shrink-0 items-center gap-2>
<div sticky top-0 z-100 flex flex-shrink-0 items-center gap-2>
<button outline-none @click="handlePreviousStep">
<div class="i-solar:alt-arrow-left-line-duotone h-5 w-5" />
</button>
@@ -332,7 +332,7 @@ onMounted(() => {
<!-- Configuration Form -->
<template v-else-if="step === 3 && selectedProvider">
<div h-full flex flex-col gap-4>
<div bg="white dark:#181818" sticky top-0 z-100 flex flex-shrink-0 items-center gap-2>
<div sticky top-0 z-100 flex flex-shrink-0 items-center gap-2>
<button outline-none @click="handlePreviousStep">
<div i-solar:alt-arrow-left-line-duotone h-5 w-5 />
</button>
@@ -422,7 +422,7 @@ onMounted(() => {
<!-- Models Configuration Form -->
<template v-else-if="step === 4 && selectedProvider">
<div h-full flex flex-col gap-4>
<div bg="white dark:#181818" sticky top-0 z-100 flex flex-shrink-0 items-center gap-2>
<div sticky top-0 z-100 flex flex-shrink-0 items-center gap-2>
<button outline-none @click="handlePreviousStep">
<div i-solar:alt-arrow-left-line-duotone h-5 w-5 />
</button>
@@ -1,7 +1,8 @@
<script setup lang="ts">
import { useMediaQuery } from '@vueuse/core'
import { useMediaQuery, useResizeObserver, useScreenSafeArea } from '@vueuse/core'
import { DialogContent, DialogOverlay, DialogPortal, DialogRoot } from 'reka-ui'
import { DrawerContent, DrawerHandle, DrawerOverlay, DrawerPortal, DrawerRoot } from 'vaul-vue'
import { onMounted } from 'vue'
import Onboarding from './Onboarding.vue'
@@ -13,6 +14,10 @@ const emit = defineEmits<{
const showDialog = defineModel({ type: Boolean, default: false, required: false })
const isDesktop = useMediaQuery('(min-width: 768px)')
const screenSafeArea = useScreenSafeArea()
useResizeObserver(document.documentElement, () => screenSafeArea.update())
onMounted(() => screenSafeArea.update())
</script>
<template>
@@ -27,7 +32,7 @@ const isDesktop = useMediaQuery('(min-width: 768px)')
<DrawerRoot v-else :open="showDialog" should-scale-background @update:open="value => showDialog = value">
<DrawerPortal>
<DrawerOverlay class="fixed inset-0" />
<DrawerContent class="fixed bottom-0 left-0 right-0 z-1000 mt-20 h-full max-h-[96%] flex flex-col rounded-t-2xl bg-neutral-50 p-4 outline-none backdrop-blur-md dark:bg-neutral-900/95">
<DrawerContent class="fixed bottom-0 left-0 right-0 z-1000 mt-20 h-full max-h-[96%] flex flex-col rounded-t-2xl bg-neutral-50 px-4 pt-4 outline-none backdrop-blur-md dark:bg-neutral-900/95" :style="{ paddingBottom: `${Math.max(Number.parseFloat(screenSafeArea.bottom.value.replace('px', '')), 12)}px` }">
<DrawerHandle />
<Onboarding @configured="emit('configured')" @skipped="emit('skipped')" />
</DrawerContent>