@@ -19,7 +19,9 @@ import {
1919 createPhotoNoteFromScene ,
2020 createPhotoNoteFromItem ,
2121 createHandoutNoteFromPage ,
22- createMediaNoteFromSound
22+ createMediaNoteFromSound ,
23+ importFolderAsNotes ,
24+ importPlaylistAsNotes
2325} from "./utils/creation-utils.js" ;
2426
2527// v13 namespaced imports
@@ -254,6 +256,7 @@ Hooks.once("init", () => {
254256
255257// Hook to initialize socket for collaborative editing
256258Hooks . once ( "ready" , ( ) => {
259+ console . log ( "Investigation Board: Ready hook fired. v13 detected." ) ;
257260 initSocket ( ) ;
258261
259262 // Show setup warning to GM if enabled
@@ -425,13 +428,33 @@ async function _resolveDocumentFromLi(li, collection) {
425428 const el = li instanceof HTMLElement ? li : li [ 0 ] ;
426429 if ( ! el ) return null ;
427430
431+ // v13 resolution is much more varied
432+ const target = el . closest ( ".directory-item" ) || el . closest ( ".document" ) || el . closest ( ".playlist" ) || el ;
433+
434+ console . log ( "Investigation Board DEBUG: Resolving document from element" , target ) ;
435+ console . log ( "Investigation Board DEBUG: Target dataset:" , JSON . stringify ( target . dataset ) ) ;
436+
428437 // 1. Try UUID directly (most reliable in v13)
429- const uuid = el . dataset . uuid || el . getAttribute ( "data-uuid" ) ;
430- if ( uuid ) return await fromUuid ( uuid ) ;
438+ const uuid = target . dataset . uuid || target . getAttribute ( "data-uuid" ) ;
439+ if ( uuid ) {
440+ console . log ( "Investigation Board DEBUG: Found UUID:" , uuid ) ;
441+ return await fromUuid ( uuid ) ;
442+ }
431443
432444 // 2. Try ID and Pack
433- const docId = el . dataset . documentId || el . dataset . entryId || el . dataset . id || el . getAttribute ( "data-id" ) ;
434- const packName = el . closest ( "[data-pack]" ) ?. dataset . pack || el . closest ( ".compendium" ) ?. dataset . pack ;
445+ // v13 uses entryId or documentId often. Playlists sometimes use playlistId.
446+ const docId = target . dataset . documentId ||
447+ target . dataset . entryId ||
448+ target . dataset . id ||
449+ target . dataset . playlistId ||
450+ target . getAttribute ( "data-document-id" ) ||
451+ target . getAttribute ( "data-entry-id" ) ||
452+ target . getAttribute ( "data-id" ) ||
453+ target . getAttribute ( "data-playlist-id" ) ;
454+
455+ console . log ( "Investigation Board DEBUG: Resolved docId:" , docId ) ;
456+
457+ const packName = target . closest ( "[data-pack]" ) ?. dataset . pack || target . closest ( ".compendium" ) ?. dataset . pack ;
435458
436459 if ( docId ) {
437460 // If we have a pack name, use it
@@ -552,6 +575,113 @@ Hooks.on("getPlaylistSoundContextOptions", (html, entryOptions) => {
552575 } ) ;
553576} ) ;
554577
578+ // Context menu hook for Playlist entries
579+ Hooks . on ( "getPlaylistContextOptions" , ( html , entryOptions ) => {
580+ console . log ( "Investigation Board: getPlaylistContextOptions fired" ) ;
581+ entryOptions . push ( {
582+ name : "Import Playlist as Notes" ,
583+ icon : '<i class="fas fa-cassette-tape"></i>' ,
584+ callback : async ( li ) => {
585+ const playlist = await _resolveDocumentFromLi ( li , game . playlists ) ;
586+ if ( playlist ) {
587+ await importPlaylistAsNotes ( playlist ) ;
588+ } else {
589+ ui . notifications . warn ( "Investigation Board: Could not resolve Playlist." ) ;
590+ }
591+ } ,
592+ condition : ( ) => game . user . isGM
593+ } ) ;
594+ } ) ;
595+
596+ Hooks . on ( "getPlaylistDirectoryEntryContext" , ( html , entryOptions ) => {
597+ // Keeping this as a secondary variation for compatibility
598+ if ( entryOptions . find ( e => e . name === "Import Playlist as Notes" ) ) return ;
599+ entryOptions . push ( {
600+ name : "Import Playlist as Notes" ,
601+ icon : '<i class="fas fa-cassette-tape"></i>' ,
602+ callback : async ( li ) => {
603+ const playlist = await _resolveDocumentFromLi ( li , game . playlists ) ;
604+ if ( playlist ) {
605+ await importPlaylistAsNotes ( playlist ) ;
606+ } else {
607+ ui . notifications . warn ( "Investigation Board: Could not resolve Playlist." ) ;
608+ }
609+ } ,
610+ condition : ( ) => game . user . isGM
611+ } ) ;
612+ } ) ;
613+
614+ /**
615+ * Shared callback for folder context menu to import content
616+ */
617+ async function _onImportFolderAsNotes ( li ) {
618+ // li might be the jQuery element or the raw element from our manual hook
619+ const el = ( li instanceof HTMLElement ) ? li : ( li [ 0 ] || li ) ;
620+
621+ // v13 resolution is much more varied
622+ const folderId = el . dataset . id ||
623+ el . dataset . folderId ||
624+ el . getAttribute ( "data-id" ) ||
625+ el . getAttribute ( "data-folder-id" ) ||
626+ el . closest ( ".folder" ) ?. dataset . folderId ||
627+ el . closest ( ".folder" ) ?. dataset . id ||
628+ el . closest ( "[data-folder-id]" ) ?. dataset . folderId ;
629+
630+ if ( ! folderId ) {
631+ console . warn ( "Investigation Board: Could not find folder ID on element" , el ) ;
632+ return ;
633+ }
634+
635+ const folder = game . folders . get ( folderId ) ;
636+ if ( folder ) {
637+ await importFolderAsNotes ( folder ) ;
638+ } else {
639+ console . warn ( "Investigation Board: Could not find folder with ID" , folderId ) ;
640+ }
641+ }
642+
643+ const _folderHookCallback = ( html , entryOptions ) => {
644+ // Avoid duplicate entries
645+ if ( entryOptions . find ( e => e . name === "Import Folder as Notes" ) ) return ;
646+
647+ entryOptions . push ( {
648+ name : "Import Folder as Notes" ,
649+ icon : '<i class="fa-solid fa-camera-polaroid"></i>' ,
650+ callback : ( li ) => _onImportFolderAsNotes ( li ) ,
651+ condition : ( ) => game . user . isGM
652+ } ) ;
653+ } ;
654+
655+ // Folder Context Hooks - using multiple variations for v13 compatibility
656+ Hooks . on ( "getDirectoryFolderContext" , _folderHookCallback ) ;
657+ Hooks . on ( "getActorDirectoryFolderContext" , _folderHookCallback ) ;
658+ Hooks . on ( "getItemDirectoryFolderContext" , _folderHookCallback ) ;
659+ Hooks . on ( "getSceneDirectoryFolderContext" , _folderHookCallback ) ;
660+ Hooks . on ( "getPlaylistDirectoryFolderContext" , _folderHookCallback ) ;
661+ Hooks . on ( "getFolderContextOptions" , _folderHookCallback ) ;
662+
663+ // Some v13 sidebars might trigger EntryContext for folders
664+ Hooks . on ( "getActorDirectoryEntryContext" , ( html , entryOptions ) => {
665+ if ( html [ 0 ] ?. classList . contains ( "folder" ) || html [ 0 ] ?. closest ( ".folder" ) ) {
666+ _folderHookCallback ( html , entryOptions ) ;
667+ }
668+ } ) ;
669+ Hooks . on ( "getItemDirectoryEntryContext" , ( html , entryOptions ) => {
670+ if ( html [ 0 ] ?. classList . contains ( "folder" ) || html [ 0 ] ?. closest ( ".folder" ) ) {
671+ _folderHookCallback ( html , entryOptions ) ;
672+ }
673+ } ) ;
674+ Hooks . on ( "getSceneDirectoryEntryContext" , ( html , entryOptions ) => {
675+ if ( html [ 0 ] ?. classList . contains ( "folder" ) || html [ 0 ] ?. closest ( ".folder" ) ) {
676+ _folderHookCallback ( html , entryOptions ) ;
677+ }
678+ } ) ;
679+ Hooks . on ( "getPlaylistDirectoryEntryContext" , ( html , entryOptions ) => {
680+ if ( html [ 0 ] ?. classList . contains ( "folder" ) || html [ 0 ] ?. closest ( ".folder" ) ) {
681+ _folderHookCallback ( html , entryOptions ) ;
682+ }
683+ } ) ;
684+
555685
556686// Hook to deactivate connect mode on scene change and initialize connection lines
557687Hooks . on ( "canvasReady" , ( ) => {
@@ -577,4 +707,4 @@ Hooks.on("canvasReady", () => {
577707 updatePins ( ) ;
578708 drawAllConnectionLines ( ) ;
579709 } , 100 ) ;
580- } ) ;
710+ } ) ;
0 commit comments