From 2529c91d58a83b9c850a537f1a529c6c28eeb64a Mon Sep 17 00:00:00 2001 From: Till JS Date: Fri, 3 Apr 2026 13:24:58 +0200 Subject: [PATCH] feat(manacore/web): show item title + app color in drag preview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DragPreview now accepts a resolveEntity callback that maps drag type + data to display info (title, app color, app name). Dragging a task shows "Meeting mit Team · Todo" instead of just "task". Co-Authored-By: Claude Opus 4.6 (1M context) --- .../apps/web/src/routes/(app)/+page.svelte | 19 +++++- packages/shared-ui/src/dnd/DragPreview.svelte | 66 +++++++++++++++---- 2 files changed, 70 insertions(+), 15 deletions(-) diff --git a/apps/manacore/apps/web/src/routes/(app)/+page.svelte b/apps/manacore/apps/web/src/routes/(app)/+page.svelte index 45de6fd8b..220787dba 100644 --- a/apps/manacore/apps/web/src/routes/(app)/+page.svelte +++ b/apps/manacore/apps/web/src/routes/(app)/+page.svelte @@ -5,6 +5,23 @@ import { getAppEntry } from '$lib/components/workbench/app-registry'; import { createAppSettingsStore } from '@manacore/shared-stores'; import { DragPreview } from '@manacore/shared-ui/dnd'; + import { getEntityByDragType, ensureEntitiesRegistered } from '$lib/entities'; + import type { DragType } from '@manacore/shared-ui/dnd'; + + ensureEntitiesRegistered(); + + function resolveEntity(type: string, data: Record) { + const entity = getEntityByDragType(type as DragType); + if (!entity) return null; + const display = entity.getDisplayData(data); + const appEntry = getAppEntry(entity.appId); + return { + title: display.title, + subtitle: display.subtitle, + color: appEntry?.color, + appName: appEntry?.name, + }; + } // ── Persisted workbench state ─────────────────────────── const DEFAULT_WIDTH = 480; @@ -134,7 +151,7 @@ Home - ManaCore - +
* - * It reads from dragState and renders a small pill showing what's being dragged. + * It reads from dragState and renders a pill showing what's being dragged. + * For tags: colored dot + tag name. + * For entities: app color dot + item title + app name. */ import { dragState } from './drag-state.svelte'; import type { TagDragData } from './types'; + interface Props { + /** Resolve display data for a dragged entity. */ + resolveEntity?: ( + type: string, + data: Record + ) => { title: string; subtitle?: string; color?: string; appName?: string } | null; + } + + let { resolveEntity }: Props = $props(); + const OFFSET_X = 12; const OFFSET_Y = -20; @@ -18,6 +30,15 @@ dragState.activeDrag?.type === 'tag' ? (dragState.activeDrag.data as TagDragData) : null ); + const entityData = $derived(() => { + if (!dragState.activeDrag || dragState.activeDrag.type === 'tag') return null; + if (!resolveEntity) return null; + return resolveEntity( + dragState.activeDrag.type, + dragState.activeDrag.data as Record + ); + }); + const style = $derived( dragState.isDragging ? `left:${dragState.pointerX + OFFSET_X}px;top:${dragState.pointerY + OFFSET_Y}px;` @@ -28,10 +49,17 @@ {#if dragState.isDragging}
{#if tagData} - - {tagData.name} + + {tagData.name} + {:else if entityData()} + {@const entity = entityData()} + + {entity?.title} + {#if entity?.appName} + {entity.appName} + {/if} {:else if dragState.activeDrag} - {dragState.activeDrag.type} + {dragState.activeDrag.type} {/if}
{/if} @@ -54,8 +82,8 @@ 0 8px 24px -4px rgba(0, 0, 0, 0.15), 0 2px 6px -1px rgba(0, 0, 0, 0.1); font-size: 0.8125rem; - font-weight: 600; white-space: nowrap; + max-width: 280px; transform: scale(1.05); animation: drag-preview-in 150ms ease-out; } @@ -76,23 +104,33 @@ } } - .tag-dot { + .preview-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; } - .tag-name { - color: var(--color-foreground, #1a1a1a); + .preview-title { + font-weight: 600; + color: #1a1a1a; + overflow: hidden; + text-overflow: ellipsis; + } + :global(.dark) .preview-title { + color: #e5e5e5; } - :global(.dark) .tag-name { - color: var(--color-foreground, #e5e5e5); - } - - .generic-label { - color: var(--color-muted-foreground, #6b7280); + .preview-title.fallback { + color: #6b7280; text-transform: capitalize; + font-weight: 500; + } + + .preview-app { + font-size: 0.6875rem; + font-weight: 400; + color: #9ca3af; + flex-shrink: 0; }