Skip to content

Commit cda1da2

Browse files
committed
add Shift click ability
1 parent 47fd2e4 commit cda1da2

File tree

1 file changed

+90
-4
lines changed

1 file changed

+90
-4
lines changed

apps/obsidian/src/components/canvas/TldrawViewComponent.tsx

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,18 @@ import {
4242
} from "~/components/canvas/shapes/DiscourseRelationBinding";
4343
import ToastListener from "./ToastListener";
4444
import { RelationsOverlay } from "./overlays/RelationOverlay";
45+
import { DiscourseNodeShape } from "~/components/canvas/shapes/DiscourseNodeShape";
46+
import {
47+
extractBlockRefId,
48+
resolveLinkedTFileByBlockRef,
49+
} from "~/components/canvas/stores/assetStore";
50+
import { showToast } from "~/components/canvas/utils/toastUtils";
4551

46-
interface TldrawPreviewProps {
52+
type TldrawPreviewProps = {
4753
store: TLStore;
4854
file: TFile;
4955
assetStore: ObsidianTLAssetStore;
50-
}
56+
};
5157

5258
// No longer needed - using tldraw's event system instead
5359

@@ -60,9 +66,11 @@ export const TldrawPreviewComponent = ({
6066
const [currentStore, setCurrentStore] = useState<TLStore>(store);
6167
const [isReady, setIsReady] = useState(false);
6268
const isCreatingRelationRef = useRef(false);
63-
const saveTimeoutRef = useRef<NodeJS.Timeout>();
69+
const lastShiftClickRef = useRef<number>(0);
70+
const SHIFT_CLICK_DEBOUNCE_MS = 300; // Prevent double clicks within 300ms
71+
const saveTimeoutRef = useRef<NodeJS.Timeout>(null);
6472
const lastSavedDataRef = useRef<string>("");
65-
const editorRef = useRef<Editor>();
73+
const editorRef = useRef<Editor>(null);
6674
const plugin = usePlugin();
6775

6876
const customShapeUtils = [
@@ -190,6 +198,84 @@ export const TldrawPreviewComponent = ({
190198
BaseRelationBindingUtil.checkAndReifyRelation(editor);
191199
isCreatingRelationRef.current = false;
192200
}
201+
202+
if (e.shiftKey) {
203+
const now = Date.now();
204+
205+
// Debounce to prevent double opening
206+
if (now - lastShiftClickRef.current < SHIFT_CLICK_DEBOUNCE_MS) {
207+
return;
208+
}
209+
lastShiftClickRef.current = now;
210+
211+
const shape = editor.getShapeAtPoint(
212+
editor.inputs.currentPagePoint,
213+
) as DiscourseNodeShape;
214+
215+
if (!shape || shape.type !== "discourse-node") return;
216+
217+
const selectedShapes = editor.getSelectedShapes();
218+
const selectedDiscourseNodes = selectedShapes.filter(
219+
(s) => s.type === "discourse-node",
220+
);
221+
222+
if (selectedDiscourseNodes.length > 1) {
223+
return;
224+
}
225+
226+
const blockRefId = extractBlockRefId(shape.props.src ?? undefined);
227+
if (!blockRefId) {
228+
showToast({
229+
severity: "warning",
230+
title: "Cannot open node",
231+
description: "No valid block reference found",
232+
});
233+
return;
234+
}
235+
236+
const canvasFileCache = plugin.app.metadataCache.getFileCache(file);
237+
if (!canvasFileCache) {
238+
showToast({
239+
severity: "error",
240+
title: "Error",
241+
description: "Could not read canvas file",
242+
});
243+
return;
244+
}
245+
246+
void resolveLinkedTFileByBlockRef({
247+
app: plugin.app,
248+
canvasFile: file,
249+
blockRefId,
250+
canvasFileCache,
251+
})
252+
.then((linkedFile) => {
253+
if (!linkedFile) {
254+
showToast({
255+
severity: "warning",
256+
title: "Cannot open node",
257+
description: "Linked file not found",
258+
});
259+
return;
260+
}
261+
262+
void plugin.app.workspace.openLinkText(
263+
linkedFile.path,
264+
file.path,
265+
true,
266+
);
267+
268+
editor.selectNone();
269+
})
270+
.catch((error) => {
271+
console.error("Error opening linked file:", error);
272+
showToast({
273+
severity: "error",
274+
title: "Error",
275+
description: "Failed to open linked file",
276+
});
277+
});
278+
}
193279
}
194280
});
195281
};

0 commit comments

Comments
 (0)