@@ -7,6 +7,10 @@ import {
77} from "~/components/AdvancedNodeSearchDialog/utils" ;
88
99const SIDEBAR_ROOT_ID = "dg-node-search-sidebar-root" ;
10+ const OUTLINE_WRAPPER_SELECTOR =
11+ "#roam-right-sidebar-content .rm-sidebar-outline-wrapper" ;
12+ const SIDEBAR_OPEN_WIDTH_PX = 40 ;
13+ const MAX_WRAPPER_WAIT_FRAMES = 30 ;
1014
1115export type DockedSearchState = {
1216 query : string ;
@@ -17,41 +21,90 @@ export type DockedSearchState = {
1721
1822let unmountSidebarSearch : ( ( ) => void ) | null = null ;
1923
20- const waitForLatestSidebarWindow = async ( ) : Promise < HTMLElement > => {
21- for ( let attempt = 0 ; attempt < 40 ; attempt += 1 ) {
22- const windows =
23- document . querySelectorAll < HTMLElement > ( ".rm-sidebar-window" ) ;
24- const latest = windows [ windows . length - 1 ] ;
25- if ( latest ) return latest ;
24+ const isRightSidebarOpen = ( ) : boolean => {
25+ const sidebar = document . getElementById ( "right-sidebar" ) ;
26+ return (
27+ ! ! sidebar && sidebar . getBoundingClientRect ( ) . width > SIDEBAR_OPEN_WIDTH_PX
28+ ) ;
29+ } ;
30+
31+ const getOutlineWrapperCount = ( ) : number =>
32+ document . querySelectorAll ( OUTLINE_WRAPPER_SELECTOR ) . length ;
33+
34+ const getLatestOutlineWrapper = ( ) : HTMLElement | null => {
35+ const wrappers = document . querySelectorAll < HTMLElement > (
36+ OUTLINE_WRAPPER_SELECTOR ,
37+ ) ;
38+ return wrappers [ wrappers . length - 1 ] ?? null ;
39+ } ;
40+
41+ const waitForOutlineWrapper = async (
42+ minCount : number ,
43+ ) : Promise < HTMLElement > => {
44+ for ( let attempt = 0 ; attempt < MAX_WRAPPER_WAIT_FRAMES ; attempt += 1 ) {
45+ const wrappers = document . querySelectorAll < HTMLElement > (
46+ OUTLINE_WRAPPER_SELECTOR ,
47+ ) ;
48+ if ( wrappers . length >= minCount && wrappers . length > 0 ) {
49+ return wrappers [ wrappers . length - 1 ] ;
50+ }
2651 await new Promise < void > ( ( resolve ) => {
2752 requestAnimationFrame ( ( ) => resolve ( ) ) ;
2853 } ) ;
2954 }
3055 throw new Error ( "Sidebar window did not appear" ) ;
3156} ;
3257
33- const setSidebarWindowTitle = ( windowEl : HTMLElement ) : void => {
34- const titleEl = windowEl . querySelector < HTMLElement > (
58+ const openRightSidebar = async ( {
59+ anchorPageUid,
60+ wrapperCountBefore,
61+ } : {
62+ anchorPageUid : string ;
63+ wrapperCountBefore : number ;
64+ } ) : Promise < HTMLElement | null > => {
65+ const rightSidebar = window . roamAlphaAPI . ui . rightSidebar as {
66+ open ?: ( ) => Promise < void > ;
67+ } ;
68+ if ( rightSidebar . open ) {
69+ await rightSidebar . open ( ) ;
70+ return null ;
71+ }
72+
73+ await addOutlineSidebarWindow ( anchorPageUid ) ;
74+ return waitForOutlineWrapper ( Math . max ( wrapperCountBefore + 1 , 1 ) ) ;
75+ } ;
76+
77+ const addOutlineSidebarWindow = async ( blockUid : string ) : Promise < void > => {
78+ await window . roamAlphaAPI . ui . rightSidebar . addWindow ( {
79+ window : {
80+ type : "outline" ,
81+ // @ts -expect-error - block-uid is valid for outline sidebar windows
82+ // eslint-disable-next-line @typescript-eslint/naming-convention
83+ "block-uid" : blockUid ,
84+ } ,
85+ } ) ;
86+ } ;
87+
88+ const setSidebarWindowTitle = ( outlineWrapper : HTMLElement ) : void => {
89+ const pane = outlineWrapper . closest < HTMLElement > (
90+ "#roam-right-sidebar-content .sidebar-content > *" ,
91+ ) ;
92+ const titleEl = pane ?. querySelector < HTMLElement > (
3593 ".window-headers span[style*='font-weight']" ,
3694 ) ;
3795 if ( titleEl ) titleEl . textContent = "DG node search" ;
3896} ;
3997
40- const mountPanelInSidebarWindow = ( {
98+ const mountPanelInOutlineWrapper = ( {
4199 dockedState,
42- windowEl ,
100+ outlineWrapper ,
43101} : {
44102 dockedState : DockedSearchState ;
45- windowEl : HTMLElement ;
103+ outlineWrapper : HTMLElement ;
46104} ) : void => {
47105 unmountSidebarSearch ?.( ) ;
48106 unmountSidebarSearch = null ;
49107
50- const outlineWrapper = windowEl . querySelector ( ".rm-sidebar-outline-wrapper" ) ;
51- if ( ! outlineWrapper ) {
52- throw new Error ( "Sidebar outline wrapper not found" ) ;
53- }
54-
55108 outlineWrapper . innerHTML = "" ;
56109
57110 const root = document . createElement ( "div" ) ;
@@ -62,32 +115,46 @@ const mountPanelInSidebarWindow = ({
62115 outlineWrapper . appendChild ( root ) ;
63116
64117 unmountSidebarSearch = renderWithUnmount (
65- < AdvancedSearchSidebarPanel { ... dockedState } /> ,
118+ < AdvancedSearchSidebarPanel dockedState = { dockedState } /> ,
66119 root ,
67120 ) ;
68121} ;
69122
70123export const openDgSearchInSidebar = async (
71124 dockedState : DockedSearchState ,
72125) : Promise < void > => {
73- const anchorPageUid = window . roamAlphaAPI . util . dateToPageUid ( new Date ( ) ) ;
126+ const anchorPageUid =
127+ ( await window . roamAlphaAPI . ui . mainWindow . getOpenPageOrBlockUid ( ) ) ||
128+ window . roamAlphaAPI . util . dateToPageUid ( new Date ( ) ) ;
129+ const wrapperCountBefore = getOutlineWrapperCount ( ) ;
74130
75- await window . roamAlphaAPI . ui . rightSidebar . addWindow ( {
76- window : {
77- type : "outline" ,
78- // @ts -expect-error - block-uid is valid for outline sidebar windows
79- // eslint-disable-next-line @typescript-eslint/naming-convention
80- "block-uid" : anchorPageUid ,
81- } ,
82- } ) ;
131+ if ( ! isRightSidebarOpen ( ) ) {
132+ const openedWrapper = await openRightSidebar ( {
133+ anchorPageUid,
134+ wrapperCountBefore,
135+ } ) ;
136+ if ( openedWrapper ) {
137+ setSidebarWindowTitle ( openedWrapper ) ;
138+ mountPanelInOutlineWrapper ( {
139+ dockedState,
140+ outlineWrapper : openedWrapper ,
141+ } ) ;
142+ return ;
143+ }
144+ }
83145
84- const sidebarWindow = await waitForLatestSidebarWindow ( ) ;
85- setSidebarWindowTitle ( sidebarWindow ) ;
86- mountPanelInSidebarWindow ( { dockedState, windowEl : sidebarWindow } ) ;
87- } ;
146+ const existingWrapper = getLatestOutlineWrapper ( ) ;
147+ if ( existingWrapper ) {
148+ setSidebarWindowTitle ( existingWrapper ) ;
149+ mountPanelInOutlineWrapper ( {
150+ dockedState,
151+ outlineWrapper : existingWrapper ,
152+ } ) ;
153+ return ;
154+ }
88155
89- export const unmountDgSearchSidebar = ( ) : void => {
90- unmountSidebarSearch ?. ( ) ;
91- unmountSidebarSearch = null ;
92- document . getElementById ( SIDEBAR_ROOT_ID ) ?. remove ( ) ;
156+ await addOutlineSidebarWindow ( anchorPageUid ) ;
157+ const outlineWrapper = await waitForOutlineWrapper ( wrapperCountBefore + 1 ) ;
158+ setSidebarWindowTitle ( outlineWrapper ) ;
159+ mountPanelInOutlineWrapper ( { dockedState , outlineWrapper } ) ;
93160} ;
0 commit comments