Skip to content

Commit 0b7966f

Browse files
authored
[ENG-968] Obsidian - Open node functionalities (#524)
* new ways to open file * sm fix
1 parent 4cd3dcc commit 0b7966f

File tree

3 files changed

+119
-17
lines changed

3 files changed

+119
-17
lines changed

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

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import { RelationsOverlay } from "./overlays/RelationOverlay";
5151
import { showToast } from "./utils/toastUtils";
5252
import { WHITE_LOGO_SVG } from "~/icons";
5353
import { CustomContextMenu } from "./CustomContextMenu";
54+
import { openFileInSidebar, openFileInNewTab } from "./utils/openFileUtils";
5455

5556
type TldrawPreviewProps = {
5657
store: TLStore;
@@ -235,8 +236,10 @@ export const TldrawPreviewComponent = ({
235236
isCreatingRelationRef.current = false;
236237
}
237238

238-
if (e.shiftKey) {
239+
// Handle Shift+Click (open in sidebar) or Cmd+Click (open in new tab)
240+
if (e.shiftKey || e.metaKey) {
239241
const now = Date.now();
242+
const openInNewTab = e.metaKey; // Cmd on Mac, Ctrl on other platforms
240243

241244
// Debounce to prevent double opening
242245
if (now - lastShiftClickRef.current < SHIFT_CLICK_DEBOUNCE_MS) {
@@ -295,21 +298,12 @@ export const TldrawPreviewComponent = ({
295298
return;
296299
}
297300

298-
const rightSplit = plugin.app.workspace.rightSplit;
299-
const rightLeaf = plugin.app.workspace.getRightLeaf(false);
300-
301-
if (rightLeaf) {
302-
if (rightSplit && rightSplit.collapsed) {
303-
rightSplit.expand();
304-
}
305-
await rightLeaf.openFile(linkedFile);
306-
plugin.app.workspace.setActiveLeaf(rightLeaf);
301+
// Open in sidebar (Shift+Click) or new tab (Cmd+Click)
302+
if (openInNewTab) {
303+
await openFileInNewTab(plugin.app, linkedFile);
307304
} else {
308-
const leaf = plugin.app.workspace.getLeaf("split", "vertical");
309-
await leaf.openFile(linkedFile);
310-
plugin.app.workspace.setActiveLeaf(leaf);
305+
await openFileInSidebar(plugin.app, linkedFile);
311306
}
312-
313307
editor.selectNone();
314308
})
315309
.catch((error) => {

apps/obsidian/src/components/canvas/shapes/DiscourseNodeShape.tsx

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
TLBaseShape,
77
TLResizeInfo,
88
useEditor,
9+
useValue,
910
} from "tldraw";
1011
import type { App, TFile } from "obsidian";
1112
import { memo, createElement, useEffect } from "react";
@@ -18,6 +19,8 @@ import {
1819
import { resolveLinkedFileFromSrc } from "~/components/canvas/stores/assetStore";
1920
import { getNodeTypeById } from "~/utils/typeUtils";
2021
import { calcDiscourseNodeSize } from "~/utils/calcDiscourseNodeSize";
22+
import { openFileInSidebar } from "~/components/canvas/utils/openFileUtils";
23+
import { showToast } from "~/components/canvas/utils/toastUtils";
2124

2225
export type DiscourseNodeShape = TLBaseShape<
2326
"discourse-node",
@@ -140,6 +143,14 @@ const discourseNodeContent = memo(
140143
const { src, title, nodeTypeId } = shape.props;
141144
const nodeType = getNodeTypeById(plugin, nodeTypeId);
142145

146+
const isHovered = useValue(
147+
"is hovered",
148+
() => {
149+
return editor.getHoveredShapeId() === shape.id;
150+
},
151+
[editor, shape.id],
152+
);
153+
143154
useEffect(() => {
144155
const loadNodeData = async () => {
145156
if (!src) {
@@ -255,17 +266,89 @@ const discourseNodeContent = memo(
255266
nodeType?.keyImage,
256267
]);
257268

269+
const handleOpenInSidebar = async (): Promise<void> => {
270+
if (!src) {
271+
showToast({
272+
severity: "warning",
273+
title: "Cannot open node",
274+
description: "No source file linked",
275+
});
276+
return;
277+
}
278+
try {
279+
const linkedFile = await resolveLinkedFileFromSrc({
280+
app,
281+
canvasFile,
282+
src,
283+
});
284+
285+
if (!linkedFile) {
286+
showToast({
287+
severity: "warning",
288+
title: "Cannot open node",
289+
description: "Linked file not found",
290+
});
291+
return;
292+
}
293+
294+
await openFileInSidebar(app, linkedFile);
295+
editor.selectNone();
296+
} catch (error) {
297+
console.error("Error opening linked file:", error);
298+
showToast({
299+
severity: "error",
300+
title: "Error",
301+
description: "Failed to open linked file",
302+
});
303+
}
304+
};
305+
258306
return (
259307
<div
260308
style={{
261309
backgroundColor: nodeType?.color ?? "",
262310
}}
263-
// NOTE: These Tailwind classes (p-2, border-2, rounded-md, m-1, text-base, m-0, text-sm)
311+
// NOTE: These Tailwind classes (p-2, border-2, rounded-md, m-1, text-base, m-0, text-sm)
264312
// correspond to constants in nodeConstants.ts. If you change these classes, update the
265313
// constants and the measureNodeText function to keep measurements accurate.
266-
className="box-border flex h-full w-full flex-col items-start justify-start rounded-md border-2 p-2"
314+
className="relative box-border flex h-full w-full flex-col items-start justify-center rounded-md border-2 p-2"
267315
>
268-
<h1 className="m-1 text-base">{title || "..."}</h1>
316+
{isHovered && (
317+
<button
318+
onClick={(e) => {
319+
e.stopPropagation();
320+
void handleOpenInSidebar();
321+
}}
322+
onPointerDown={(e) => {
323+
e.stopPropagation();
324+
e.preventDefault();
325+
}}
326+
onPointerUp={(e) => {
327+
e.stopPropagation();
328+
}}
329+
className="absolute left-1 top-1 z-10 flex h-6 w-6 cursor-pointer items-center justify-center rounded border border-black/10 bg-white/90 p-1 shadow-sm transition-all duration-200 hover:bg-white"
330+
style={{
331+
pointerEvents: "auto",
332+
}}
333+
title="Open in sidebar"
334+
>
335+
<svg
336+
xmlns="http://www.w3.org/2000/svg"
337+
width="16"
338+
height="16"
339+
viewBox="0 0 24 24"
340+
fill="none"
341+
stroke="currentColor"
342+
strokeWidth="2"
343+
strokeLinecap="round"
344+
strokeLinejoin="round"
345+
>
346+
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
347+
<line x1="15" y1="3" x2="15" y2="21" />
348+
</svg>
349+
</button>
350+
)}
351+
<h1 className="m-0 text-base">{title || "..."}</h1>
269352
<p className="m-0 text-sm opacity-80">{nodeType?.name || ""}</p>
270353
{shape.props.imageSrc ? (
271354
<div className="mt-2 flex min-h-0 w-full flex-1 items-center justify-center overflow-hidden">
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { App, TFile } from "obsidian";
2+
3+
export const openFileInSidebar = async (app: App, file: TFile): Promise<void> => {
4+
const rightSplit = app.workspace.rightSplit;
5+
const rightLeaf = app.workspace.getRightLeaf(false);
6+
7+
if (rightLeaf) {
8+
if (rightSplit && rightSplit.collapsed) {
9+
rightSplit.expand();
10+
}
11+
await rightLeaf.openFile(file);
12+
app.workspace.setActiveLeaf(rightLeaf);
13+
} else {
14+
const leaf = app.workspace.getLeaf("split", "vertical");
15+
await leaf.openFile(file);
16+
app.workspace.setActiveLeaf(leaf);
17+
}
18+
};
19+
20+
export const openFileInNewTab = async (app: App, file: TFile): Promise<void> => {
21+
const leaf = app.workspace.getLeaf("tab");
22+
await leaf.openFile(file);
23+
app.workspace.setActiveLeaf(leaf);
24+
};
25+

0 commit comments

Comments
 (0)