3 Commits

Author SHA1 Message Date
cffb129e4f Auto-open dev extensions after loading
- Dev extensions are now automatically opened in a window after successful load
- Simplified extension finding logic by using devExtensions directly
- Fixed table name handling to support both double quotes and backticks in permission manager
2025-11-04 00:46:46 +01:00
405cf25aab Bump version to 0.1.6 2025-11-03 11:10:11 +01:00
b097bf211d Make windows fullscreen on small screens
- Update window components to use fullscreen layout on small screens
- Adjust UI components styling for better mobile display
- Update desktop store for small screen handling
2025-11-03 11:08:26 +01:00
10 changed files with 65 additions and 23 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "haex-hub", "name": "haex-hub",
"private": true, "private": true,
"version": "0.1.4", "version": "0.1.6",
"type": "module", "type": "module",
"scripts": { "scripts": {
"build": "nuxt build", "build": "nuxt build",

View File

@ -196,7 +196,8 @@ impl PermissionManager {
table_name: &str, table_name: &str,
) -> Result<(), ExtensionError> { ) -> Result<(), ExtensionError> {
// Remove quotes from table name if present (from SDK's getTableName()) // Remove quotes from table name if present (from SDK's getTableName())
let clean_table_name = table_name.trim_matches('"'); // Support both double quotes and backticks (Drizzle uses backticks by default)
let clean_table_name = table_name.trim_matches('"').trim_matches('`');
// Auto-allow: Extensions have full access to their own tables // Auto-allow: Extensions have full access to their own tables
// Table format: {publicKey}__{extensionName}__{tableName} // Table format: {publicKey}__{extensionName}__{tableName}

View File

@ -90,11 +90,14 @@ defineOptions({
const extensionStore = useExtensionsStore() const extensionStore = useExtensionsStore()
const windowManagerStore = useWindowManagerStore() const windowManagerStore = useWindowManagerStore()
const uiStore = useUiStore()
const { t } = useI18n() const { t } = useI18n()
const open = ref(false) const open = ref(false)
const { isSmallScreen } = storeToRefs(uiStore)
// Uninstall dialog state // Uninstall dialog state
const showUninstallDialog = ref(false) const showUninstallDialog = ref(false)
const extensionToUninstall = ref<LauncherItem | null>(null) const extensionToUninstall = ref<LauncherItem | null>(null)
@ -242,6 +245,11 @@ const handleDragStart = (event: DragEvent, item: LauncherItem) => {
if (dragImage) { if (dragImage) {
event.dataTransfer.setDragImage(dragImage, 20, 20) event.dataTransfer.setDragImage(dragImage, 20, 20)
} }
// Close drawer on small screens to reveal workspace for drop
if (isSmallScreen.value) {
open.value = false
}
} }
</script> </script>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="p-4 mx-auto space-y-6 bg-default/90 backdrop-blur-2xl"> <div class="p-4 mx-auto space-y-6 bg-default">
<div class="space-y-2"> <div class="space-y-2">
<h1 class="text-2xl font-bold">{{ t('title') }}</h1> <h1 class="text-2xl font-bold">{{ t('title') }}</h1>
<p class="text-sm opacity-70">{{ t('description') }}</p> <p class="text-sm opacity-70">{{ t('description') }}</p>
@ -122,6 +122,7 @@ const browseExtensionPathAsync = async () => {
} }
} }
const windowManagerStore = useWindowManagerStore()
// Load a dev extension // Load a dev extension
const loadDevExtensionAsync = async () => { const loadDevExtensionAsync = async () => {
if (!extensionPath.value) return if (!extensionPath.value) return
@ -140,9 +141,24 @@ const loadDevExtensionAsync = async () => {
// Reload list // Reload list
await loadDevExtensionListAsync() await loadDevExtensionListAsync()
// Get the newly loaded extension info from devExtensions
const newlyLoadedExtension = devExtensions.value.find((ext) =>
extensionPath.value.includes(ext.name),
)
// Reload all extensions in the main extension store so they appear in the launcher // Reload all extensions in the main extension store so they appear in the launcher
await loadExtensionsAsync() await loadExtensionsAsync()
// Open the newly loaded extension
if (newlyLoadedExtension) {
await windowManagerStore.openWindowAsync({
sourceId: newlyLoadedExtension.id,
type: 'extension',
icon: newlyLoadedExtension.icon || 'i-heroicons-puzzle-piece-solid',
title: newlyLoadedExtension.name,
})
}
// Clear input // Clear input
extensionPath.value = '' extensionPath.value = ''
} catch (error) { } catch (error) {

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="w-full h-full bg-default"> <div class="w-full h-full bg-default overflow-scroll">
<div class="grid grid-cols-2 p-2"> <div class="grid grid-cols-2 p-2">
<div class="p-2">{{ t('language') }}</div> <div class="p-2">{{ t('language') }}</div>
<div><UiDropdownLocale @select="onSelectLocaleAsync" /></div> <div><UiDropdownLocale @select="onSelectLocaleAsync" /></div>

View File

@ -16,6 +16,7 @@
: 'border border-gray-200 dark:border-gray-700', : 'border border-gray-200 dark:border-gray-700',
]" ]"
@mousedown="handleActivate" @mousedown="handleActivate"
@contextmenu.stop.prevent
> >
<!-- Window Titlebar --> <!-- Window Titlebar -->
<div <div
@ -50,6 +51,7 @@
/> />
<HaexWindowButton <HaexWindowButton
v-if="!isSmallScreen"
:is-maximized :is-maximized
variant="maximize" variant="maximize"
@click.stop="handleMaximize" @click.stop="handleMaximize"
@ -74,7 +76,7 @@
<!-- Resize Handles --> <!-- Resize Handles -->
<HaexWindowResizeHandles <HaexWindowResizeHandles
:disabled="isMaximized" :disabled="isMaximized || isSmallScreen"
@resize-start="handleResizeStart" @resize-start="handleResizeStart"
/> />
</div> </div>
@ -114,12 +116,16 @@ const height = defineModel<number>('height', { default: 600 })
const windowEl = useTemplateRef('windowEl') const windowEl = useTemplateRef('windowEl')
const titlebarEl = useTemplateRef('titlebarEl') const titlebarEl = useTemplateRef('titlebarEl')
const uiStore = useUiStore()
const { isSmallScreen } = storeToRefs(uiStore)
// Inject viewport size from parent desktop // Inject viewport size from parent desktop
const viewportSize = inject<{ const viewportSize = inject<{
width: Ref<number> width: Ref<number>
height: Ref<number> height: Ref<number>
}>('viewportSize') }>('viewportSize')
const isMaximized = ref(false) // Don't start maximized // Start maximized on small screens
const isMaximized = ref(isSmallScreen.value)
// Store initial position/size for restore // Store initial position/size for restore
const preMaximizeState = ref({ const preMaximizeState = ref({
@ -151,7 +157,8 @@ const isResizingOrDragging = computed(
// Setup drag with useDrag composable (supports mouse + touch) // Setup drag with useDrag composable (supports mouse + touch)
useDrag( useDrag(
({ movement: [mx, my], first, last }) => { ({ movement: [mx, my], first, last }) => {
if (isMaximized.value) return // Disable dragging on small screens (always fullscreen)
if (isMaximized.value || isSmallScreen.value) return
if (first) { if (first) {
// Drag started - save initial position // Drag started - save initial position

View File

@ -7,6 +7,7 @@
...buttonProps, ...buttonProps,
...$attrs, ...$attrs,
}" }"
size="lg"
@click="$emit('click', $event)" @click="$emit('click', $event)"
> >
<template <template

View File

@ -5,7 +5,7 @@
:readonly="props.readOnly" :readonly="props.readOnly"
:leading-icon="props.leadingIcon" :leading-icon="props.leadingIcon"
:ui="{ base: 'peer' }" :ui="{ base: 'peer' }"
:size="isSmallScreen ? 'lg' : 'md'" size="lg"
@change="(e) => $emit('change', e)" @change="(e) => $emit('change', e)"
@blur="(e) => $emit('blur', e)" @blur="(e) => $emit('blur', e)"
@keyup="(e: KeyboardEvent) => $emit('keyup', e)" @keyup="(e: KeyboardEvent) => $emit('keyup', e)"

View File

@ -25,7 +25,7 @@
<div <div
v-show="lastVaults.length" v-show="lastVaults.length"
class="max-w-md w-full sm:px-5" class="w-56"
> >
<div class="font-thin text-sm pb-1 w-full"> <div class="font-thin text-sm pb-1 w-full">
{{ t('lastUsed') }} {{ t('lastUsed') }}

View File

@ -298,6 +298,28 @@ export const useDesktopStore = defineStore('desktopStore', () => {
openDesktopItem(itemType, referenceId) openDesktopItem(itemType, referenceId)
} }
// Build second menu group based on item type
const secondGroup = [
{
label: $i18n.t('desktop.contextMenu.removeFromDesktop'),
icon: 'i-heroicons-x-mark',
onSelect: async () => {
await removeDesktopItemAsync(id)
},
},
]
// Only show uninstall option for extensions
if (itemType === 'extension') {
secondGroup.push({
label: $i18n.t('desktop.contextMenu.uninstall'),
icon: 'i-heroicons-trash',
onSelect: async () => {
onUninstall()
},
})
}
return [ return [
[ [
{ {
@ -306,20 +328,7 @@ export const useDesktopStore = defineStore('desktopStore', () => {
onSelect: handleOpen, onSelect: handleOpen,
}, },
], ],
[ secondGroup,
{
label: $i18n.t('desktop.contextMenu.removeFromDesktop'),
icon: 'i-heroicons-x-mark',
onSelect: async () => {
await removeDesktopItemAsync(id)
},
},
{
label: $i18n.t('desktop.contextMenu.uninstall'),
icon: 'i-heroicons-trash',
onSelect: onUninstall,
},
],
] ]
} }