mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-17 06:30:50 +01:00
use window system
This commit is contained in:
13
src/stores/desktop/de.json
Normal file
13
src/stores/desktop/de.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"contextMenu": {
|
||||
"open": "Öffnen",
|
||||
"removeFromDesktop": "Von Desktop entfernen",
|
||||
"uninstall": "Deinstallieren"
|
||||
},
|
||||
"confirmUninstall": {
|
||||
"title": "Erweiterung deinstallieren",
|
||||
"message": "Möchten Sie die Erweiterung '{name}' wirklich deinstallieren? Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||
"confirm": "Deinstallieren",
|
||||
"cancel": "Abbrechen"
|
||||
}
|
||||
}
|
||||
13
src/stores/desktop/en.json
Normal file
13
src/stores/desktop/en.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"contextMenu": {
|
||||
"open": "Open",
|
||||
"removeFromDesktop": "Remove from Desktop",
|
||||
"uninstall": "Uninstall"
|
||||
},
|
||||
"confirmUninstall": {
|
||||
"title": "Uninstall Extension",
|
||||
"message": "Do you really want to uninstall the extension '{name}'? This action cannot be undone.",
|
||||
"confirm": "Uninstall",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,8 @@ import type {
|
||||
InsertHaexDesktopItems,
|
||||
SelectHaexDesktopItems,
|
||||
} from '~~/src-tauri/database/schemas'
|
||||
import de from './de.json'
|
||||
import en from './en.json'
|
||||
|
||||
export type DesktopItemType = 'extension' | 'file' | 'folder'
|
||||
|
||||
@ -14,8 +16,17 @@ export interface IDesktopItem extends SelectHaexDesktopItems {
|
||||
|
||||
export const useDesktopStore = defineStore('desktopStore', () => {
|
||||
const { currentVault } = storeToRefs(useVaultStore())
|
||||
const workspaceStore = useWorkspaceStore()
|
||||
const { currentWorkspace } = storeToRefs(workspaceStore)
|
||||
const { $i18n } = useNuxtApp()
|
||||
|
||||
$i18n.setLocaleMessage('de', {
|
||||
desktop: de,
|
||||
})
|
||||
$i18n.setLocaleMessage('en', { desktop: en })
|
||||
|
||||
const desktopItems = ref<IDesktopItem[]>([])
|
||||
const selectedItemIds = ref<Set<string>>(new Set())
|
||||
|
||||
const loadDesktopItemsAsync = async () => {
|
||||
if (!currentVault.value?.drizzle) {
|
||||
@ -23,10 +34,16 @@ export const useDesktopStore = defineStore('desktopStore', () => {
|
||||
return
|
||||
}
|
||||
|
||||
if (!currentWorkspace.value) {
|
||||
console.error('Kein Workspace aktiv')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const items = await currentVault.value.drizzle
|
||||
.select()
|
||||
.from(haexDesktopItems)
|
||||
.where(eq(haexDesktopItems.workspaceId, currentWorkspace.value.id))
|
||||
|
||||
desktopItems.value = items
|
||||
} catch (error) {
|
||||
@ -45,8 +62,13 @@ export const useDesktopStore = defineStore('desktopStore', () => {
|
||||
throw new Error('Kein Vault geöffnet')
|
||||
}
|
||||
|
||||
if (!currentWorkspace.value) {
|
||||
throw new Error('Kein Workspace aktiv')
|
||||
}
|
||||
|
||||
try {
|
||||
const newItem: InsertHaexDesktopItems = {
|
||||
workspaceId: currentWorkspace.value.id,
|
||||
itemType: itemType,
|
||||
referenceId: referenceId,
|
||||
positionX: positionX,
|
||||
@ -100,6 +122,7 @@ export const useDesktopStore = defineStore('desktopStore', () => {
|
||||
}
|
||||
|
||||
const removeDesktopItemAsync = async (id: string) => {
|
||||
console.log('removeDesktopItemAsync', id)
|
||||
if (!currentVault.value?.drizzle) {
|
||||
throw new Error('Kein Vault geöffnet')
|
||||
}
|
||||
@ -126,33 +149,128 @@ export const useDesktopStore = defineStore('desktopStore', () => {
|
||||
)
|
||||
}
|
||||
|
||||
const openDesktopItem = (
|
||||
itemType: DesktopItemType,
|
||||
referenceId: string,
|
||||
sourcePosition?: { x: number; y: number; width: number; height: number },
|
||||
) => {
|
||||
if (itemType === 'extension') {
|
||||
const windowManager = useWindowManagerStore()
|
||||
const extensionsStore = useExtensionsStore()
|
||||
|
||||
const extension = extensionsStore.availableExtensions.find(
|
||||
(ext) => ext.id === referenceId,
|
||||
)
|
||||
|
||||
if (extension) {
|
||||
windowManager.openWindow(
|
||||
'extension',
|
||||
extension.id,
|
||||
extension.name,
|
||||
extension.icon || undefined,
|
||||
undefined, // Use default viewport-aware width
|
||||
undefined, // Use default viewport-aware height
|
||||
sourcePosition,
|
||||
)
|
||||
}
|
||||
}
|
||||
// Für später: file und folder handling
|
||||
}
|
||||
|
||||
const uninstallDesktopItem = async (
|
||||
id: string,
|
||||
itemType: DesktopItemType,
|
||||
referenceId: string,
|
||||
) => {
|
||||
if (itemType === 'extension') {
|
||||
try {
|
||||
const extensionsStore = useExtensionsStore()
|
||||
const extension = extensionsStore.availableExtensions.find(
|
||||
(ext) => ext.id === referenceId,
|
||||
)
|
||||
if (!extension) {
|
||||
console.error('Extension nicht gefunden')
|
||||
return
|
||||
}
|
||||
|
||||
// Uninstall the extension
|
||||
await extensionsStore.removeExtensionAsync(
|
||||
extension.publicKey,
|
||||
extension.name,
|
||||
extension.version,
|
||||
)
|
||||
|
||||
// Reload extensions after uninstall
|
||||
await extensionsStore.loadExtensionsAsync()
|
||||
|
||||
// Remove desktop item
|
||||
await removeDesktopItemAsync(id)
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Deinstallieren:', error)
|
||||
}
|
||||
}
|
||||
// Für später: file und folder handling
|
||||
}
|
||||
|
||||
const toggleSelection = (id: string, ctrlKey: boolean = false) => {
|
||||
if (ctrlKey) {
|
||||
// Mit Ctrl: Toggle einzelnes Element
|
||||
if (selectedItemIds.value.has(id)) {
|
||||
selectedItemIds.value.delete(id)
|
||||
} else {
|
||||
selectedItemIds.value.add(id)
|
||||
}
|
||||
} else {
|
||||
// Ohne Ctrl: Nur dieses Element auswählen
|
||||
selectedItemIds.value.clear()
|
||||
selectedItemIds.value.add(id)
|
||||
}
|
||||
}
|
||||
|
||||
const clearSelection = () => {
|
||||
selectedItemIds.value.clear()
|
||||
}
|
||||
|
||||
const isItemSelected = (id: string) => {
|
||||
return selectedItemIds.value.has(id)
|
||||
}
|
||||
|
||||
const selectedItems = computed(() => {
|
||||
return desktopItems.value.filter((item) =>
|
||||
selectedItemIds.value.has(item.id),
|
||||
)
|
||||
})
|
||||
|
||||
const getContextMenuItems = (
|
||||
id: string,
|
||||
itemType: DesktopItemType,
|
||||
referenceId: string,
|
||||
onOpen: () => void,
|
||||
onUninstall: () => void,
|
||||
) => {
|
||||
const handleOpen = () => {
|
||||
openDesktopItem(itemType, referenceId)
|
||||
}
|
||||
|
||||
return [
|
||||
[
|
||||
{
|
||||
label: 'Öffnen',
|
||||
label: $i18n.t('desktop.contextMenu.open'),
|
||||
icon: 'i-heroicons-arrow-top-right-on-square',
|
||||
click: onOpen,
|
||||
onSelect: handleOpen,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Von Desktop entfernen',
|
||||
label: $i18n.t('desktop.contextMenu.removeFromDesktop'),
|
||||
icon: 'i-heroicons-x-mark',
|
||||
click: async () => {
|
||||
onSelect: async () => {
|
||||
await removeDesktopItemAsync(id)
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Deinstallieren',
|
||||
label: $i18n.t('desktop.contextMenu.uninstall'),
|
||||
icon: 'i-heroicons-trash',
|
||||
click: onUninstall,
|
||||
onSelect: onUninstall,
|
||||
},
|
||||
],
|
||||
]
|
||||
@ -160,11 +278,18 @@ export const useDesktopStore = defineStore('desktopStore', () => {
|
||||
|
||||
return {
|
||||
desktopItems,
|
||||
selectedItemIds,
|
||||
selectedItems,
|
||||
loadDesktopItemsAsync,
|
||||
addDesktopItemAsync,
|
||||
updateDesktopItemPositionAsync,
|
||||
removeDesktopItemAsync,
|
||||
getDesktopItemByReference,
|
||||
getContextMenuItems,
|
||||
openDesktopItem,
|
||||
uninstallDesktopItem,
|
||||
toggleSelection,
|
||||
clearSelection,
|
||||
isItemSelected,
|
||||
}
|
||||
})
|
||||
312
src/stores/desktop/windowManager.ts
Normal file
312
src/stores/desktop/windowManager.ts
Normal file
@ -0,0 +1,312 @@
|
||||
import { defineAsyncComponent, type Component } from 'vue'
|
||||
|
||||
export interface IWindow {
|
||||
id: string
|
||||
workspaceId: string // Window belongs to a specific workspace
|
||||
type: 'system' | 'extension'
|
||||
sourceId: string // extensionId or systemWindowId (depends on type)
|
||||
title: string
|
||||
icon?: string
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
isMinimized: boolean
|
||||
zIndex: number
|
||||
// Animation source position (icon position)
|
||||
sourceX?: number
|
||||
sourceY?: number
|
||||
sourceWidth?: number
|
||||
sourceHeight?: number
|
||||
// Animation state
|
||||
isOpening?: boolean
|
||||
isClosing?: boolean
|
||||
}
|
||||
|
||||
export interface SystemWindowDefinition {
|
||||
id: string
|
||||
name: string
|
||||
icon: string
|
||||
component: Component
|
||||
defaultWidth: number
|
||||
defaultHeight: number
|
||||
resizable?: boolean
|
||||
singleton?: boolean // Nur eine Instanz erlaubt?
|
||||
}
|
||||
|
||||
export const useWindowManagerStore = defineStore('windowManager', () => {
|
||||
const workspaceStore = useWorkspaceStore()
|
||||
const { currentWorkspace, workspaces } = storeToRefs(workspaceStore)
|
||||
|
||||
const windows = ref<IWindow[]>([])
|
||||
const activeWindowId = ref<string | null>(null)
|
||||
const nextZIndex = ref(100)
|
||||
|
||||
// System Windows Registry
|
||||
const systemWindows: Record<string, SystemWindowDefinition> = {
|
||||
settings: {
|
||||
id: 'settings',
|
||||
name: 'Settings',
|
||||
icon: 'i-mdi-cog',
|
||||
component: defineAsyncComponent(
|
||||
() => import('@/components/haex/system/settings.vue'),
|
||||
) as Component,
|
||||
defaultWidth: 800,
|
||||
defaultHeight: 600,
|
||||
resizable: true,
|
||||
singleton: true,
|
||||
},
|
||||
marketplace: {
|
||||
id: 'marketplace',
|
||||
name: 'Marketplace',
|
||||
icon: 'i-mdi-store',
|
||||
component: defineAsyncComponent(
|
||||
() => import('@/components/haex/system/marketplace.vue'),
|
||||
),
|
||||
defaultWidth: 1000,
|
||||
defaultHeight: 700,
|
||||
resizable: true,
|
||||
singleton: false,
|
||||
},
|
||||
}
|
||||
|
||||
const getSystemWindow = (id: string): SystemWindowDefinition | undefined => {
|
||||
return systemWindows[id]
|
||||
}
|
||||
|
||||
const getAllSystemWindows = (): SystemWindowDefinition[] => {
|
||||
return Object.values(systemWindows)
|
||||
}
|
||||
|
||||
// Window animation settings
|
||||
const windowAnimationDuration = ref(600) // in milliseconds (matches Tailwind duration-600)
|
||||
|
||||
// Get windows for current workspace only
|
||||
const currentWorkspaceWindows = computed(() => {
|
||||
if (!currentWorkspace.value) return []
|
||||
return windows.value.filter(
|
||||
(w) => w.workspaceId === currentWorkspace.value?.id,
|
||||
)
|
||||
})
|
||||
|
||||
const windowsByWorkspaceId = (workspaceId: string) =>
|
||||
computed(() =>
|
||||
windows.value.filter((window) => window.workspaceId === workspaceId),
|
||||
)
|
||||
|
||||
const moveWindowsToWorkspace = (
|
||||
fromWorkspaceId: string,
|
||||
toWorkspaceId: string,
|
||||
) => {
|
||||
const windowsFrom = windowsByWorkspaceId(fromWorkspaceId)
|
||||
windowsFrom.value.forEach((window) => (window.workspaceId = toWorkspaceId))
|
||||
}
|
||||
|
||||
const openWindow = (
|
||||
type: 'system' | 'extension',
|
||||
sourceId: string,
|
||||
workspaceId: string,
|
||||
title?: string,
|
||||
icon?: string,
|
||||
width?: number,
|
||||
height?: number,
|
||||
sourcePosition?: { x: number; y: number; width: number; height: number },
|
||||
) => {
|
||||
const workspace = workspaces.value.find((w) => w.id === workspaceId)
|
||||
if (!workspace) {
|
||||
console.error('Cannot open window: No active workspace')
|
||||
return
|
||||
}
|
||||
|
||||
// System Window specific handling
|
||||
if (type === 'system') {
|
||||
const systemWindowDef = getSystemWindow(sourceId)
|
||||
if (!systemWindowDef) {
|
||||
console.error(`System window '${sourceId}' not found in registry`)
|
||||
return
|
||||
}
|
||||
|
||||
// Singleton check: If already open, activate existing window
|
||||
if (systemWindowDef.singleton) {
|
||||
const existingWindow = windows.value.find(
|
||||
(w) => w.type === 'system' && w.sourceId === sourceId,
|
||||
)
|
||||
if (existingWindow) {
|
||||
activateWindow(existingWindow.id)
|
||||
return existingWindow.id
|
||||
}
|
||||
}
|
||||
|
||||
// Use system window defaults
|
||||
title = title ?? systemWindowDef.name
|
||||
icon = icon ?? systemWindowDef.icon
|
||||
width = width ?? systemWindowDef.defaultWidth
|
||||
height = height ?? systemWindowDef.defaultHeight
|
||||
}
|
||||
|
||||
// Create new window
|
||||
const windowId = crypto.randomUUID()
|
||||
|
||||
// Calculate viewport-aware size
|
||||
const viewportWidth = window.innerWidth
|
||||
const viewportHeight = window.innerHeight
|
||||
const isMobile = viewportWidth < 768 // Tailwind md breakpoint
|
||||
|
||||
// Default size based on viewport
|
||||
const defaultWidth = isMobile ? Math.floor(viewportWidth * 0.95) : 800
|
||||
const defaultHeight = isMobile ? Math.floor(viewportHeight * 0.85) : 600
|
||||
|
||||
const windowWidth = width ?? defaultWidth
|
||||
const windowHeight = height ?? defaultHeight
|
||||
|
||||
// Calculate centered position with cascading offset (only count windows in current workspace)
|
||||
const offset = currentWorkspaceWindows.value.length * 30
|
||||
const centerX = Math.max(0, (viewportWidth - windowWidth) / 2)
|
||||
const centerY = Math.max(0, (viewportHeight - windowHeight) / 2)
|
||||
const x = Math.min(centerX + offset, viewportWidth - windowWidth)
|
||||
const y = Math.min(centerY + offset, viewportHeight - windowHeight)
|
||||
|
||||
const newWindow: IWindow = {
|
||||
id: windowId,
|
||||
workspaceId: workspace.id,
|
||||
type,
|
||||
sourceId,
|
||||
title: title!,
|
||||
icon,
|
||||
x,
|
||||
y,
|
||||
width: windowWidth,
|
||||
height: windowHeight,
|
||||
isMinimized: false,
|
||||
zIndex: nextZIndex.value++,
|
||||
sourceX: sourcePosition?.x,
|
||||
sourceY: sourcePosition?.y,
|
||||
sourceWidth: sourcePosition?.width,
|
||||
sourceHeight: sourcePosition?.height,
|
||||
isOpening: true,
|
||||
isClosing: false,
|
||||
}
|
||||
|
||||
windows.value.push(newWindow)
|
||||
activeWindowId.value = windowId
|
||||
|
||||
// Remove opening flag after animation
|
||||
setTimeout(() => {
|
||||
const window = windows.value.find((w) => w.id === windowId)
|
||||
if (window) {
|
||||
window.isOpening = false
|
||||
}
|
||||
}, windowAnimationDuration.value)
|
||||
|
||||
return windowId
|
||||
}
|
||||
|
||||
/*****************************************************************************************************
|
||||
* TODO: Momentan werden die Fenster einfach nur geschlossen.
|
||||
* In Zukunft sollte aber vorher ein close event an die Erweiterungen via postMessage geschickt werden,
|
||||
* so dass die Erweiterungen darauf reagieren können, um eventuell ungespeicherte Daten zu sichern
|
||||
*****************************************************************************************************/
|
||||
const closeWindow = (windowId: string) => {
|
||||
const window = windows.value.find((w) => w.id === windowId)
|
||||
if (!window) return
|
||||
|
||||
// Start closing animation
|
||||
window.isClosing = true
|
||||
|
||||
// Remove window after animation completes
|
||||
setTimeout(() => {
|
||||
const index = windows.value.findIndex((w) => w.id === windowId)
|
||||
if (index !== -1) {
|
||||
windows.value.splice(index, 1)
|
||||
|
||||
// If closed window was active, activate the topmost window
|
||||
if (activeWindowId.value === windowId) {
|
||||
if (windows.value.length > 0) {
|
||||
const topWindow = windows.value.reduce((max, w) =>
|
||||
w.zIndex > max.zIndex ? w : max,
|
||||
)
|
||||
activeWindowId.value = topWindow.id
|
||||
} else {
|
||||
activeWindowId.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}, windowAnimationDuration.value)
|
||||
}
|
||||
|
||||
const minimizeWindow = (windowId: string) => {
|
||||
const window = windows.value.find((w) => w.id === windowId)
|
||||
if (window) {
|
||||
window.isMinimized = true
|
||||
}
|
||||
}
|
||||
|
||||
const restoreWindow = (windowId: string) => {
|
||||
const window = windows.value.find((w) => w.id === windowId)
|
||||
if (window) {
|
||||
window.isMinimized = false
|
||||
activateWindow(windowId)
|
||||
}
|
||||
}
|
||||
|
||||
const activateWindow = (windowId: string) => {
|
||||
const window = windows.value.find((w) => w.id === windowId)
|
||||
if (window) {
|
||||
window.zIndex = nextZIndex.value++
|
||||
activeWindowId.value = windowId
|
||||
}
|
||||
}
|
||||
|
||||
const updateWindowPosition = (windowId: string, x: number, y: number) => {
|
||||
const window = windows.value.find((w) => w.id === windowId)
|
||||
if (window) {
|
||||
window.x = x
|
||||
window.y = y
|
||||
}
|
||||
}
|
||||
|
||||
const updateWindowSize = (
|
||||
windowId: string,
|
||||
width: number,
|
||||
height: number,
|
||||
) => {
|
||||
const window = windows.value.find((w) => w.id === windowId)
|
||||
if (window) {
|
||||
window.width = width
|
||||
window.height = height
|
||||
}
|
||||
}
|
||||
|
||||
const isWindowActive = (windowId: string) => {
|
||||
return activeWindowId.value === windowId
|
||||
}
|
||||
|
||||
const getVisibleWindows = computed(() => {
|
||||
return currentWorkspaceWindows.value.filter((w) => !w.isMinimized)
|
||||
})
|
||||
|
||||
const getMinimizedWindows = computed(() => {
|
||||
return currentWorkspaceWindows.value.filter((w) => w.isMinimized)
|
||||
})
|
||||
|
||||
return {
|
||||
activateWindow,
|
||||
activeWindowId,
|
||||
closeWindow,
|
||||
currentWorkspaceWindows,
|
||||
getAllSystemWindows,
|
||||
getMinimizedWindows,
|
||||
getSystemWindow,
|
||||
getVisibleWindows,
|
||||
isWindowActive,
|
||||
minimizeWindow,
|
||||
moveWindowsToWorkspace,
|
||||
openWindow,
|
||||
restoreWindow,
|
||||
updateWindowPosition,
|
||||
updateWindowSize,
|
||||
windowAnimationDuration,
|
||||
windows,
|
||||
windowsByWorkspaceId,
|
||||
}
|
||||
})
|
||||
189
src/stores/desktop/workspace.ts
Normal file
189
src/stores/desktop/workspace.ts
Normal file
@ -0,0 +1,189 @@
|
||||
import { asc, eq } from 'drizzle-orm'
|
||||
import {
|
||||
haexWorkspaces,
|
||||
type InsertHaexWorkspaces,
|
||||
type SelectHaexWorkspaces,
|
||||
} from '~~/src-tauri/database/schemas'
|
||||
import type { Swiper } from 'swiper/types'
|
||||
|
||||
export type IWorkspace = SelectHaexWorkspaces
|
||||
|
||||
export const useWorkspaceStore = defineStore('workspaceStore', () => {
|
||||
const vaultStore = useVaultStore()
|
||||
const windowStore = useWindowManagerStore()
|
||||
|
||||
const { currentVault } = storeToRefs(vaultStore)
|
||||
|
||||
const swiperInstance = ref<Swiper | null>(null)
|
||||
|
||||
const allowSwipe = ref(true)
|
||||
|
||||
// Workspace Overview Mode (GNOME-style)
|
||||
const isOverviewMode = ref(false)
|
||||
|
||||
const workspaces = ref<IWorkspace[]>([])
|
||||
|
||||
const currentWorkspaceIndex = ref(0)
|
||||
|
||||
// Load workspaces from database
|
||||
const loadWorkspacesAsync = async () => {
|
||||
if (!currentVault.value?.drizzle) {
|
||||
console.error('Kein Vault geöffnet')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const items = await currentVault.value.drizzle
|
||||
.select()
|
||||
.from(haexWorkspaces)
|
||||
.orderBy(asc(haexWorkspaces.position))
|
||||
|
||||
workspaces.value = items
|
||||
|
||||
// Create default workspace if none exist
|
||||
if (items.length === 0) {
|
||||
await addWorkspaceAsync('Workspace 1')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Workspaces:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const currentWorkspace = computed(() => {
|
||||
return workspaces.value[currentWorkspaceIndex.value]
|
||||
})
|
||||
|
||||
const addWorkspaceAsync = async (name?: string) => {
|
||||
if (!currentVault.value?.drizzle) {
|
||||
throw new Error('Kein Vault geöffnet')
|
||||
}
|
||||
|
||||
try {
|
||||
const newIndex = workspaces.value.length + 1
|
||||
const newWorkspace: InsertHaexWorkspaces = {
|
||||
name: name || `Workspace ${newIndex}`,
|
||||
position: workspaces.value.length,
|
||||
}
|
||||
|
||||
const result = await currentVault.value.drizzle
|
||||
.insert(haexWorkspaces)
|
||||
.values(newWorkspace)
|
||||
.returning()
|
||||
|
||||
if (result.length > 0 && result[0]) {
|
||||
workspaces.value.push(result[0])
|
||||
currentWorkspaceIndex.value = workspaces.value.length - 1
|
||||
return result[0]
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Hinzufügen des Workspace:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const closeWorkspaceAsync = async (workspaceId: string) => {
|
||||
const openWindows = windowStore.windowsByWorkspaceId(workspaceId)
|
||||
|
||||
for (const window of openWindows.value) {
|
||||
windowStore.closeWindow(window.id)
|
||||
}
|
||||
|
||||
return await removeWorkspaceAsync(workspaceId)
|
||||
}
|
||||
|
||||
const removeWorkspaceAsync = async (workspaceId: string) => {
|
||||
// Don't allow removing the last workspace
|
||||
if (workspaces.value.length <= 1) return
|
||||
|
||||
if (!currentVault.value?.drizzle) {
|
||||
throw new Error('Kein Vault geöffnet')
|
||||
}
|
||||
|
||||
const index = workspaces.value.findIndex((ws) => ws.id === workspaceId)
|
||||
if (index === -1) return
|
||||
|
||||
try {
|
||||
await currentVault.value.drizzle
|
||||
.delete(haexWorkspaces)
|
||||
.where(eq(haexWorkspaces.id, workspaceId))
|
||||
|
||||
workspaces.value.splice(index, 1)
|
||||
|
||||
// Adjust current index if needed
|
||||
if (currentWorkspaceIndex.value >= workspaces.value.length) {
|
||||
currentWorkspaceIndex.value = workspaces.value.length - 1
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Entfernen des Workspace:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const switchToWorkspace = (index: number) => {
|
||||
if (index >= 0 && index < workspaces.value.length) {
|
||||
currentWorkspaceIndex.value = index
|
||||
}
|
||||
}
|
||||
|
||||
const switchToNext = () => {
|
||||
if (currentWorkspaceIndex.value < workspaces.value.length - 1) {
|
||||
currentWorkspaceIndex.value++
|
||||
}
|
||||
}
|
||||
|
||||
const switchToPrevious = () => {
|
||||
if (currentWorkspaceIndex.value > 0) {
|
||||
currentWorkspaceIndex.value--
|
||||
}
|
||||
}
|
||||
|
||||
const renameWorkspaceAsync = async (workspaceId: string, newName: string) => {
|
||||
if (!currentVault.value?.drizzle) {
|
||||
throw new Error('Kein Vault geöffnet')
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await currentVault.value.drizzle
|
||||
.update(haexWorkspaces)
|
||||
.set({ name: newName })
|
||||
.where(eq(haexWorkspaces.id, workspaceId))
|
||||
.returning()
|
||||
|
||||
if (result.length > 0 && result[0]) {
|
||||
const index = workspaces.value.findIndex((ws) => ws.id === workspaceId)
|
||||
if (index !== -1) {
|
||||
workspaces.value[index] = result[0]
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Umbenennen des Workspace:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const slideToWorkspace = (index: number) => {
|
||||
if (swiperInstance.value) {
|
||||
swiperInstance.value.slideTo(index)
|
||||
}
|
||||
isOverviewMode.value = false
|
||||
}
|
||||
|
||||
return {
|
||||
addWorkspaceAsync,
|
||||
allowSwipe,
|
||||
closeWorkspaceAsync,
|
||||
currentWorkspace,
|
||||
currentWorkspaceIndex,
|
||||
isOverviewMode,
|
||||
slideToWorkspace,
|
||||
loadWorkspacesAsync,
|
||||
removeWorkspaceAsync,
|
||||
renameWorkspaceAsync,
|
||||
swiperInstance,
|
||||
switchToNext,
|
||||
switchToPrevious,
|
||||
switchToWorkspace,
|
||||
workspaces,
|
||||
}
|
||||
})
|
||||
@ -31,26 +31,12 @@ export const useExtensionsStore = defineStore('extensionsStore', () => {
|
||||
)
|
||||
})
|
||||
|
||||
/* const { addNotificationAsync } = useNotificationStore() */
|
||||
|
||||
/* const extensionLinks = computed<ISidebarItem[]>(() =>
|
||||
availableExtensions.value
|
||||
.filter((extension) => extension.enabled && extension.installed)
|
||||
.map((extension) => ({
|
||||
icon: extension.icon ?? '',
|
||||
id: extension.id,
|
||||
name: extension.name ?? '',
|
||||
tooltip: extension.name ?? '',
|
||||
to: { name: 'haexExtension', params: { extensionId: extension.id } },
|
||||
})),
|
||||
) */
|
||||
|
||||
const isActive = (id: string) =>
|
||||
/* const isActive = (id: string) =>
|
||||
computed(
|
||||
() =>
|
||||
currentRoute.value.name === 'extension' &&
|
||||
currentRoute.value.params.extensionId === id,
|
||||
)
|
||||
) */
|
||||
|
||||
const extensionEntry = computed(() => {
|
||||
if (
|
||||
@ -65,7 +51,7 @@ export const useExtensionsStore = defineStore('extensionsStore', () => {
|
||||
currentExtension.value.name,
|
||||
currentExtension.value.version,
|
||||
'index.html',
|
||||
currentExtension.value.devServerUrl ?? undefined
|
||||
currentExtension.value.devServerUrl ?? undefined,
|
||||
)
|
||||
})
|
||||
|
||||
@ -286,7 +272,7 @@ export const useExtensionsStore = defineStore('extensionsStore', () => {
|
||||
currentExtensionId,
|
||||
extensionEntry,
|
||||
installAsync,
|
||||
isActive,
|
||||
//isActive,
|
||||
isExtensionInstalledAsync,
|
||||
loadExtensionsAsync,
|
||||
previewManifestAsync,
|
||||
|
||||
@ -92,12 +92,20 @@ export const useVaultStore = defineStore('vaultStore', () => {
|
||||
delete openVaults.value?.[currentVaultId.value]
|
||||
}
|
||||
|
||||
const existsVault = () => {
|
||||
if (!currentVault.value?.drizzle) {
|
||||
console.error('Kein Vault geöffnet')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
closeAsync,
|
||||
createAsync,
|
||||
currentVault,
|
||||
currentVaultId,
|
||||
currentVaultName,
|
||||
existsVault,
|
||||
openAsync,
|
||||
openVaults,
|
||||
}
|
||||
@ -118,6 +126,11 @@ const isSelectQuery = (sql: string) => {
|
||||
return selectRegex.test(sql)
|
||||
}
|
||||
|
||||
const hasReturning = (sql: string) => {
|
||||
const returningRegex = /\bRETURNING\b/i
|
||||
return returningRegex.test(sql)
|
||||
}
|
||||
|
||||
const drizzleCallback = (async (
|
||||
sql: string,
|
||||
params: unknown[],
|
||||
@ -125,18 +138,33 @@ const drizzleCallback = (async (
|
||||
) => {
|
||||
let rows: unknown[] = []
|
||||
|
||||
console.log('drizzleCallback', method, sql, params)
|
||||
|
||||
if (isSelectQuery(sql)) {
|
||||
// SELECT statements
|
||||
rows = await invoke<unknown[]>('sql_select', { sql, params }).catch((e) => {
|
||||
console.error('SQL select Error:', e, sql, params)
|
||||
return []
|
||||
})
|
||||
} else if (hasReturning(sql)) {
|
||||
// INSERT/UPDATE/DELETE with RETURNING → use query
|
||||
rows = await invoke<unknown[]>('sql_query_with_crdt', {
|
||||
sql,
|
||||
params,
|
||||
}).catch((e) => {
|
||||
console.error('SQL query with CRDT Error:', e, sql, params)
|
||||
return []
|
||||
})
|
||||
} else {
|
||||
rows = await invoke<unknown[]>('sql_execute', { sql, params }).catch(
|
||||
(e) => {
|
||||
console.error('SQL execute Error:', e, sql, params)
|
||||
return []
|
||||
},
|
||||
)
|
||||
// INSERT/UPDATE/DELETE without RETURNING → use execute
|
||||
await invoke<unknown[]>('sql_execute_with_crdt', {
|
||||
sql,
|
||||
params,
|
||||
}).catch((e) => {
|
||||
console.error('SQL execute with CRDT Error:', e, sql, params, rows)
|
||||
return []
|
||||
})
|
||||
return { rows: undefined }
|
||||
}
|
||||
|
||||
if (method === 'get') {
|
||||
|
||||
Reference in New Issue
Block a user