diff --git a/frontend/src/desktop/CardDragLogic.ts b/frontend/src/desktop/CardDragLogic.ts
index 0d53c96..0f473c9 100644
--- a/frontend/src/desktop/CardDragLogic.ts
+++ b/frontend/src/desktop/CardDragLogic.ts
@@ -1,6 +1,6 @@
import { LayoutStore } from './LayoutSystem';
-export const handleDragStart = (store: LayoutStore, selection: string[], leadingId: string, offset: { x: number, y: number }) => {
+export const handleDragStart = (store: LayoutStore, selection: string[], leadingId: string, offset: { x: number, y: number }, pointerId: number) => {
const leadingCard = store.items.get(leadingId);
if (!leadingCard) return;
@@ -9,6 +9,9 @@ export const handleDragStart = (store: LayoutStore, selection: string[], leading
if (id === leadingId) return;
const card = store.items.get(id);
if (card) {
+ // If card is already dragging by another pointer, skip it
+ if (card.isDragging && card.dragPointerId !== pointerId) return;
+
const leadingCenterX = leadingCard.x + leadingCard.width / 2;
const leadingCenterY = leadingCard.y + leadingCard.height / 2;
@@ -23,6 +26,9 @@ export const handleDragStart = (store: LayoutStore, selection: string[], leading
const card = store.items.get(id);
if (!card) return;
+ // If card is already dragging by another pointer, skip it
+ if (card.isDragging && card.dragPointerId !== pointerId) return;
+
let myOffset = offset;
if (id !== leadingId) {
@@ -35,25 +41,45 @@ export const handleDragStart = (store: LayoutStore, selection: string[], leading
};
}
- card.beginDrag(myOffset);
+ card.beginDrag(myOffset, pointerId);
});
};
-export const handleDragMove = (store: LayoutStore, selection: string[], delta: { x: number, y: number }) => {
+export const attachToDragGroup = (store: LayoutStore, selection: string[], leadingId: string, pointerId: number) => {
+ const leadingCard = store.items.get(leadingId);
+ if (!leadingCard || !leadingCard.isDragging) return;
+
selection.forEach(id => {
+ if (id === leadingId) return;
const card = store.items.get(id);
- if (card) {
+
+ // If card exists and is not already dragging, attach it
+ if (card && !card.isDragging) {
+ const leadingCenterX = leadingCard.x + leadingCard.width / 2;
+ const leadingCenterY = leadingCard.y + leadingCard.height / 2;
+
+ const targetX = leadingCenterX - card.width / 2;
+ const targetY = leadingCenterY - card.height / 2;
+
+ card.snapTo(targetX, targetY);
+ card.beginDrag({ x: 0, y: 0 }, pointerId);
+ }
+ });
+};
+
+export const handleDragMove = (store: LayoutStore, _selection: string[], delta: { x: number, y: number }, pointerId: number) => {
+ for (const card of store.items.values()) {
+ if (card.isDragging && card.dragPointerId === pointerId) {
card.continueDrag(delta);
}
- });
+ }
};
-export const handleDragEnd = (store: LayoutStore, selection: string[]) => {
- selection.forEach(id => {
- const card = store.items.get(id);
- if (card) {
+export const handleDragEnd = (store: LayoutStore, _selection: string[], pointerId: number) => {
+ for (const card of store.items.values()) {
+ if (card.dragPointerId === pointerId) {
card.finishDrag();
}
- });
+ }
store.saveLayout();
};
diff --git a/frontend/src/desktop/DesktopWorkspace.tsx b/frontend/src/desktop/DesktopWorkspace.tsx
index a0b53b1..e22d549 100644
--- a/frontend/src/desktop/DesktopWorkspace.tsx
+++ b/frontend/src/desktop/DesktopWorkspace.tsx
@@ -16,6 +16,7 @@ import '../styles/workspace/workspace-items.css';
import '../styles/workspace/workspace-cards.css';
import { useWorkspaceSelectionContext } from '../app/WorkspaceSelectionContext';
import { createDocumentEntryKey } from '../app/entryKey';
+import { PointerTrackingProvider, usePointerTracking } from './PointerTrackingContext';
import type { Identifier } from '../types/identifiers';
import type { DocumentsListEntry, Document } from '../types/documents';
@@ -77,9 +78,10 @@ const DesktopDocumentContainer: React.FC
);
});
+
DesktopDocumentContainer.displayName = 'DesktopDocumentContainer';
-const DesktopWorkspace: React.FC = ({
+const DesktopWorkspaceContent: React.FC = ({
entries,
ensureAssetUrl,
getDocumentAsset,
@@ -89,6 +91,7 @@ const DesktopWorkspace: React.FC = ({
tenantId,
viewId,
}) => {
+ const { addPointer, removePointer } = usePointerTracking();
const containerRef = useRef(null);
const [isLayoutReady, setIsLayoutReady] = useState(false);
@@ -219,10 +222,26 @@ const DesktopWorkspace: React.FC = ({
onKeyDown={handleShellKeyDown}
onPointerDown={(e) => {
if (e.target === e.currentTarget) {
+ // Register background pointer
+ addPointer(e.pointerId);
+ (e.target as Element).setPointerCapture(e.pointerId);
+
onClearSelection();
focusShell();
}
}}
+ onPointerUp={(e) => {
+ if (e.target === e.currentTarget) {
+ removePointer(e.pointerId);
+ (e.target as Element).releasePointerCapture(e.pointerId);
+ }
+ }}
+ onPointerCancel={(e) => {
+ if (e.target === e.currentTarget) {
+ removePointer(e.pointerId);
+ (e.target as Element).releasePointerCapture(e.pointerId);
+ }
+ }}
>
{isLayoutReady && items.map((doc, index) => {
const docId = doc.id ? String(doc.id) : `temp-${index}`;
@@ -295,4 +314,12 @@ const DesktopWorkspace: React.FC = ({
);
};
+const DesktopWorkspace: React.FC = (props) => {
+ return (
+
+
+
+ );
+};
+
export default DesktopWorkspace;
diff --git a/frontend/src/desktop/LayoutSystem.ts b/frontend/src/desktop/LayoutSystem.ts
index ded71b7..f3f8971 100644
--- a/frontend/src/desktop/LayoutSystem.ts
+++ b/frontend/src/desktop/LayoutSystem.ts
@@ -335,7 +335,7 @@ export class LayoutStore {
}, ref);
// Calculate initial position using the card instance
- const { x: initX, y: initY } = getInitialPosition(
+ const { x: initX, y: initY, rotation: initRotation } = getInitialPosition(
this.containerWidth,
this.containerHeight,
card,
@@ -343,7 +343,7 @@ export class LayoutStore {
);
// Update card with calculated position
- card.update({ x: initX, y: initY }, { markDirty: true });
+ card.update({ x: initX, y: initY, rotation: initRotation }, { markDirty: true });
}
this.items.set(id, card);
diff --git a/frontend/src/desktop/PointerTrackingContext.tsx b/frontend/src/desktop/PointerTrackingContext.tsx
new file mode 100644
index 0000000..d3fd6ae
--- /dev/null
+++ b/frontend/src/desktop/PointerTrackingContext.tsx
@@ -0,0 +1,35 @@
+import React, { createContext, useContext, useRef, useCallback } from 'react';
+
+interface PointerTrackingContextType {
+ activePointersRef: React.MutableRefObject