@@ -28,11 +28,7 @@ import {
2828 TLDATA_DELIMITER_START ,
2929} from "~/constants" ;
3030import { TFile } from "obsidian" ;
31- import {
32- ObsidianTLAssetStore ,
33- resolveLinkedTFileByBlockRef ,
34- extractBlockRefId ,
35- } from "~/components/canvas/stores/assetStore" ;
31+ import { ObsidianTLAssetStore } from "~/components/canvas/stores/assetStore" ;
3632import {
3733 createDiscourseNodeUtil ,
3834 DiscourseNodeShape ,
@@ -51,7 +47,12 @@ import { RelationsOverlay } from "./overlays/RelationOverlay";
5147import { showToast } from "./utils/toastUtils" ;
5248import { WHITE_LOGO_SVG } from "~/icons" ;
5349import { CustomContextMenu } from "./CustomContextMenu" ;
54- import { openFileInSidebar , openFileInNewTab } from "./utils/openFileUtils" ;
50+ import {
51+ openFileInSidebar ,
52+ openFileInNewTab ,
53+ openFileInNewLeaf ,
54+ resolveDiscourseNodeFile ,
55+ } from "./utils/openFileUtils" ;
5556
5657type TldrawPreviewProps = {
5758 store : TLStore ;
@@ -69,6 +70,7 @@ export const TldrawPreviewComponent = ({
6970 const containerRef = useRef < HTMLDivElement > ( null ) ;
7071 const [ currentStore , setCurrentStore ] = useState < TLStore > ( store ) ;
7172 const [ isReady , setIsReady ] = useState ( false ) ;
73+ const [ isEditorMounted , setIsEditorMounted ] = useState ( false ) ;
7274 const isCreatingRelationRef = useRef ( false ) ;
7375 const saveTimeoutRef = useRef < NodeJS . Timeout > ( null ) ;
7476 const isSavingRef = useRef < boolean > ( false ) ;
@@ -103,6 +105,50 @@ export const TldrawPreviewComponent = ({
103105 return ( ) => clearTimeout ( timer ) ;
104106 } , [ ] ) ;
105107
108+ // Add keyboard event listener for Meta+Alt+Enter when editor is mounted
109+ useEffect ( ( ) => {
110+ if ( ! isEditorMounted || ! editorRef . current ) return ;
111+
112+ const editor = editorRef . current ;
113+
114+ const handleKeyDown = ( e : KeyboardEvent ) => {
115+ // Check for Meta+Alt+Enter (Command+Alt+Enter on Mac)
116+ if (
117+ e . key === "Enter" &&
118+ e . metaKey &&
119+ e . altKey &&
120+ ! e . shiftKey &&
121+ ! e . ctrlKey
122+ ) {
123+ const hoveredShapeId = editor . getHoveredShapeId ( ) ;
124+ if ( ! hoveredShapeId ) return ;
125+
126+ const hoveredShape = editor . getShape ( hoveredShapeId ) ;
127+ if ( ! hoveredShape || hoveredShape . type !== "discourse-node" ) return ;
128+
129+ const shape = hoveredShape as DiscourseNodeShape ;
130+ void ( async ( ) => {
131+ const linkedFile = await resolveDiscourseNodeFile (
132+ shape ,
133+ file ,
134+ plugin . app ,
135+ ) ;
136+
137+ if ( ! linkedFile ) return ;
138+
139+ await openFileInNewLeaf ( plugin . app , linkedFile ) ;
140+ editor . selectNone ( ) ;
141+ } ) ( ) ;
142+ }
143+ } ;
144+
145+ window . addEventListener ( "keydown" , handleKeyDown , true ) ;
146+
147+ return ( ) => {
148+ window . removeEventListener ( "keydown" , handleKeyDown , true ) ;
149+ } ;
150+ } , [ isEditorMounted , file , plugin ] ) ;
151+
106152 const saveChanges = useCallback ( async ( ) => {
107153 // Prevent concurrent saves
108154 if ( isSavingRef . current ) {
@@ -218,8 +264,11 @@ export const TldrawPreviewComponent = ({
218264
219265 const handleMount = ( editor : Editor ) => {
220266 editorRef . current = editor ;
267+ setIsEditorMounted ( true ) ;
221268
222269 editor . on ( "event" , ( event ) => {
270+ // Handle pointer events
271+ if ( event . type !== "pointer" ) return ;
223272 const e = event as TLPointerEventInfo ;
224273 if ( e . type === "pointer" && e . name === "pointer_down" ) {
225274 const currentTool = editor . getCurrentTool ( ) ;
@@ -262,58 +311,23 @@ export const TldrawPreviewComponent = ({
262311 return ;
263312 }
264313
265- const blockRefId = extractBlockRefId ( shape . props . src ?? undefined ) ;
266- if ( ! blockRefId ) {
267- showToast ( {
268- severity : "warning" ,
269- title : "Cannot open node" ,
270- description : "No valid block reference found" ,
271- } ) ;
272- return ;
273- }
314+ void ( async ( ) => {
315+ const linkedFile = await resolveDiscourseNodeFile (
316+ shape ,
317+ file ,
318+ plugin . app ,
319+ ) ;
274320
275- const canvasFileCache = plugin . app . metadataCache . getFileCache ( file ) ;
276- if ( ! canvasFileCache ) {
277- showToast ( {
278- severity : "error" ,
279- title : "Error" ,
280- description : "Could not read canvas file" ,
281- } ) ;
282- return ;
283- }
321+ if ( ! linkedFile ) return ;
284322
285- void resolveLinkedTFileByBlockRef ( {
286- app : plugin . app ,
287- canvasFile : file ,
288- blockRefId,
289- canvasFileCache,
290- } )
291- . then ( async ( linkedFile ) => {
292- if ( ! linkedFile ) {
293- showToast ( {
294- severity : "warning" ,
295- title : "Cannot open node" ,
296- description : "Linked file not found" ,
297- } ) ;
298- return ;
299- }
300-
301- // Open in sidebar (Shift+Click) or new tab (Cmd+Click)
302- if ( openInNewTab ) {
303- await openFileInNewTab ( plugin . app , linkedFile ) ;
304- } else {
305- await openFileInSidebar ( plugin . app , linkedFile ) ;
306- }
307- editor . selectNone ( ) ;
308- } )
309- . catch ( ( error ) => {
310- console . error ( "Error opening linked file:" , error ) ;
311- showToast ( {
312- severity : "error" ,
313- title : "Error" ,
314- description : "Failed to open linked file" ,
315- } ) ;
316- } ) ;
323+ // Open in sidebar (Shift+Click) or new tab (Cmd+Click)
324+ if ( openInNewTab ) {
325+ await openFileInNewTab ( plugin . app , linkedFile ) ;
326+ } else {
327+ await openFileInSidebar ( plugin . app , linkedFile ) ;
328+ }
329+ editor . selectNone ( ) ;
330+ } ) ( ) ;
317331 }
318332 }
319333 } ) ;
0 commit comments