Refactor extension handlers and improve mobile UX

- Split extensionMessageHandler into separate handler files
  - Created handlers directory with individual files for database, filesystem, http, permissions, context, and storage
  - Reduced main handler file from 602 to 342 lines
  - Improved code organization and maintainability

- Add viewport utilities for safe area handling
  - New viewport.ts utility with helpers for fullscreen dimensions
  - Proper safe area inset calculations for mobile devices
  - Fixed window positioning on small screens to start at 0,0

- Create UiDrawer wrapper component
  - Automatically applies safe area insets
  - Uses TypeScript DrawerProps interface for code completion
  - Replaced all UDrawer instances with UiDrawer

- Improve window management
  - Windows on small screens now use full viewport with safe areas
  - Fixed maximize functionality to respect safe areas
  - Consolidated safe area logic in reusable utilities
This commit is contained in:
2025-11-08 00:14:53 +01:00
parent 2b739b9e79
commit 43ba246174
20 changed files with 2261 additions and 1725 deletions

View File

@ -1,5 +1,5 @@
<template>
<UDrawer
<UiDrawer
v-model:open="open"
direction="right"
:title="t('launcher.title')"
@ -7,9 +7,6 @@
:overlay="false"
:modal="false"
:handle-only="true"
:ui="{
content: 'w-dvw max-w-md sm:max-w-fit',
}"
>
<UButton
icon="material-symbols:apps"
@ -66,7 +63,7 @@
</div>
</div>
</template>
</UDrawer>
</UiDrawer>
<!-- Uninstall Confirmation Dialog -->
<UiDialogConfirm

View File

@ -83,6 +83,7 @@
</template>
<script setup lang="ts">
import { getAvailableContentHeight } from '~/utils/viewport'
const props = defineProps<{
id: string
title: string
@ -329,31 +330,11 @@ const handleMaximize = () => {
const bounds = getViewportBounds()
if (bounds && bounds.width > 0 && bounds.height > 0) {
// Get safe-area-insets from CSS variables for debug
const safeAreaTop = parseFloat(
getComputedStyle(document.documentElement).getPropertyValue(
'--safe-area-inset-top',
) || '0',
)
const safeAreaBottom = parseFloat(
getComputedStyle(document.documentElement).getPropertyValue(
'--safe-area-inset-bottom',
) || '0',
)
// Desktop container uses 'absolute inset-0' which stretches over full viewport
// bounds.height = full viewport height (includes header area + safe-areas)
// We need to calculate available space properly
// Get header height from UI store (measured reactively in layout)
const uiStore = useUiStore()
const headerHeight = uiStore.headerHeight
x.value = 0
y.value = 0 // Start below header and status bar
y.value = 0
width.value = bounds.width
// Height: viewport - header - both safe-areas
height.value = bounds.height - headerHeight - safeAreaTop - safeAreaBottom
// Use helper function to calculate correct height with safe areas
height.value = getAvailableContentHeight()
isMaximized.value = true
}
}

View File

@ -1,5 +1,5 @@
<template>
<UDrawer
<UiDrawer
v-model:open="localShowWindowOverview"
direction="bottom"
:title="t('modal.title')"
@ -70,7 +70,7 @@
</div>
</div>
</template>
</UDrawer>
</UiDrawer>
</template>
<script setup lang="ts">

View File

@ -1,5 +1,5 @@
<template>
<UDrawer
<UiDrawer
v-model:open="isOverviewMode"
direction="left"
:overlay="false"
@ -8,7 +8,7 @@
description="Workspaces"
>
<template #content>
<div class="py-8 pl-8 pr-4 h-full overflow-y-auto">
<div class="pl-8 pr-4 h-full overflow-y-auto">
<!-- Workspace Cards -->
<div class="flex flex-col gap-3">
<HaexWorkspaceCard
@ -29,7 +29,7 @@
/>
</div>
</template>
</UDrawer>
</UiDrawer>
</template>
<script setup lang="ts">

View File

@ -0,0 +1,30 @@
<template>
<UDrawer
v-bind="$attrs"
:ui="{
content:
'pb-[env(safe-area-inset-bottom)] pt-[env(safe-area-inset-top)] w-dvw max-w-md sm:max-w-fit',
...(ui || {}),
}"
>
<template
v-for="(_, name) in $slots"
#[name]="slotData"
>
<slot
:name="name"
v-bind="slotData"
/>
</template>
</UDrawer>
</template>
<script setup lang="ts">
import type { DrawerProps } from '@nuxt/ui'
/**
* Wrapper around UDrawer that automatically applies safe area insets for mobile devices.
* Passes through all props and slots to UDrawer.
*/
defineProps</* @vue-ignore */ DrawerProps>()
</script>

View File

@ -83,8 +83,6 @@ const filteredSlots = computed(() => {
Object.entries(useSlots()).filter(([name]) => name !== 'trailing'),
)
})
const { isSmallScreen } = storeToRefs(useUiStore())
</script>
<i18n lang="yaml">