diff --git a/package.json b/package.json
index afeeb01..1231e26 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "haex-hub",
"private": true,
- "version": "0.1.6",
+ "version": "0.1.7",
"type": "module",
"scripts": {
"build": "nuxt build",
diff --git a/src-tauri/database/migrations/0002_loose_quasimodo.sql b/src-tauri/database/migrations/0002_loose_quasimodo.sql
new file mode 100644
index 0000000..f8aafe5
--- /dev/null
+++ b/src-tauri/database/migrations/0002_loose_quasimodo.sql
@@ -0,0 +1,13 @@
+CREATE TABLE `haex_devices` (
+ `id` text PRIMARY KEY NOT NULL,
+ `device_id` text NOT NULL,
+ `name` text NOT NULL,
+ `created_at` text DEFAULT (CURRENT_TIMESTAMP),
+ `updated_at` integer,
+ `haex_timestamp` text
+);
+--> statement-breakpoint
+CREATE UNIQUE INDEX `haex_devices_device_id_unique` ON `haex_devices` (`device_id`);--> statement-breakpoint
+DROP INDEX `haex_settings_key_type_value_unique`;--> statement-breakpoint
+ALTER TABLE `haex_settings` ADD `device_id` text REFERENCES haex_devices(id);--> statement-breakpoint
+CREATE UNIQUE INDEX `haex_settings_device_id_key_type_unique` ON `haex_settings` (`device_id`,`key`,`type`);
\ No newline at end of file
diff --git a/src-tauri/database/migrations/meta/0002_snapshot.json b/src-tauri/database/migrations/meta/0002_snapshot.json
new file mode 100644
index 0000000..76743d9
--- /dev/null
+++ b/src-tauri/database/migrations/meta/0002_snapshot.json
@@ -0,0 +1,774 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "3aedf10c-2266-40f4-8549-0ff8b0588853",
+ "prevId": "10bec43a-4227-483e-b1c1-fd50ae32bb96",
+ "tables": {
+ "haex_crdt_configs": {
+ "name": "haex_crdt_configs",
+ "columns": {
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "haex_crdt_logs": {
+ "name": "haex_crdt_logs",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "haex_timestamp": {
+ "name": "haex_timestamp",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "table_name": {
+ "name": "table_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "row_pks": {
+ "name": "row_pks",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "op_type": {
+ "name": "op_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "column_name": {
+ "name": "column_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "new_value": {
+ "name": "new_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "old_value": {
+ "name": "old_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "idx_haex_timestamp": {
+ "name": "idx_haex_timestamp",
+ "columns": [
+ "haex_timestamp"
+ ],
+ "isUnique": false
+ },
+ "idx_table_row": {
+ "name": "idx_table_row",
+ "columns": [
+ "table_name",
+ "row_pks"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "haex_crdt_snapshots": {
+ "name": "haex_crdt_snapshots",
+ "columns": {
+ "snapshot_id": {
+ "name": "snapshot_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created": {
+ "name": "created",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "epoch_hlc": {
+ "name": "epoch_hlc",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "location_url": {
+ "name": "location_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "file_size_bytes": {
+ "name": "file_size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "haex_desktop_items": {
+ "name": "haex_desktop_items",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "workspace_id": {
+ "name": "workspace_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "item_type": {
+ "name": "item_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "extension_id": {
+ "name": "extension_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "system_window_id": {
+ "name": "system_window_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "position_x": {
+ "name": "position_x",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 0
+ },
+ "position_y": {
+ "name": "position_y",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 0
+ },
+ "haex_timestamp": {
+ "name": "haex_timestamp",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "haex_desktop_items_workspace_id_haex_workspaces_id_fk": {
+ "name": "haex_desktop_items_workspace_id_haex_workspaces_id_fk",
+ "tableFrom": "haex_desktop_items",
+ "tableTo": "haex_workspaces",
+ "columnsFrom": [
+ "workspace_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "haex_desktop_items_extension_id_haex_extensions_id_fk": {
+ "name": "haex_desktop_items_extension_id_haex_extensions_id_fk",
+ "tableFrom": "haex_desktop_items",
+ "tableTo": "haex_extensions",
+ "columnsFrom": [
+ "extension_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {
+ "item_reference": {
+ "name": "item_reference",
+ "value": "(\"haex_desktop_items\".\"item_type\" = 'extension' AND \"haex_desktop_items\".\"extension_id\" IS NOT NULL AND \"haex_desktop_items\".\"system_window_id\" IS NULL) OR (\"haex_desktop_items\".\"item_type\" = 'system' AND \"haex_desktop_items\".\"system_window_id\" IS NOT NULL AND \"haex_desktop_items\".\"extension_id\" IS NULL) OR (\"haex_desktop_items\".\"item_type\" = 'file' AND \"haex_desktop_items\".\"system_window_id\" IS NOT NULL AND \"haex_desktop_items\".\"extension_id\" IS NULL) OR (\"haex_desktop_items\".\"item_type\" = 'folder' AND \"haex_desktop_items\".\"system_window_id\" IS NOT NULL AND \"haex_desktop_items\".\"extension_id\" IS NULL)"
+ }
+ }
+ },
+ "haex_devices": {
+ "name": "haex_devices",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "device_id": {
+ "name": "device_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "(CURRENT_TIMESTAMP)"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "haex_timestamp": {
+ "name": "haex_timestamp",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "haex_devices_device_id_unique": {
+ "name": "haex_devices_device_id_unique",
+ "columns": [
+ "device_id"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "haex_extension_permissions": {
+ "name": "haex_extension_permissions",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "extension_id": {
+ "name": "extension_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "resource_type": {
+ "name": "resource_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "action": {
+ "name": "action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "target": {
+ "name": "target",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "constraints": {
+ "name": "constraints",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'denied'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "(CURRENT_TIMESTAMP)"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "haex_timestamp": {
+ "name": "haex_timestamp",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "haex_extension_permissions_extension_id_resource_type_action_target_unique": {
+ "name": "haex_extension_permissions_extension_id_resource_type_action_target_unique",
+ "columns": [
+ "extension_id",
+ "resource_type",
+ "action",
+ "target"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "haex_extension_permissions_extension_id_haex_extensions_id_fk": {
+ "name": "haex_extension_permissions_extension_id_haex_extensions_id_fk",
+ "tableFrom": "haex_extension_permissions",
+ "tableTo": "haex_extensions",
+ "columnsFrom": [
+ "extension_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "haex_extensions": {
+ "name": "haex_extensions",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "public_key": {
+ "name": "public_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "version": {
+ "name": "version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "author": {
+ "name": "author",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "entry": {
+ "name": "entry",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'index.html'"
+ },
+ "homepage": {
+ "name": "homepage",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "icon": {
+ "name": "icon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "signature": {
+ "name": "signature",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "single_instance": {
+ "name": "single_instance",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "haex_timestamp": {
+ "name": "haex_timestamp",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "haex_extensions_public_key_name_unique": {
+ "name": "haex_extensions_public_key_name_unique",
+ "columns": [
+ "public_key",
+ "name"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "haex_notifications": {
+ "name": "haex_notifications",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "alt": {
+ "name": "alt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "date": {
+ "name": "date",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "icon": {
+ "name": "icon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "read": {
+ "name": "read",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "text": {
+ "name": "text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "haex_timestamp": {
+ "name": "haex_timestamp",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "haex_settings": {
+ "name": "haex_settings",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "device_id": {
+ "name": "device_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "haex_timestamp": {
+ "name": "haex_timestamp",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "haex_settings_device_id_key_type_unique": {
+ "name": "haex_settings_device_id_key_type_unique",
+ "columns": [
+ "device_id",
+ "key",
+ "type"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "haex_settings_device_id_haex_devices_id_fk": {
+ "name": "haex_settings_device_id_haex_devices_id_fk",
+ "tableFrom": "haex_settings",
+ "tableTo": "haex_devices",
+ "columnsFrom": [
+ "device_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "haex_workspaces": {
+ "name": "haex_workspaces",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "device_id": {
+ "name": "device_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "position": {
+ "name": "position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 0
+ },
+ "background": {
+ "name": "background",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "haex_timestamp": {
+ "name": "haex_timestamp",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "haex_workspaces_position_unique": {
+ "name": "haex_workspaces_position_unique",
+ "columns": [
+ "position"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
\ No newline at end of file
diff --git a/src-tauri/database/migrations/meta/_journal.json b/src-tauri/database/migrations/meta/_journal.json
index 0099206..c0a785b 100644
--- a/src-tauri/database/migrations/meta/_journal.json
+++ b/src-tauri/database/migrations/meta/_journal.json
@@ -15,6 +15,13 @@
"when": 1762122405562,
"tag": "0001_furry_brother_voodoo",
"breakpoints": true
+ },
+ {
+ "idx": 2,
+ "version": "6",
+ "when": 1762263814375,
+ "tag": "0002_loose_quasimodo",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/src-tauri/database/vault.db b/src-tauri/database/vault.db
index 1d59aed..7f46bed 100644
Binary files a/src-tauri/database/vault.db and b/src-tauri/database/vault.db differ
diff --git a/src/components/haex/desktop/icon.vue b/src/components/haex/desktop/icon.vue
index 7e1fd4b..cdad25c 100644
--- a/src/components/haex/desktop/icon.vue
+++ b/src/components/haex/desktop/icon.vue
@@ -24,29 +24,25 @@
-
![]()
-
()
const desktopStore = useDesktopStore()
+const { effectiveIconSize } = storeToRefs(desktopStore)
const showUninstallDialog = ref(false)
const { t } = useI18n()
const isSelected = computed(() => desktopStore.isItemSelected(props.id))
+const containerSize = computed(() => effectiveIconSize.value) // Container size
+const innerIconSize = computed(() => effectiveIconSize.value * 0.7) // Inner icon is 70% of container
const handleClick = (e: MouseEvent) => {
// Prevent selection during drag
@@ -131,9 +131,40 @@ const isDragging = ref(false)
const offsetX = ref(0)
const offsetY = ref(0)
-// Icon dimensions (approximate)
-const iconWidth = 120 // Matches design in template
-const iconHeight = 140
+// Track actual icon dimensions dynamically
+const { width: iconWidth, height: iconHeight } = useElementSize(draggableEl)
+
+// Re-center icon position when dimensions are measured
+watch([iconWidth, iconHeight], async ([width, height]) => {
+ if (width > 0 && height > 0) {
+ console.log('📐 Icon dimensions measured:', {
+ label: props.label,
+ width,
+ height,
+ currentPosition: { x: x.value, y: y.value },
+ gridCellSize: desktopStore.gridCellSize,
+ })
+
+ // Re-snap to grid with actual dimensions to ensure proper centering
+ const snapped = desktopStore.snapToGrid(x.value, y.value, width, height)
+
+ console.log('📍 Snapped position:', {
+ label: props.label,
+ oldPosition: { x: x.value, y: y.value },
+ newPosition: snapped,
+ })
+
+ const oldX = x.value
+ const oldY = y.value
+ x.value = snapped.x
+ y.value = snapped.y
+
+ // Save corrected position to database if it changed
+ if (oldX !== snapped.x || oldY !== snapped.y) {
+ emit('positionChanged', props.id, snapped.x, snapped.y)
+ }
+ }
+}, { once: true }) // Only run once when dimensions are first measured
const style = computed(() => ({
position: 'absolute' as const,
@@ -146,7 +177,7 @@ const handlePointerDown = (e: PointerEvent) => {
if (!draggableEl.value || !draggableEl.value.parentElement) return
isDragging.value = true
- emit('dragStart', props.id, props.itemType, props.referenceId)
+ emit('dragStart', props.id, props.itemType, props.referenceId, iconWidth.value, iconHeight.value, x.value, y.value)
// Get parent offset to convert from viewport coordinates to parent-relative coordinates
const parentRect = draggableEl.value.parentElement.getBoundingClientRect()
@@ -165,8 +196,12 @@ const handlePointerMove = (e: PointerEvent) => {
const newX = e.clientX - parentRect.left - offsetX.value
const newY = e.clientY - parentRect.top - offsetY.value
+ // Clamp y position to minimum 0 (parent is already below header)
x.value = newX
- y.value = newY
+ y.value = Math.max(0, newY)
+
+ // Emit current position during drag
+ emit('dragging', props.id, x.value, y.value)
}
const handlePointerUp = (e: PointerEvent) => {
@@ -177,10 +212,15 @@ const handlePointerUp = (e: PointerEvent) => {
draggableEl.value.releasePointerCapture(e.pointerId)
}
+ // Snap to grid with icon dimensions
+ const snapped = desktopStore.snapToGrid(x.value, y.value, iconWidth.value, iconHeight.value)
+ x.value = snapped.x
+ y.value = snapped.y
+
// Snap icon to viewport bounds if outside
if (viewportSize) {
- const maxX = Math.max(0, viewportSize.width.value - iconWidth)
- const maxY = Math.max(0, viewportSize.height.value - iconHeight)
+ const maxX = Math.max(0, viewportSize.width.value - iconWidth.value)
+ const maxY = Math.max(0, viewportSize.height.value - iconHeight.value)
x.value = Math.max(0, Math.min(maxX, x.value))
y.value = Math.max(0, Math.min(maxY, y.value))
}
diff --git a/src/components/haex/desktop/index.vue b/src/components/haex/desktop/index.vue
index 68dbe8f..8868b5b 100644
--- a/src/components/haex/desktop/index.vue
+++ b/src/components/haex/desktop/index.vue
@@ -32,13 +32,15 @@
@dragover.prevent="handleDragOver"
@drop.prevent="handleDrop($event, workspace.id)"
>
-
+
@@ -79,6 +81,7 @@
class="no-swipe"
@position-changed="handlePositionChanged"
@drag-start="handleDragStart"
+ @dragging="handleDragging"
@drag-end="handleDragEnd"
/>
@@ -249,8 +252,6 @@ const {
const { getWorkspaceBackgroundStyle, getWorkspaceContextMenuItems } =
workspaceStore
-const { x: mouseX } = useMouse()
-
const desktopEl = useTemplateRef('desktopEl')
// Track desktop viewport size reactively
@@ -284,9 +285,41 @@ const selectionBoxStyle = computed(() => {
// Drag state for desktop icons
const isDragging = ref(false)
-const currentDraggedItemId = ref()
-const currentDraggedItemType = ref()
-const currentDraggedReferenceId = ref()
+const currentDraggedItem = reactive({
+ id: '',
+ itemType: '',
+ referenceId: '',
+ width: 0,
+ height: 0,
+ x: 0,
+ y: 0,
+})
+
+// Track mouse position for showing drop target
+const { x: mouseX, y: mouseY } = useMouse()
+
+const dropTargetZone = computed(() => {
+ if (!isDragging.value) return null
+
+ // Use the actual icon position during drag, not the mouse position
+ const iconX = currentDraggedItem.x
+ const iconY = currentDraggedItem.y
+
+ // Use snapToGrid to get the exact position where the icon will land
+ const snapped = desktopStore.snapToGrid(
+ iconX,
+ iconY,
+ currentDraggedItem.width || undefined,
+ currentDraggedItem.height || undefined,
+ )
+
+ return {
+ x: snapped.x,
+ y: snapped.y,
+ width: currentDraggedItem.width || desktopStore.gridCellSize,
+ height: currentDraggedItem.height || desktopStore.gridCellSize,
+ }
+})
// Window drag state for snap zones
const isWindowDragging = ref(false)
@@ -378,20 +411,43 @@ const handlePositionChanged = async (id: string, x: number, y: number) => {
}
}
-const handleDragStart = (id: string, itemType: string, referenceId: string) => {
+const handleDragStart = (
+ id: string,
+ itemType: string,
+ referenceId: string,
+ width: number,
+ height: number,
+ x: number,
+ y: number,
+) => {
isDragging.value = true
- currentDraggedItemId.value = id
- currentDraggedItemType.value = itemType
- currentDraggedReferenceId.value = referenceId
+ currentDraggedItem.id = id
+ currentDraggedItem.itemType = itemType
+ currentDraggedItem.referenceId = referenceId
+ currentDraggedItem.width = width
+ currentDraggedItem.height = height
+ currentDraggedItem.x = x
+ currentDraggedItem.y = y
allowSwipe.value = false // Disable Swiper during icon drag
}
+const handleDragging = (id: string, x: number, y: number) => {
+ if (currentDraggedItem.id === id) {
+ currentDraggedItem.x = x
+ currentDraggedItem.y = y
+ }
+}
+
const handleDragEnd = async () => {
// Cleanup drag state
isDragging.value = false
- currentDraggedItemId.value = undefined
- currentDraggedItemType.value = undefined
- currentDraggedReferenceId.value = undefined
+ currentDraggedItem.id = ''
+ currentDraggedItem.itemType = ''
+ currentDraggedItem.referenceId = ''
+ currentDraggedItem.width = 0
+ currentDraggedItem.height = 0
+ currentDraggedItem.x = 0
+ currentDraggedItem.y = 0
allowSwipe.value = true // Re-enable Swiper after drag
}
@@ -426,15 +482,18 @@ const handleDrop = async (event: DragEvent, workspaceId: string) => {
const desktopRect = (
event.currentTarget as HTMLElement
).getBoundingClientRect()
- const x = Math.max(0, event.clientX - desktopRect.left - 32) // Center icon (64px / 2)
- const y = Math.max(0, event.clientY - desktopRect.top - 32)
+ const rawX = Math.max(0, event.clientX - desktopRect.left - 32) // Center icon (64px / 2)
+ const rawY = Math.max(0, event.clientY - desktopRect.top - 32)
+
+ // Snap to grid
+ const snapped = desktopStore.snapToGrid(rawX, rawY)
// Create desktop icon on the specific workspace
await desktopStore.addDesktopItemAsync(
item.type as DesktopItemType,
item.id,
- x,
- y,
+ snapped.x,
+ snapped.y,
workspaceId,
)
} catch (error) {
diff --git a/src/components/haex/icon.vue b/src/components/haex/icon.vue
new file mode 100644
index 0000000..14419f1
--- /dev/null
+++ b/src/components/haex/icon.vue
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/haex/system/settings.vue b/src/components/haex/system/settings.vue
index 1f6eac5..a272891 100644
--- a/src/components/haex/system/settings.vue
+++ b/src/components/haex/system/settings.vue
@@ -47,6 +47,21 @@
/>
+
+
+
{{ t('desktopGrid.title') }}
+
+
+ {{ t('desktopGrid.iconSize.label') }}
+
+
+
+
@@ -63,6 +78,7 @@ import {
remove,
} from '@tauri-apps/plugin-fs'
import { appLocalDataDir } from '@tauri-apps/api/path'
+import { DesktopIconSizePreset } from '~/stores/vault/settings'
const { t, setLocale } = useI18n()
@@ -104,8 +120,40 @@ const workspaceStore = useWorkspaceStore()
const { currentWorkspace } = storeToRefs(workspaceStore)
const { updateWorkspaceBackgroundAsync } = workspaceStore
+const desktopStore = useDesktopStore()
+const { iconSizePreset } = storeToRefs(desktopStore)
+const { syncDesktopIconSizeAsync, updateDesktopIconSizeAsync } = desktopStore
+
+// Icon size preset options
+const iconSizePresetOptions = [
+ {
+ label: t('desktopGrid.iconSize.presets.small'),
+ value: DesktopIconSizePreset.small,
+ },
+ {
+ label: t('desktopGrid.iconSize.presets.medium'),
+ value: DesktopIconSizePreset.medium,
+ },
+ {
+ label: t('desktopGrid.iconSize.presets.large'),
+ value: DesktopIconSizePreset.large,
+ },
+ {
+ label: t('desktopGrid.iconSize.presets.extraLarge'),
+ value: DesktopIconSizePreset.extraLarge,
+ },
+]
+
+// Watch for icon size preset changes and update DB
+watch(iconSizePreset, async (newPreset) => {
+ if (newPreset) {
+ await updateDesktopIconSizeAsync(newPreset)
+ }
+})
+
onMounted(async () => {
await readDeviceNameAsync()
+ await syncDesktopIconSizeAsync()
})
const onUpdateDeviceNameAsync = async () => {
@@ -295,6 +343,22 @@ de:
label: Hintergrund entfernen
success: Hintergrund erfolgreich entfernt
error: Fehler beim Entfernen des Hintergrunds
+ desktopGrid:
+ title: Desktop-Raster
+ columns:
+ label: Spalten
+ unit: Spalten
+ rows:
+ label: Zeilen
+ unit: Zeilen
+ iconSize:
+ label: Icon-Größe
+ presets:
+ small: Klein
+ medium: Mittel
+ large: Groß
+ extraLarge: Sehr groß
+ unit: px
en:
language: Language
design: Design
@@ -322,4 +386,20 @@ en:
label: Remove Background
success: Background successfully removed
error: Error removing background
+ desktopGrid:
+ title: Desktop Grid
+ columns:
+ label: Columns
+ unit: columns
+ rows:
+ label: Rows
+ unit: rows
+ iconSize:
+ label: Icon Size
+ presets:
+ small: Small
+ medium: Medium
+ large: Large
+ extraLarge: Extra Large
+ unit: px
diff --git a/src/components/haex/window/index.vue b/src/components/haex/window/index.vue
index 4ffa3f2..cffa9f2 100644
--- a/src/components/haex/window/index.vue
+++ b/src/components/haex/window/index.vue
@@ -26,10 +26,10 @@
>
-
diff --git a/src/components/haex/workspace/card.vue b/src/components/haex/workspace/card.vue
index f5c8dca..2e3e6db 100644
--- a/src/components/haex/workspace/card.vue
+++ b/src/components/haex/workspace/card.vue
@@ -25,17 +25,70 @@
/>
+
+
+
+
+
+
+
+
+ +{{ remainingCount }}
+
+
+
+
+
+ {{ t('noWindows') }}
+
+
+
+de:
+ noWindows: Keine Fenster geöffnet
+en:
+ noWindows: No windows open
+
diff --git a/src/components/haex/workspace/drawer.vue b/src/components/haex/workspace/drawer.vue
new file mode 100644
index 0000000..b3f65b9
--- /dev/null
+++ b/src/components/haex/workspace/drawer.vue
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+de:
+ add: Workspace hinzufügen
+en:
+ add: Add Workspace
+
diff --git a/src/database/schemas/haex.ts b/src/database/schemas/haex.ts
index e6bb163..07c1711 100644
--- a/src/database/schemas/haex.ts
+++ b/src/database/schemas/haex.ts
@@ -24,17 +24,42 @@ export const withCrdtColumns = <
haexTimestamp: text(crdtColumnNames.haexTimestamp),
})
+export const haexDevices = sqliteTable(
+ tableNames.haex.devices.name,
+ withCrdtColumns({
+ id: text(tableNames.haex.devices.columns.id)
+ .$defaultFn(() => crypto.randomUUID())
+ .primaryKey(),
+ deviceId: text(tableNames.haex.devices.columns.deviceId)
+ .notNull()
+ .unique(),
+ name: text(tableNames.haex.devices.columns.name).notNull(),
+ createdAt: text(tableNames.haex.devices.columns.createdAt).default(
+ sql`(CURRENT_TIMESTAMP)`,
+ ),
+ updatedAt: integer(tableNames.haex.devices.columns.updatedAt, {
+ mode: 'timestamp',
+ }).$onUpdate(() => new Date()),
+ }),
+)
+export type InsertHaexDevices = typeof haexDevices.$inferInsert
+export type SelectHaexDevices = typeof haexDevices.$inferSelect
+
export const haexSettings = sqliteTable(
tableNames.haex.settings.name,
withCrdtColumns({
- id: text()
+ id: text(tableNames.haex.settings.columns.id)
.$defaultFn(() => crypto.randomUUID())
.primaryKey(),
- key: text(),
- type: text(),
- value: text(),
+ deviceId: text(tableNames.haex.settings.columns.deviceId).references(
+ (): AnySQLiteColumn => haexDevices.id,
+ { onDelete: 'cascade' },
+ ),
+ key: text(tableNames.haex.settings.columns.key),
+ type: text(tableNames.haex.settings.columns.type),
+ value: text(tableNames.haex.settings.columns.value),
}),
- (table) => [unique().on(table.key, table.type, table.value)],
+ (table) => [unique().on(table.deviceId, table.key, table.type)],
)
export type InsertHaexSettings = typeof haexSettings.$inferInsert
export type SelectHaexSettings = typeof haexSettings.$inferSelect
diff --git a/src/database/tableNames.json b/src/database/tableNames.json
index 2a77b72..813bbb6 100644
--- a/src/database/tableNames.json
+++ b/src/database/tableNames.json
@@ -4,6 +4,7 @@
"name": "haex_settings",
"columns": {
"id": "id",
+ "deviceId": "device_id",
"key": "key",
"type": "type",
"value": "value",
@@ -89,6 +90,18 @@
"haexTimestamp": "haex_timestamp"
}
},
+ "devices": {
+ "name": "haex_devices",
+ "columns": {
+ "id": "id",
+ "deviceId": "device_id",
+ "name": "name",
+ "createdAt": "created_at",
+ "updatedAt": "updated_at",
+
+ "haexTimestamp": "haex_timestamp"
+ }
+ },
"crdt": {
"logs": {
diff --git a/src/layouts/default.vue b/src/layouts/default.vue
index c4f51a6..fa1131a 100644
--- a/src/layouts/default.vue
+++ b/src/layouts/default.vue
@@ -59,47 +59,7 @@
-
-
-
-
- Workspaces
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -116,15 +76,7 @@ const { showWindowOverview, openWindowsCount } = storeToRefs(
useWindowManagerStore(),
)
-const workspaceStore = useWorkspaceStore()
-const { workspaces, isOverviewMode } = storeToRefs(workspaceStore)
-
-const handleAddWorkspace = async () => {
- const workspace = await workspaceStore.addWorkspaceAsync()
- nextTick(() => {
- workspaceStore.slideToWorkspace(workspace?.id)
- })
-}
+const { isOverviewMode } = storeToRefs(useWorkspaceStore())
// Measure header height and store it in UI store
const headerEl = useTemplateRef('headerEl')
@@ -140,15 +92,11 @@ watch(height, (newHeight) => {
de:
search:
label: Suche
-
workspaces:
label: Workspaces
- add: Workspace hinzufügen
en:
search:
label: Search
-
workspaces:
label: Workspaces
- add: Add Workspace
diff --git a/src/pages/vault.vue b/src/pages/vault.vue
index b2007ef..b9c7871 100644
--- a/src/pages/vault.vue
+++ b/src/pages/vault.vue
@@ -53,6 +53,7 @@ const { addDeviceNameAsync } = useDeviceStore()
const { deviceId } = storeToRefs(useDeviceStore())
const { syncLocaleAsync, syncThemeAsync, syncVaultNameAsync } =
useVaultSettingsStore()
+const { syncDesktopIconSizeAsync } = useDesktopStore()
onMounted(async () => {
try {
@@ -62,6 +63,7 @@ onMounted(async () => {
syncLocaleAsync(),
syncThemeAsync(),
syncVaultNameAsync(),
+ syncDesktopIconSizeAsync(),
loadExtensionsAsync(),
readNotificationsAsync(),
])
diff --git a/src/stores/desktop/index.ts b/src/stores/desktop/index.ts
index 1e273b3..2bd0465 100644
--- a/src/stores/desktop/index.ts
+++ b/src/stores/desktop/index.ts
@@ -1,9 +1,13 @@
import { eq } from 'drizzle-orm'
-import { haexDesktopItems } from '~/database/schemas'
+import { haexDesktopItems, haexDevices } from '~/database/schemas'
import type {
InsertHaexDesktopItems,
SelectHaexDesktopItems,
} from '~/database/schemas'
+import {
+ DesktopIconSizePreset,
+ iconSizePresetValues,
+} from '~/stores/vault/settings'
import de from './de.json'
import en from './en.json'
@@ -20,6 +24,10 @@ export const useDesktopStore = defineStore('desktopStore', () => {
const workspaceStore = useWorkspaceStore()
const { currentWorkspace } = storeToRefs(workspaceStore)
const { $i18n } = useNuxtApp()
+ const uiStore = useUiStore()
+ const { isSmallScreen } = storeToRefs(uiStore)
+ const deviceStore = useDeviceStore()
+ const settingsStore = useVaultSettingsStore()
$i18n.setLocaleMessage('de', {
desktop: de,
@@ -29,6 +37,86 @@ export const useDesktopStore = defineStore('desktopStore', () => {
const desktopItems = ref([])
const selectedItemIds = ref>(new Set())
+ // Desktop Grid Settings (stored in DB per device)
+ const iconSizePreset = ref(DesktopIconSizePreset.medium)
+
+ // Get device internal ID from DB
+ const getDeviceInternalIdAsync = async () => {
+ if (!deviceStore.deviceId || !currentVault.value?.drizzle) return undefined
+
+ const device = await currentVault.value.drizzle.query.haexDevices.findFirst({
+ where: eq(haexDevices.deviceId, deviceStore.deviceId),
+ })
+
+ return device?.id ? device.id : undefined
+ }
+
+ // Sync icon size from DB
+ const syncDesktopIconSizeAsync = async () => {
+ const deviceInternalId = await getDeviceInternalIdAsync()
+ if (!deviceInternalId) return
+
+ const preset = await settingsStore.syncDesktopIconSizeAsync(deviceInternalId)
+ iconSizePreset.value = preset
+ }
+
+ // Update icon size in DB
+ const updateDesktopIconSizeAsync = async (preset: DesktopIconSizePreset) => {
+ const deviceInternalId = await getDeviceInternalIdAsync()
+ if (!deviceInternalId) return
+
+ await settingsStore.updateDesktopIconSizeAsync(deviceInternalId, preset)
+ iconSizePreset.value = preset
+ }
+
+ // Reactive grid settings based on screen size
+ const effectiveGridColumns = computed(() => {
+ return isSmallScreen.value ? 4 : 8
+ })
+
+ const effectiveGridRows = computed(() => {
+ return isSmallScreen.value ? 5 : 6
+ })
+
+ const effectiveIconSize = computed(() => {
+ return iconSizePresetValues[iconSizePreset.value]
+ })
+
+ // Calculate grid cell size based on icon size
+ const gridCellSize = computed(() => {
+ // Add padding around icon (20px extra for spacing)
+ return effectiveIconSize.value + 20
+ })
+
+ // Snap position to grid (centers icon in cell)
+ // iconWidth and iconHeight are optional - if provided, they're used for centering
+ const snapToGrid = (x: number, y: number, iconWidth?: number, iconHeight?: number) => {
+ const cellSize = gridCellSize.value
+
+ // Calculate which grid cell the position falls into
+ const col = Math.floor(x / cellSize)
+ const row = Math.floor(y / cellSize)
+
+ // Use provided dimensions or fall back to cell size
+ const actualIconWidth = iconWidth || cellSize
+ const actualIconHeight = iconHeight || cellSize
+
+ // Center the icon in the cell(s) it occupies
+ const cellsWide = Math.max(1, Math.ceil(actualIconWidth / cellSize))
+ const cellsHigh = Math.max(1, Math.ceil(actualIconHeight / cellSize))
+
+ const totalWidth = cellsWide * cellSize
+ const totalHeight = cellsHigh * cellSize
+
+ const paddingX = (totalWidth - actualIconWidth) / 2
+ const paddingY = (totalHeight - actualIconHeight) / 2
+
+ return {
+ x: col * cellSize + paddingX,
+ y: row * cellSize + paddingY,
+ }
+ }
+
const loadDesktopItemsAsync = async () => {
if (!currentVault.value?.drizzle) {
console.error('Kein Vault geöffnet')
@@ -347,5 +435,14 @@ export const useDesktopStore = defineStore('desktopStore', () => {
toggleSelection,
clearSelection,
isItemSelected,
+ // Grid settings
+ iconSizePreset,
+ syncDesktopIconSizeAsync,
+ updateDesktopIconSizeAsync,
+ effectiveGridColumns,
+ effectiveGridRows,
+ effectiveIconSize,
+ gridCellSize,
+ snapToGrid,
}
})
diff --git a/src/stores/vault/device.ts b/src/stores/vault/device.ts
index bf4e850..fa9c93c 100644
--- a/src/stores/vault/device.ts
+++ b/src/stores/vault/device.ts
@@ -45,7 +45,9 @@ export const useDeviceStore = defineStore('vaultDeviceStore', () => {
const isKnownDeviceAsync = async () => {
const { readDeviceNameAsync } = useVaultSettingsStore()
- return !!(await readDeviceNameAsync(deviceId.value))
+ const device = await readDeviceNameAsync(deviceId.value)
+ console.log('device', device)
+ return !!device
}
const readDeviceNameAsync = async (id?: string) => {
@@ -54,7 +56,8 @@ export const useDeviceStore = defineStore('vaultDeviceStore', () => {
if (!_id) return
- deviceName.value = (await readDeviceNameAsync(_id))?.value ?? ''
+ const device = await readDeviceNameAsync(_id)
+ deviceName.value = device?.name ?? ''
return deviceName.value
}
diff --git a/src/stores/vault/settings.ts b/src/stores/vault/settings.ts
index 3d4d93d..6e3b57c 100644
--- a/src/stores/vault/settings.ts
+++ b/src/stores/vault/settings.ts
@@ -4,14 +4,29 @@ import * as schema from '~/database/schemas/haex'
import type { Locale } from 'vue-i18n'
export enum VaultSettingsTypeEnum {
- deviceName = 'deviceName',
settings = 'settings',
+ system = 'system',
}
export enum VaultSettingsKeyEnum {
locale = 'locale',
theme = 'theme',
vaultName = 'vaultName',
+ desktopIconSize = 'desktopIconSize',
+}
+
+export enum DesktopIconSizePreset {
+ small = 'small',
+ medium = 'medium',
+ large = 'large',
+ extraLarge = 'extra-large',
+}
+
+export const iconSizePresetValues: Record = {
+ [DesktopIconSizePreset.small]: 60,
+ [DesktopIconSizePreset.medium]: 80,
+ [DesktopIconSizePreset.large]: 120,
+ [DesktopIconSizePreset.extraLarge]: 160,
}
export const vaultDeviceNameSchema = z.string().min(3).max(255)
@@ -118,20 +133,22 @@ export const useVaultSettingsStore = defineStore('vaultSettingsStore', () => {
.where(eq(schema.haexSettings.key, 'vaultName'))
}
- const readDeviceNameAsync = async (id?: string) => {
+ const readDeviceNameAsync = async (deviceId?: string) => {
const { currentVault } = useVaultStore()
- if (!id) return undefined
+ if (!deviceId) return undefined
- const deviceName =
- await currentVault?.drizzle?.query.haexSettings.findFirst({
- where: and(
- eq(schema.haexSettings.type, VaultSettingsTypeEnum.deviceName),
- eq(schema.haexSettings.key, id),
- ),
+ const device =
+ await currentVault?.drizzle?.query.haexDevices.findFirst({
+ where: eq(schema.haexDevices.deviceId, deviceId),
})
- return deviceName?.id ? deviceName : undefined
+ // Workaround für Drizzle Bug: findFirst gibt manchmal Objekt mit undefined Werten zurück
+ // https://github.com/drizzle-team/drizzle-orm/issues/3872
+ // Prüfe ob das Device wirklich existiert (id muss gesetzt sein, da NOT NULL)
+ if (!device?.id) return undefined
+
+ return device
}
const addDeviceNameAsync = async ({
@@ -149,10 +166,9 @@ export const useVaultSettingsStore = defineStore('vaultSettingsStore', () => {
return
}
- return currentVault?.drizzle?.insert(schema.haexSettings).values({
- type: VaultSettingsTypeEnum.deviceName,
- key: deviceId,
- value: deviceName,
+ return currentVault?.drizzle?.insert(schema.haexDevices).values({
+ deviceId,
+ name: deviceName,
})
}
@@ -169,14 +185,49 @@ export const useVaultSettingsStore = defineStore('vaultSettingsStore', () => {
if (!isNameOk.success) return
return currentVault?.drizzle
- ?.update(schema.haexSettings)
+ ?.update(schema.haexDevices)
.set({
- value: deviceName,
+ name: deviceName,
})
+ .where(eq(schema.haexDevices.deviceId, deviceId))
+ }
+
+ const syncDesktopIconSizeAsync = async (deviceInternalId: string) => {
+ const iconSizeRow =
+ await currentVault.value?.drizzle.query.haexSettings.findFirst({
+ where: and(
+ eq(schema.haexSettings.deviceId, deviceInternalId),
+ eq(schema.haexSettings.key, VaultSettingsKeyEnum.desktopIconSize),
+ eq(schema.haexSettings.type, VaultSettingsTypeEnum.system),
+ ),
+ })
+
+ if (!iconSizeRow?.id) {
+ // Kein Eintrag vorhanden, erstelle einen mit Default (medium)
+ await currentVault.value?.drizzle.insert(schema.haexSettings).values({
+ deviceId: deviceInternalId,
+ key: VaultSettingsKeyEnum.desktopIconSize,
+ type: VaultSettingsTypeEnum.system,
+ value: DesktopIconSizePreset.medium,
+ })
+ return DesktopIconSizePreset.medium
+ }
+
+ return iconSizeRow.value as DesktopIconSizePreset
+ }
+
+ const updateDesktopIconSizeAsync = async (
+ deviceInternalId: string,
+ preset: DesktopIconSizePreset,
+ ) => {
+ return await currentVault.value?.drizzle
+ .update(schema.haexSettings)
+ .set({ value: preset })
.where(
and(
- eq(schema.haexSettings.key, deviceId),
- eq(schema.haexSettings.type, VaultSettingsTypeEnum.deviceName),
+ eq(schema.haexSettings.deviceId, deviceInternalId),
+ eq(schema.haexSettings.key, VaultSettingsKeyEnum.desktopIconSize),
+ eq(schema.haexSettings.type, VaultSettingsTypeEnum.system),
),
)
}
@@ -191,5 +242,7 @@ export const useVaultSettingsStore = defineStore('vaultSettingsStore', () => {
updateLocaleAsync,
updateThemeAsync,
updateVaultNameAsync,
+ syncDesktopIconSizeAsync,
+ updateDesktopIconSizeAsync,
}
})