@@ -30,8 +30,15 @@ import {
3030 WHITE_LOGO_SVG ,
3131} from "~/constants" ;
3232import { TFile } from "obsidian" ;
33- import { ObsidianTLAssetStore } from "~/components/canvas/stores/assetStore" ;
34- import { createDiscourseNodeUtil } from "~/components/canvas/shapes/DiscourseNodeShape" ;
33+ import {
34+ ObsidianTLAssetStore ,
35+ resolveLinkedTFileByBlockRef ,
36+ extractBlockRefId ,
37+ } from "~/components/canvas/stores/assetStore" ;
38+ import {
39+ createDiscourseNodeUtil ,
40+ DiscourseNodeShape ,
41+ } from "~/components/canvas/shapes/DiscourseNodeShape" ;
3542import { DiscourseNodeTool } from "./DiscourseNodeTool" ;
3643import { DiscourseToolPanel } from "./DiscourseToolPanel" ;
3744import { usePlugin } from "~/components/PluginContext" ;
@@ -43,6 +50,7 @@ import {
4350} from "~/components/canvas/shapes/DiscourseRelationBinding" ;
4451import ToastListener from "./ToastListener" ;
4552import { RelationsOverlay } from "./overlays/RelationOverlay" ;
53+ import { showToast } from "./utils/toastUtils" ;
4654
4755type TldrawPreviewProps = {
4856 store : TLStore ;
@@ -60,6 +68,8 @@ export const TldrawPreviewComponent = ({
6068 const [ isReady , setIsReady ] = useState ( false ) ;
6169 const isCreatingRelationRef = useRef ( false ) ;
6270 const saveTimeoutRef = useRef < NodeJS . Timeout > ( null ) ;
71+ const lastShiftClickRef = useRef < number > ( 0 ) ;
72+ const SHIFT_CLICK_DEBOUNCE_MS = 300 ; // Prevent double clicks within 300ms
6373 const lastSavedDataRef = useRef < string > ( "" ) ;
6474 const editorRef = useRef < Editor > ( null ) ;
6575 const plugin = usePlugin ( ) ;
@@ -189,6 +199,92 @@ export const TldrawPreviewComponent = ({
189199 BaseRelationBindingUtil . checkAndReifyRelation ( editor ) ;
190200 isCreatingRelationRef . current = false ;
191201 }
202+
203+ if ( e . shiftKey ) {
204+ const now = Date . now ( ) ;
205+
206+ // Debounce to prevent double opening
207+ if ( now - lastShiftClickRef . current < SHIFT_CLICK_DEBOUNCE_MS ) {
208+ return ;
209+ }
210+ lastShiftClickRef . current = now ;
211+
212+ const shape = editor . getShapeAtPoint (
213+ editor . inputs . currentPagePoint ,
214+ ) as DiscourseNodeShape ;
215+
216+ if ( ! shape || shape . type !== "discourse-node" ) return ;
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 ( async ( 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+ const rightSplit = plugin . app . workspace . rightSplit ;
263+ const rightLeaf = plugin . app . workspace . getRightLeaf ( false ) ;
264+
265+ if ( rightLeaf ) {
266+ if ( rightSplit && rightSplit . collapsed ) {
267+ rightSplit . expand ( ) ;
268+ }
269+ await rightLeaf . openFile ( linkedFile ) ;
270+ plugin . app . workspace . setActiveLeaf ( rightLeaf ) ;
271+ } else {
272+ const leaf = plugin . app . workspace . getLeaf ( "split" , "vertical" ) ;
273+ await leaf . openFile ( linkedFile ) ;
274+ plugin . app . workspace . setActiveLeaf ( leaf ) ;
275+ }
276+
277+ editor . selectNone ( ) ;
278+ } )
279+ . catch ( ( error ) => {
280+ console . error ( "Error opening linked file:" , error ) ;
281+ showToast ( {
282+ severity : "error" ,
283+ title : "Error" ,
284+ description : "Failed to open linked file" ,
285+ } ) ;
286+ } ) ;
287+ }
192288 }
193289 } ) ;
194290 } ;
0 commit comments