desktopicons now with foreign key to extensions

This commit is contained in:
2025-10-26 00:19:15 +02:00
parent 86b65f117d
commit 5ee5ced8c0
10 changed files with 184 additions and 39 deletions

View File

@ -41,18 +41,17 @@
/>
<!-- Snap Dropzones (only visible when window drag near edge) -->
<Transition name="fade">
<div
v-if="showLeftSnapZone"
class="absolute left-0 top-0 bottom-0 w-1/2 bg-blue-500/20 border-2 border-blue-500 pointer-events-none backdrop-blur-sm z-40"
/>
</Transition>
<Transition name="fade">
<div
v-if="showRightSnapZone"
class="absolute right-0 top-0 bottom-0 w-1/2 bg-blue-500/20 border-2 border-blue-500 pointer-events-none backdrop-blur-sm z-40"
/>
</Transition>
<div
class="absolute left-0 top-0 bottom-0 border-blue-500 pointer-events-none backdrop-blur-sm z-40 transition-all duration-500"
:class="showLeftSnapZone ? 'w-1/2 bg-blue-500/20 border-2' : 'w-0'"
/>
<div
class="absolute right-0 top-0 bottom-0 border-blue-500 pointer-events-none backdrop-blur-sm z-40 transition-all duration-500 ease-in-out"
:class="showRightSnapZone ? 'w-1/2 bg-blue-500/20 border-2' : 'w-0'"
/>
<!-- </Transition> -->
<!-- Area Selection Box -->
<div
@ -494,6 +493,34 @@ const handleWindowDragStart = (windowId: string) => {
const handleWindowDragEnd = async () => {
console.log('[Desktop] handleWindowDragEnd')
// Check if window should snap to left or right
const draggingWindowId = windowManager.draggingWindowId
if (draggingWindowId) {
if (showLeftSnapZone.value) {
// Snap to left half
windowManager.updateWindowPosition(draggingWindowId, 0, 0)
windowManager.updateWindowSize(
draggingWindowId,
viewportWidth.value / 2,
viewportHeight.value,
)
} else if (showRightSnapZone.value) {
// Snap to right half
windowManager.updateWindowPosition(
draggingWindowId,
viewportWidth.value / 2,
0,
)
windowManager.updateWindowSize(
draggingWindowId,
viewportWidth.value / 2,
viewportHeight.value,
)
}
}
isWindowDragging.value = false
windowManager.draggingWindowId = null // Clear from store
allowSwipe.value = true // Re-enable Swiper after drag

View File

@ -55,9 +55,23 @@
</ul>
</template>
</UPopover>
<!-- Uninstall Confirmation Dialog -->
<UiDialogConfirm
v-model:open="showUninstallDialog"
:title="t('uninstall.confirm.title')"
:description="t('uninstall.confirm.description', { name: extensionToUninstall?.name || '' })"
:confirm-label="t('uninstall.confirm.button')"
confirm-icon="i-heroicons-trash"
@confirm="confirmUninstall"
/>
</template>
<script setup lang="ts">
defineOptions({
inheritAttrs: false,
})
const extensionStore = useExtensionsStore()
const windowManagerStore = useWindowManagerStore()
@ -65,6 +79,10 @@ const { t } = useI18n()
const open = ref(false)
// Uninstall dialog state
const showUninstallDialog = ref(false)
const extensionToUninstall = ref<LauncherItem | null>(null)
// Unified launcher item type
interface LauncherItem {
id: string
@ -127,17 +145,44 @@ const openItem = async (item: LauncherItem) => {
}
}
// Uninstall extension
// Uninstall extension - shows confirmation dialog first
const uninstallExtension = async (item: LauncherItem) => {
extensionToUninstall.value = item
showUninstallDialog.value = true
}
// Confirm uninstall - actually removes the extension
const confirmUninstall = async () => {
if (!extensionToUninstall.value) return
try {
const extension = extensionStore.availableExtensions.find(ext => ext.id === item.id)
const extension = extensionStore.availableExtensions.find(
(ext) => ext.id === extensionToUninstall.value!.id,
)
if (!extension) return
// Close all windows of this extension first
const extensionWindows = windowManagerStore.windows.filter(
(win) => win.type === 'extension' && win.sourceId === extension.id,
)
for (const win of extensionWindows) {
windowManagerStore.closeWindow(win.id)
}
// Uninstall the extension
await extensionStore.removeExtensionAsync(
extension.publicKey,
extension.name,
extension.version
extension.version,
)
// Refresh available extensions list
await extensionStore.loadExtensionsAsync()
// Close dialog and reset state
showUninstallDialog.value = false
extensionToUninstall.value = null
} catch (error) {
console.error('Failed to uninstall extension:', error)
}
@ -149,8 +194,8 @@ const getContextMenuItems = (item: LauncherItem) => {
{
label: t('contextMenu.open'),
icon: 'i-heroicons-arrow-top-right-on-square',
click: () => openItem(item),
}
onSelect: () => openItem(item),
},
]
// Add uninstall option for extensions
@ -158,7 +203,7 @@ const getContextMenuItems = (item: LauncherItem) => {
items.push({
label: t('contextMenu.uninstall'),
icon: 'i-heroicons-trash',
click: () => uninstallExtension(item),
onSelect: () => uninstallExtension(item),
})
}
@ -171,7 +216,10 @@ const handleDragStart = (event: DragEvent, item: LauncherItem) => {
// Store the launcher item data
event.dataTransfer.effectAllowed = 'copy'
event.dataTransfer.setData('application/haex-launcher-item', JSON.stringify(item))
event.dataTransfer.setData(
'application/haex-launcher-item',
JSON.stringify(item),
)
// Set drag image (optional - uses default if not set)
const dragImage = event.target as HTMLElement
@ -192,6 +240,11 @@ de:
contextMenu:
open: Öffnen
uninstall: Deinstallieren
uninstall:
confirm:
title: Erweiterung deinstallieren
description: Möchtest du wirklich "{name}" deinstallieren? Diese Aktion kann nicht rückgängig gemacht werden.
button: Deinstallieren
en:
disabled: Disabled
@ -199,4 +252,9 @@ en:
contextMenu:
open: Open
uninstall: Uninstall
uninstall:
confirm:
title: Uninstall Extension
description: Do you really want to uninstall "{name}"? This action cannot be undone.
button: Uninstall
</i18n>

View File

@ -8,7 +8,7 @@
>
<div class="flex items-start gap-4">
<!-- Icon -->
<div class="flex-shrink-0">
<div class="shrink-0">
<div
v-if="extension.icon"
class="w-16 h-16 rounded-lg bg-primary/10 flex items-center justify-center"