chore: prune unused apps and services for the moeka fork
CI / Lint (push) Canceled after 0s
CI / Build Test (stage-web) (push) Canceled after 0s
CI / Build Test (ui-loading-screens) (push) Canceled after 0s
CI / Build Test (ui-transitions) (push) Canceled after 0s
CI / Unit Test (push) Canceled after 0s
CI / Type Check (push) Canceled after 0s
CI / Check Provenance (push) Canceled after 0s
Sync Labels / sync-labels (push) Successful in 1m43s
CI / Lint (push) Canceled after 0s
CI / Build Test (stage-web) (push) Canceled after 0s
CI / Build Test (ui-loading-screens) (push) Canceled after 0s
CI / Build Test (ui-transitions) (push) Canceled after 0s
CI / Unit Test (push) Canceled after 0s
CI / Type Check (push) Canceled after 0s
CI / Check Provenance (push) Canceled after 0s
Sync Labels / sync-labels (push) Successful in 1m43s
- Remove stage-tamagotchi, stage-pocket, server, and engine workspaces with their workflows, addons, and docs - Switch the toolchain from pnpm to bun, replace lockfiles with bun.lock - Rewrite remaining product references to Moeka
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"description": "Shared Layouts",
|
||||
"author": {
|
||||
"name": "Moeru AI Project AIRI Team",
|
||||
"name": "Moeka Team",
|
||||
"email": "airi@moeru.ai",
|
||||
"url": "https://github.com/moeru-ai"
|
||||
},
|
||||
|
||||
@@ -102,7 +102,7 @@ describe('adaptive input geometry', () => {
|
||||
// ROOT CAUSE:
|
||||
//
|
||||
// Safari keeps reporting the keyboard-sized Visual Viewport for part of its close animation.
|
||||
// If AIRI uses that stale measurement after blur, the input region stays compressed and leaves
|
||||
// If Moeka uses that stale measurement after blur, the input region stays compressed and leaves
|
||||
// a visible gap above the keyboard.
|
||||
//
|
||||
// Before the fix, blur disabled keyboardVisible but kept height and visibleBottom at 404px.
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { ProfileSwitcherPopover } from '@proj-airi/stage-ui/components'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { RouterLink, useRouter } from 'vue-router'
|
||||
|
||||
import HeaderAvatar from './HeaderAvatar.vue'
|
||||
import HeaderLink from './HeaderLink.vue'
|
||||
import ActionAbout from './InteractiveArea/Actions/About.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
|
||||
function handleNavigation() {
|
||||
@@ -19,7 +20,15 @@ function handleNavigation() {
|
||||
<div flex items-center gap-2>
|
||||
<ActionAbout />
|
||||
<ProfileSwitcherPopover @manage="handleNavigation" />
|
||||
<HeaderAvatar />
|
||||
<RouterLink
|
||||
border="2 solid neutral-100/60 dark:neutral-800/30"
|
||||
bg="neutral-50/70 dark:neutral-800/70"
|
||||
w-fit flex items-center justify-center rounded-xl p-2 backdrop-blur-md
|
||||
:title="t('settings.title')"
|
||||
to="/settings"
|
||||
>
|
||||
<div i-solar:settings-minimalistic-bold-duotone size-5 text="neutral-500 dark:neutral-400" />
|
||||
</RouterLink>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { signOut } from '@proj-airi/stage-ui/libs/auth'
|
||||
import { useAuthStore } from '@proj-airi/stage-ui/stores/auth'
|
||||
import { AnimatedContent, Avatar } from '@proj-airi/ui'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuRoot,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { RouterLink } from 'vue-router'
|
||||
|
||||
defineProps<{
|
||||
/** Shows only the account control on the mobile Stage. @default false */
|
||||
compact?: boolean
|
||||
}>()
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const { isAuthenticated, user, credits } = storeToRefs(authStore)
|
||||
const { t } = useI18n()
|
||||
|
||||
const userName = computed(() => user.value?.name)
|
||||
const userAvatar = computed(() => user.value?.image)
|
||||
|
||||
const formattedCredits = computed(() => credits.value.toLocaleString())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div flex items-center gap-2>
|
||||
<!-- Non-authenticated: Settings & Sign in -->
|
||||
<!-- NOTICE: The avatar is stored in the localstorage, it will be shown at the first time of the page load, so we do not need the skeleton loading here -->
|
||||
<template v-if="!isAuthenticated">
|
||||
<RouterLink
|
||||
v-if="!compact"
|
||||
border="2 solid neutral-100/60 dark:neutral-800/30"
|
||||
bg="neutral-50/70 dark:neutral-800/70"
|
||||
w-fit flex items-center justify-center rounded-xl p-2 backdrop-blur-md
|
||||
title="Settings"
|
||||
to="/settings"
|
||||
>
|
||||
<div i-solar:settings-minimalistic-bold-duotone size-5 text="neutral-500 dark:neutral-400" />
|
||||
</RouterLink>
|
||||
|
||||
<button
|
||||
:class="compact ? ['min-h-11 min-w-11 focus-visible:outline-2 focus-visible:outline-primary-500'] : undefined"
|
||||
border="2 solid neutral-100/60 dark:neutral-800/30"
|
||||
bg="neutral-50/70 dark:neutral-800/70"
|
||||
w-fit flex items-center justify-center rounded-xl p-2 backdrop-blur-md
|
||||
title="Sign in"
|
||||
type="button"
|
||||
@click="authStore.needsLogin = true"
|
||||
>
|
||||
<div i-solar:user-bold-duotone />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!-- Authenticated: Avatar Dropdown -->
|
||||
<DropdownMenuRoot v-else>
|
||||
<DropdownMenuTrigger as-child>
|
||||
<button
|
||||
type="button"
|
||||
:aria-label="userName || t('settings.pages.account.title')"
|
||||
:class="[
|
||||
'group flex items-center gap-2 rounded-full border-2 p-1 outline-none backdrop-blur-md',
|
||||
compact ? 'size-11 justify-center focus-visible:ring-2 focus-visible:ring-primary-500' : 'pl-1 pr-3',
|
||||
'border-neutral-100/60 bg-neutral-50/70 dark:border-neutral-800/30 dark:bg-neutral-800/70',
|
||||
'hover:bg-neutral-100 data-[state=open]:bg-neutral-100',
|
||||
'dark:hover:bg-neutral-800 dark:data-[state=open]:bg-neutral-800',
|
||||
'data-[state=open]:ring-2 data-[state=open]:ring-primary-500/20',
|
||||
'transition-colors duration-200 ease-in-out',
|
||||
]"
|
||||
>
|
||||
<Avatar
|
||||
:src="userAvatar"
|
||||
:class="[
|
||||
'h-7 w-7 rounded-full',
|
||||
'bg-neutral-200 text-neutral-500 dark:bg-neutral-700 dark:text-neutral-400',
|
||||
]"
|
||||
/>
|
||||
|
||||
<span
|
||||
v-if="userName && !compact"
|
||||
:class="[
|
||||
'max-w-[100px] hidden truncate text-sm font-medium sm:block',
|
||||
'text-neutral-700 dark:text-neutral-200',
|
||||
]"
|
||||
>
|
||||
{{ userName }}
|
||||
</span>
|
||||
<div
|
||||
v-if="!compact"
|
||||
:class="[
|
||||
'i-solar:alt-arrow-down-linear text-neutral-400',
|
||||
'transition-transform duration-200',
|
||||
'group-data-[state=open]:rotate-180',
|
||||
]"
|
||||
/>
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuContent
|
||||
as-child
|
||||
align="end"
|
||||
side="bottom"
|
||||
:side-offset="6"
|
||||
:class="[
|
||||
'z-10000 w-60 rounded-xl border p-1 shadow-lg outline-none backdrop-blur-md',
|
||||
'border-neutral-100/80 bg-neutral-100/80 text-neutral-700',
|
||||
'dark:border-neutral-800/60 dark:bg-neutral-800/80 dark:text-neutral-100',
|
||||
]"
|
||||
>
|
||||
<AnimatedContent>
|
||||
<div class="px-3 py-2">
|
||||
<p class="text-xs text-neutral-500 dark:text-neutral-400">
|
||||
Signed in as
|
||||
</p>
|
||||
<p class="truncate text-sm text-neutral-900 font-medium dark:text-white">
|
||||
{{ userName }}
|
||||
</p>
|
||||
<div class="mt-1 flex items-center gap-1.5 text-xs text-primary-600 font-medium dark:text-primary-400">
|
||||
<div class="i-solar:battery-charge-bold-duotone text-sm" />
|
||||
<span>{{ formattedCredits }} Flux</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DropdownMenuSeparator :class="['mx-2 my-1 h-px bg-neutral-200/80 dark:bg-neutral-700/80']" />
|
||||
|
||||
<DropdownMenuItem as-child>
|
||||
<RouterLink
|
||||
to="/settings/account"
|
||||
:class="[
|
||||
'group w-full flex cursor-pointer select-none items-center gap-3 rounded-lg px-3 py-2',
|
||||
'text-sm leading-none outline-none text-neutral-700 dark:text-neutral-200',
|
||||
'data-[highlighted]:bg-primary-100/80 dark:data-[highlighted]:bg-primary-900/40',
|
||||
'transition-colors duration-150 ease-in-out',
|
||||
]"
|
||||
>
|
||||
<div class="i-solar:user-id-bold-duotone text-lg text-neutral-400 transition group-hover:text-primary-500" />
|
||||
Profile
|
||||
</RouterLink>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem as-child>
|
||||
<RouterLink
|
||||
to="/settings/flux"
|
||||
:class="[
|
||||
'group w-full flex cursor-pointer select-none items-center gap-3 rounded-lg px-3 py-2',
|
||||
'text-sm leading-none outline-none text-neutral-700 dark:text-neutral-200',
|
||||
'data-[highlighted]:bg-primary-100/80 dark:data-[highlighted]:bg-primary-900/40',
|
||||
'transition-colors duration-150 ease-in-out',
|
||||
]"
|
||||
>
|
||||
<div class="i-solar:battery-charge-bold-duotone text-lg text-neutral-400 transition group-hover:text-primary-500" />
|
||||
Flux
|
||||
</RouterLink>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem as-child>
|
||||
<RouterLink
|
||||
to="/settings"
|
||||
:class="[
|
||||
'group w-full flex cursor-pointer select-none items-center gap-3 rounded-lg px-3 py-2',
|
||||
'text-sm leading-none outline-none text-neutral-700 dark:text-neutral-200',
|
||||
'data-[highlighted]:bg-primary-100/80 dark:data-[highlighted]:bg-primary-900/40',
|
||||
'transition-colors duration-150 ease-in-out',
|
||||
]"
|
||||
>
|
||||
<div class="i-solar:settings-minimalistic-bold-duotone text-lg text-neutral-400 transition group-hover:text-primary-500" />
|
||||
Settings
|
||||
</RouterLink>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuSeparator :class="['mx-2 my-1 h-px bg-neutral-200/80 dark:bg-neutral-700/80']" />
|
||||
|
||||
<DropdownMenuItem as-child>
|
||||
<button
|
||||
:class="[
|
||||
'group w-full flex cursor-pointer select-none items-center gap-3 rounded-lg px-3 py-2',
|
||||
'text-sm leading-none outline-none text-red-600 dark:text-red-400',
|
||||
'data-[highlighted]:bg-red-50 dark:data-[highlighted]:bg-red-900/20',
|
||||
'transition-colors duration-150 ease-in-out',
|
||||
]"
|
||||
@click="signOut"
|
||||
>
|
||||
<div class="i-solar:logout-3-bold-duotone text-lg transition group-hover:text-red-600 dark:group-hover:text-red-400" />
|
||||
Sign out
|
||||
</button>
|
||||
</DropdownMenuItem>
|
||||
</AnimatedContent>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuRoot>
|
||||
</div>
|
||||
</template>
|
||||
@@ -20,7 +20,7 @@ const { isDark: dark } = useTheme()
|
||||
<img :src="Logo" h-8 w-8 class="theme-colored">
|
||||
</template>
|
||||
<div translate-y="[2px]" font-quicksand font-semibold>
|
||||
<span>AIRI</span>
|
||||
<span>Moeka</span>
|
||||
</div>
|
||||
</RouterLink>
|
||||
</template>
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { HearingConfig } from '@proj-airi/stage-ui/components'
|
||||
import { useAuthStore } from '@proj-airi/stage-ui/stores/auth'
|
||||
import { useSettingsAudioDevice } from '@proj-airi/stage-ui/stores/settings'
|
||||
import { Avatar, BasicButton, BottomDrawer, Checkbox, GhostButton, useTheme } from '@proj-airi/ui'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { BasicButton, BottomDrawer, Checkbox, GhostButton, useTheme } from '@proj-airi/ui'
|
||||
import { shallowRef, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { RouterLink, useRouter } from 'vue-router'
|
||||
import { RouterLink } from 'vue-router'
|
||||
|
||||
import ActionAbout from './InteractiveArea/Actions/About.vue'
|
||||
|
||||
@@ -21,18 +19,15 @@ const emit = defineEmits<{
|
||||
const characterVoiceEnabled = defineModel<boolean>('characterVoiceEnabled', { required: true })
|
||||
const { t } = useI18n()
|
||||
const { isDark } = useTheme()
|
||||
const authStore = useAuthStore()
|
||||
const { isAuthenticated, user } = storeToRefs(authStore)
|
||||
const router = useRouter()
|
||||
const settingsAudioDevice = useSettingsAudioDevice()
|
||||
const hearingOpen = shallowRef(false)
|
||||
const backgroundDialogOpen = shallowRef(false)
|
||||
const settingsOpen = shallowRef(false)
|
||||
const aboutOpen = shallowRef(false)
|
||||
// Finish closing settings before opening a sibling modal, so focus and scroll locks have one owner.
|
||||
const nextPanel = shallowRef<'background' | 'about' | 'account' | 'hearing' | 'view'>()
|
||||
const nextPanel = shallowRef<'background' | 'about' | 'hearing' | 'view'>()
|
||||
|
||||
function openPanel(panel: 'background' | 'about' | 'account' | 'hearing' | 'view') {
|
||||
function openPanel(panel: 'background' | 'about' | 'hearing' | 'view') {
|
||||
nextPanel.value = panel
|
||||
settingsOpen.value = false
|
||||
}
|
||||
@@ -47,12 +42,6 @@ function finishSettingsClose() {
|
||||
else if (nextPanel.value === 'hearing') {
|
||||
hearingOpen.value = true
|
||||
}
|
||||
else if (nextPanel.value === 'account') {
|
||||
if (isAuthenticated.value)
|
||||
void router.push('/settings/account')
|
||||
else
|
||||
authStore.needsLogin = true
|
||||
}
|
||||
else if (nextPanel.value === 'view') {
|
||||
emit('openViewControls')
|
||||
}
|
||||
@@ -83,22 +72,6 @@ watch(hearingOpen, async (open) => {
|
||||
<span aria-hidden="true" :class="['i-solar:settings-outline size-6']" />
|
||||
</BasicButton>
|
||||
</template>
|
||||
<GhostButton
|
||||
block size="unset"
|
||||
:class="[
|
||||
'mobile-tool-row rounded-2xl',
|
||||
'[&_.basic-button-content]:w-full [&_.basic-button-content]:gap-3 [&_[aria-hidden]]:shrink-0',
|
||||
isAuthenticated ? 'mobile-tool-row-authenticated mb-4 min-h-16' : 'mobile-tool-row-anonymous mb-3 min-h-14',
|
||||
]"
|
||||
@click="openPanel('account')"
|
||||
>
|
||||
<Avatar v-if="isAuthenticated" :src="user?.image" :class="['size-12 shrink-0 rounded-full bg-neutral-200 text-neutral-500 dark:bg-neutral-700']" />
|
||||
<span :class="['min-w-0 flex-1 text-left']">
|
||||
<span :class="['block truncate text-base font-semibold']">{{ isAuthenticated ? user?.name : t('stage.mobile-tools.sign-in') }}</span>
|
||||
<span :class="['block text-xs text-neutral-500 dark:text-neutral-400']">{{ t('stage.mobile-tools.account-description') }}</span>
|
||||
</span>
|
||||
<span aria-hidden="true" :class="['i-solar:alt-arrow-right-outline size-4 shrink-0 text-neutral-400']" />
|
||||
</GhostButton>
|
||||
<section :class="['mb-4']">
|
||||
<h3 :class="['mb-2 px-1 text-xs font-semibold text-neutral-500 dark:text-neutral-400']">
|
||||
{{ t('stage.mobile-tools.appearance') }}
|
||||
@@ -201,12 +174,4 @@ watch(hearingOpen, async (open) => {
|
||||
.mobile-tool-row :deep([aria-hidden]) {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mobile-tool-row.mobile-tool-row-authenticated {
|
||||
padding: 0.75rem 1rem !important;
|
||||
}
|
||||
|
||||
.mobile-tool-row.mobile-tool-row-anonymous {
|
||||
padding: 0.5rem 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { computed, nextTick, onMounted, onScopeDispose, shallowRef, toValue, wat
|
||||
|
||||
import { useAdaptiveInput } from './use-adaptive-input'
|
||||
|
||||
/** The AIRI elements and feature policy used to lay out the mobile chat surface. */
|
||||
/** The Moeka elements and feature policy used to lay out the mobile chat surface. */
|
||||
export interface UseMobileInteractiveAreaLayoutOptions {
|
||||
/** The region that contains the message composer and its related controls. */
|
||||
area: Readonly<Ref<HTMLElement | null>>
|
||||
@@ -26,9 +26,9 @@ export interface UseMobileInteractiveAreaLayoutOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies AIRI's mobile chat proportions and transition policy to adaptive input values.
|
||||
* Applies Moeka's mobile chat proportions and transition policy to adaptive input values.
|
||||
*
|
||||
* This composable owns only AIRI presentation decisions. Browser keyboard measurement remains
|
||||
* This composable owns only Moeka presentation decisions. Browser keyboard measurement remains
|
||||
* in {@link useAdaptiveInput}, so another framework or layout can consume the same geometry.
|
||||
*/
|
||||
export function useMobileInteractiveAreaLayout(options: UseMobileInteractiveAreaLayoutOptions) {
|
||||
|
||||
Reference in New Issue
Block a user