@@ -140,13 +140,36 @@ export default class QueryBrowser {
140140 addClass ( titleEl , "yasgui-query-browser__title" ) ;
141141 titleEl . textContent = "Query Browser" ;
142142
143+ const headerButtons = document . createElement ( "div" ) ;
144+ addClass ( headerButtons , "yasgui-query-browser__header-buttons" ) ;
145+
146+ const helpButton = document . createElement ( "button" ) ;
147+ helpButton . type = "button" ;
148+ addClass ( helpButton , "yasgui-query-browser__help" ) ;
149+ helpButton . setAttribute ( "aria-label" , "Open documentation" ) ;
150+ helpButton . innerHTML = `<svg viewBox="0 0 24 24" fill="currentColor">
151+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"/>
152+ </svg>` ;
153+ helpButton . addEventListener ( "click" , ( ) => {
154+ window . open (
155+ "https://yasgui-doc.matdata.eu/docs/user-guide#managed-queries-and-workspaces" ,
156+ "_blank" ,
157+ "noopener,noreferrer" ,
158+ ) ;
159+ } ) ;
160+
143161 const closeButton = document . createElement ( "button" ) ;
144162 closeButton . type = "button" ;
145- closeButton . textContent = "Close" ;
146163 addClass ( closeButton , "yasgui-query-browser__close" ) ;
147164 closeButton . setAttribute ( "aria-label" , "Close query browser" ) ;
165+ closeButton . innerHTML = `<svg viewBox="0 0 24 24" fill="currentColor">
166+ <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
167+ </svg>` ;
148168 closeButton . addEventListener ( "click" , ( ) => this . close ( ) ) ;
149169
170+ headerButtons . appendChild ( helpButton ) ;
171+ headerButtons . appendChild ( closeButton ) ;
172+
150173 const headerControls = document . createElement ( "div" ) ;
151174 addClass ( headerControls , "yasgui-query-browser__header-controls" ) ;
152175 headerControls . appendChild ( this . workspaceSelectEl ) ;
@@ -155,7 +178,7 @@ export default class QueryBrowser {
155178 const headerTop = document . createElement ( "div" ) ;
156179 addClass ( headerTop , "yasgui-query-browser__header-top" ) ;
157180 headerTop . appendChild ( titleEl ) ;
158- headerTop . appendChild ( closeButton ) ;
181+ headerTop . appendChild ( headerButtons ) ;
159182
160183 this . headerEl . appendChild ( headerTop ) ;
161184 this . headerEl . appendChild ( headerControls ) ;
@@ -321,6 +344,64 @@ export default class QueryBrowser {
321344 this . listEl . innerHTML = "" ;
322345 }
323346
347+ private renderEmptyWorkspaceState ( ) {
348+ const emptyState = document . createElement ( "div" ) ;
349+ addClass ( emptyState , "yasgui-query-browser__empty-state" ) ;
350+
351+ const icon = document . createElement ( "div" ) ;
352+ addClass ( icon , "yasgui-query-browser__empty-icon" ) ;
353+ icon . innerHTML = `<svg viewBox="0 0 24 24" fill="currentColor">
354+ <path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z"/>
355+ </svg>` ;
356+
357+ const title = document . createElement ( "h3" ) ;
358+ addClass ( title , "yasgui-query-browser__empty-title" ) ;
359+ title . textContent = "No Workspaces Configured" ;
360+
361+ const description = document . createElement ( "p" ) ;
362+ addClass ( description , "yasgui-query-browser__empty-description" ) ;
363+ description . innerHTML = `Workspaces allow you to save and manage SPARQL queries in a shared, versioned store.
364+ You can use SPARQL endpoints (recommended) or Git repositories (GitHub, GitLab, Bitbucket, Gitea) to store your queries.` ;
365+
366+ const actionsContainer = document . createElement ( "div" ) ;
367+ addClass ( actionsContainer , "yasgui-query-browser__empty-actions" ) ;
368+
369+ const addWorkspaceBtn = document . createElement ( "button" ) ;
370+ addClass ( addWorkspaceBtn , "yasgui-query-browser__empty-button" ) ;
371+ addClass ( addWorkspaceBtn , "yasgui-query-browser__empty-button--primary" ) ;
372+ addWorkspaceBtn . textContent = "Add Workspace" ;
373+ addWorkspaceBtn . addEventListener ( "click" , ( ) => {
374+ this . close ( ) ;
375+ const tab = this . yasgui . getTab ( ) ;
376+ if ( tab && ( tab as any ) . settingsModal ) {
377+ const modal = ( tab as any ) . settingsModal ;
378+ modal . open ( ) ;
379+ // Switch to workspaces tab after a short delay to ensure modal is rendered
380+ setTimeout ( ( ) => {
381+ const workspacesButton = modal . modalContent ?. querySelector ( ".modalNavButton:nth-child(3)" ) as HTMLElement ;
382+ if ( workspacesButton ) workspacesButton . click ( ) ;
383+ } , 50 ) ;
384+ }
385+ } ) ;
386+
387+ const learnMoreLink = document . createElement ( "a" ) ;
388+ addClass ( learnMoreLink , "yasgui-query-browser__empty-link" ) ;
389+ learnMoreLink . href = "https://yasgui-doc.matdata.eu/docs/user-guide#managed-queries-and-workspaces" ;
390+ learnMoreLink . target = "_blank" ;
391+ learnMoreLink . rel = "noopener noreferrer" ;
392+ learnMoreLink . textContent = "Learn more about workspaces" ;
393+
394+ actionsContainer . appendChild ( addWorkspaceBtn ) ;
395+ actionsContainer . appendChild ( learnMoreLink ) ;
396+
397+ emptyState . appendChild ( icon ) ;
398+ emptyState . appendChild ( title ) ;
399+ emptyState . appendChild ( description ) ;
400+ emptyState . appendChild ( actionsContainer ) ;
401+
402+ this . listEl . appendChild ( emptyState ) ;
403+ }
404+
324405 private formatQueryPreview ( queryText : string , description ?: string ) : string {
325406 const normalized = queryText . replace ( / \r \n ? / g, "\n" ) . trim ( ) ;
326407 const lines = normalized . split ( "\n" ) . map ( ( l ) => l . trimEnd ( ) ) ;
@@ -917,8 +998,9 @@ export default class QueryBrowser {
917998 const workspaces = this . getWorkspaces ( ) ;
918999 if ( ! this . selectedWorkspaceId || workspaces . length === 0 ) {
9191000 this . backButtonEl . disabled = true ;
920- this . setStatus ( "No workspaces configured. Add one in settings. " ) ;
1001+ this . setStatus ( "" ) ;
9211002 this . clearList ( ) ;
1003+ this . renderEmptyWorkspaceState ( ) ;
9221004 return ;
9231005 }
9241006
@@ -988,7 +1070,7 @@ export default class QueryBrowser {
9881070 `folders:${ foldersPart } ` ,
9891071 ] . join ( ";" ) ;
9901072
991- this . setStatus ( rootEntries . length ? "" : "No queries" ) ;
1073+ this . setStatus ( rootEntries . length ? "" : "No queries, add one by saving a tab to this workspace. " ) ;
9921074 if ( signature !== this . lastRenderedSignature ) {
9931075 this . lastRenderedSignature = signature ;
9941076 this . renderTree ( backend ) ;
0 commit comments