@@ -46,7 +46,20 @@ function error(e) {
4646 // Clear loading spinner in the current tab when an error occurs
4747 const tabOutputElm = document . querySelector ( `#${ currentTabId } .results-content` ) ;
4848 if ( tabOutputElm ) {
49- tabOutputElm . innerHTML = `<div class="no-results error-result">Query failed: ${ e . message } </div>` ;
49+ tabOutputElm . innerHTML = '' ;
50+
51+ // Use error template
52+ const errorTemplate = document . getElementById ( 'error-template' ) ;
53+ const errorClone = errorTemplate . content . cloneNode ( true ) ;
54+ const errorDiv = errorClone . querySelector ( '.error-result' ) ;
55+
56+ // Set error message
57+ const errorMessage = document . createElement ( 'span' ) ;
58+ errorMessage . slot = 'error-message' ;
59+ errorMessage . textContent = `Query failed: ${ e . message } ` ;
60+ errorDiv . appendChild ( errorMessage ) ;
61+
62+ tabOutputElm . appendChild ( errorDiv ) ;
5063 }
5164
5265 setTimeout ( ( ) => {
@@ -64,19 +77,38 @@ function noerror() {
6477
6578// Status updates
6679function updateStatus ( type , message ) {
80+ const createStatusSpan = ( className , text ) => {
81+ const span = document . createElement ( 'span' ) ;
82+ span . className = className ;
83+ span . textContent = text ;
84+ return span ;
85+ } ;
86+
6787 switch ( type ) {
6888 case 'executing' :
69- editorStatusElm . innerHTML = `<span class="status-info">Executing query...</span>` ;
70- resultsStatusElm . innerHTML = `<span class="status-info">Executing query...</span>` ;
89+ editorStatusElm . innerHTML = '' ;
90+ editorStatusElm . appendChild ( createStatusSpan ( 'status-info' , 'Executing query...' ) ) ;
91+
92+ resultsStatusElm . innerHTML = '' ;
93+ resultsStatusElm . appendChild ( createStatusSpan ( 'status-info' , 'Executing query...' ) ) ;
7194 break ;
95+
7296 case 'success' :
73- editorStatusElm . innerHTML = `<span class="status-success">Query executed successfully</span>` ;
74- resultsStatusElm . innerHTML = `<span class="status-success">${ message } </span>` ;
97+ editorStatusElm . innerHTML = '' ;
98+ editorStatusElm . appendChild ( createStatusSpan ( 'status-success' , 'Query executed successfully' ) ) ;
99+
100+ resultsStatusElm . innerHTML = '' ;
101+ resultsStatusElm . appendChild ( createStatusSpan ( 'status-success' , message ) ) ;
75102 break ;
103+
76104 case 'error' :
77- editorStatusElm . innerHTML = `<span class="status-error">Query failed</span>` ;
78- resultsStatusElm . innerHTML = `<span class="status-error">${ message } </span>` ;
105+ editorStatusElm . innerHTML = '' ;
106+ editorStatusElm . appendChild ( createStatusSpan ( 'status-error' , 'Query failed' ) ) ;
107+
108+ resultsStatusElm . innerHTML = '' ;
109+ resultsStatusElm . appendChild ( createStatusSpan ( 'status-error' , message ) ) ;
79110 break ;
111+
80112 default :
81113 editorStatusElm . textContent = message ;
82114 break ;
@@ -93,12 +125,27 @@ function execute(commands, tabId = currentTabId) {
93125 tic ( ) ;
94126 updateStatus ( 'executing' ) ;
95127
128+ // Check if we need to create a new tab
129+ // If the current tab is the initial tab and it hasn't been used yet, use it
130+ // Otherwise, create a new tab
131+ const currentTabPanel = document . getElementById ( currentTabId ) ;
132+ const isInitialUnusedTab = currentTabId === 'tab1' &&
133+ currentTabPanel &&
134+ currentTabPanel . querySelector ( '.results-content' ) . innerHTML . includes ( 'Results will be displayed here' ) ;
135+
136+ if ( ! isInitialUnusedTab ) {
137+ tabId = createNewTab ( ) ;
138+ }
139+
96140 // Get the output element for the current tab
97141 const tabOutputElm = document . querySelector ( `#${ tabId } .results-content` ) ;
98142 if ( ! tabOutputElm ) return ;
99143
100- // Show loading indicator
101- tabOutputElm . innerHTML = '<div class="loading"><div class="spinner"></div><span>Executing query...</span></div>' ;
144+ // Show loading indicator using template
145+ tabOutputElm . innerHTML = '' ;
146+ const loadingTemplate = document . getElementById ( 'loading-template' ) ;
147+ const loadingClone = loadingTemplate . content . cloneNode ( true ) ;
148+ tabOutputElm . appendChild ( loadingClone ) ;
102149
103150 // Add to query history
104151 addToHistory ( commands ) ;
@@ -116,7 +163,10 @@ function execute(commands, tabId = currentTabId) {
116163 tabOutputElm . innerHTML = "" ;
117164
118165 if ( results . length === 0 ) {
119- tabOutputElm . innerHTML = '<div class="no-results">Query executed successfully. No results to display.</div>' ;
166+ const noResultsDiv = document . createElement ( 'div' ) ;
167+ noResultsDiv . className = 'no-results' ;
168+ noResultsDiv . textContent = 'Query executed successfully. No results to display.' ;
169+ tabOutputElm . appendChild ( noResultsDiv ) ;
120170 updateStatus ( 'success' , 'Query executed with no results' ) ;
121171 return ;
122172 }
@@ -140,36 +190,50 @@ function execute(commands, tabId = currentTabId) {
140190
141191// Create an HTML table
142192var tableCreate = function ( ) {
143- function valconcat ( vals , tagName ) {
144- if ( vals . length === 0 ) return '' ;
145- var open = '<' + tagName + '>' , close = '</' + tagName + '>' ;
146- return open + vals . join ( close + open ) + close ;
147- }
148193 return function ( columns , values ) {
149- var tbl = document . createElement ( 'table' ) ;
150- var html = '<thead>' + valconcat ( columns , 'th' ) + '</thead>' ;
194+ // Use the table template
195+ const tableTemplate = document . getElementById ( 'table-template' ) ;
196+ const tableClone = tableTemplate . content . cloneNode ( true ) ;
197+ const wrapper = tableClone . querySelector ( '.table-wrapper' ) ;
198+ const table = tableClone . querySelector ( 'table' ) ;
199+
200+ // Set row and column counts
201+ wrapper . querySelector ( '.row-count' ) . textContent = `${ values . length } row${ values . length !== 1 ? 's' : '' } ` ;
202+ wrapper . querySelector ( '.column-count' ) . textContent = `${ columns . length } column${ columns . length !== 1 ? 's' : '' } ` ;
203+
204+ // Create header cells
205+ const thead = table . querySelector ( 'thead tr' ) ;
206+ thead . innerHTML = '' ; // Clear the slot
207+ columns . forEach ( column => {
208+ const th = document . createElement ( 'th' ) ;
209+ th . textContent = column ;
210+ thead . appendChild ( th ) ;
211+ } ) ;
212+
213+ // Create data rows
214+ const tbody = table . querySelector ( 'tbody' ) ;
215+ tbody . innerHTML = '' ; // Clear the slot
151216
152217 if ( values . length === 0 ) {
153- html += '<tbody><tr><td colspan="' + columns . length + '" class="no-results">No results</td></tr></tbody>' ;
218+ const emptyRow = document . createElement ( 'tr' ) ;
219+ const emptyCell = document . createElement ( 'td' ) ;
220+ emptyCell . className = 'no-results' ;
221+ emptyCell . textContent = 'No results' ;
222+ emptyCell . colSpan = columns . length ;
223+ emptyRow . appendChild ( emptyCell ) ;
224+ tbody . appendChild ( emptyRow ) ;
154225 } else {
155- var rows = values . map ( function ( v ) { return valconcat ( v , 'td' ) ; } ) ;
156- html += '<tbody>' + valconcat ( rows , 'tr' ) + '</tbody>' ;
226+ values . forEach ( rowData => {
227+ const row = document . createElement ( 'tr' ) ;
228+ rowData . forEach ( cellData => {
229+ const cell = document . createElement ( 'td' ) ;
230+ cell . textContent = cellData ;
231+ row . appendChild ( cell ) ;
232+ } ) ;
233+ tbody . appendChild ( row ) ;
234+ } ) ;
157235 }
158236
159- tbl . innerHTML = html ;
160-
161- // Add a wrapper with a caption showing the number of rows
162- var wrapper = document . createElement ( 'div' ) ;
163- wrapper . className = 'table-wrapper' ;
164- var caption = document . createElement ( 'div' ) ;
165- caption . className = 'table-caption' ;
166- caption . innerHTML = `
167- <span>${ values . length } row${ values . length !== 1 ? 's' : '' } </span>
168- <span>${ columns . length } column${ columns . length !== 1 ? 's' : '' } </span>
169- ` ;
170- wrapper . appendChild ( caption ) ;
171- wrapper . appendChild ( tbl ) ;
172-
173237 return wrapper ;
174238 }
175239} ( ) ;
@@ -178,11 +242,7 @@ var tableCreate = function () {
178242function execEditorContents ( ) {
179243 noerror ( ) ;
180244
181- // Create a new tab if needed
182- if ( document . querySelectorAll ( '.results-tabs .tab' ) . length <= 2 ) { // Only the first tab and + button
183- createNewTab ( ) ;
184- }
185-
245+ // Use the current tab if it exists, otherwise create a new one
186246 try {
187247 execute ( editor . getValue ( ) + ';' ) ;
188248 } catch ( e ) {
@@ -237,8 +297,7 @@ dbFileElm.onchange = function () {
237297 // Show the schema of the loaded database
238298 editor . setValue ( "SELECT `name`, `sql`\n FROM `sqlite_master`\n WHERE type='table';" ) ;
239299
240- // Create a new tab for the results
241- createNewTab ( ) ;
300+ // Execute the query (this will create a new tab if needed)
242301 execEditorContents ( ) ;
243302
244303 // Show success notification
@@ -300,8 +359,11 @@ function showNotification(message) {
300359 document . body . appendChild ( notification ) ;
301360 }
302361
303- // Set message and show
362+ // Clear existing content and set new message
363+ notification . textContent = '' ;
304364 notification . textContent = message ;
365+
366+ // Show notification
305367 notification . classList . add ( 'show' ) ;
306368
307369 // Hide after 3 seconds
@@ -397,6 +459,18 @@ function initTabs() {
397459 // Initialize the first tab
398460 const firstTab = document . querySelector ( '.tab[data-tab="tab1"]' ) ;
399461 if ( firstTab ) {
462+ // Clear the first tab's content
463+ firstTab . innerHTML = '' ;
464+
465+ // Add the tab text directly
466+ firstTab . textContent = `Result ${ tabCounter } ` ;
467+
468+ // Add close button
469+ const closeBtn = document . createElement ( 'span' ) ;
470+ closeBtn . className = 'tab-close' ;
471+ closeBtn . textContent = '×' ;
472+ firstTab . appendChild ( closeBtn ) ;
473+
400474 setActiveTab ( 'tab1' ) ;
401475 }
402476}
@@ -406,26 +480,33 @@ function createNewTab() {
406480 tabCounter ++ ;
407481 const tabId = `tab${ tabCounter } ` ;
408482
409- // Create tab button
410- const tab = document . createElement ( 'button' ) ;
411- tab . className = 'tab' ;
483+ // Create tab button using template
484+ const tabTemplate = document . getElementById ( 'tab-template' ) ;
485+ const tabClone = tabTemplate . content . cloneNode ( true ) ;
486+ const tab = tabClone . querySelector ( '.tab' ) ;
412487 tab . dataset . tab = tabId ;
413- tab . innerHTML = `Result ${ tabCounter } <span class="tab-close">×</span>` ;
488+
489+ // Clear any existing content in the tab
490+ tab . innerHTML = '' ;
491+
492+ // Add the tab text directly (no slots)
493+ tab . textContent = `Result ${ tabCounter } ` ;
494+
495+ // Add close button
496+ const closeBtn = document . createElement ( 'span' ) ;
497+ closeBtn . className = 'tab-close' ;
498+ closeBtn . textContent = '×' ;
499+ tab . appendChild ( closeBtn ) ;
414500
415501 // Insert before the + button
416502 resultsTabs . insertBefore ( tab , newTabBtn ) ;
417503
418- // Create tab panel
419- const tabPanel = document . createElement ( 'div' ) ;
420- tabPanel . className = 'tab-panel' ;
504+ // Create tab panel using template
505+ const panelTemplate = document . getElementById ( 'tab-panel-template' ) ;
506+ const panelClone = panelTemplate . content . cloneNode ( true ) ;
507+ const tabPanel = panelClone . querySelector ( '.tab-panel' ) ;
421508 tabPanel . id = tabId ;
422509
423- // Create results content container
424- const resultsContent = document . createElement ( 'div' ) ;
425- resultsContent . className = 'results-content' ;
426- resultsContent . textContent = 'Execute a query to see results' ;
427-
428- tabPanel . appendChild ( resultsContent ) ;
429510 document . querySelector ( '.results-panel .panel-content' ) . appendChild ( tabPanel ) ;
430511
431512 // Set as active
@@ -506,9 +587,11 @@ function addToHistory(query) {
506587function updateHistoryUI ( ) {
507588 queryHistoryElm . innerHTML = '' ;
508589
509- queryHistory . forEach ( ( item , index ) => {
510- const historyItem = document . createElement ( 'div' ) ;
511- historyItem . className = 'history-item' ;
590+ queryHistory . forEach ( ( item ) => {
591+ // Use history item template
592+ const historyTemplate = document . getElementById ( 'history-item-template' ) ;
593+ const historyClone = historyTemplate . content . cloneNode ( true ) ;
594+ const historyItem = historyClone . querySelector ( '.history-item' ) ;
512595
513596 // Format timestamp
514597 const timestamp = item . timestamp ;
@@ -519,10 +602,18 @@ function updateHistoryUI() {
519602 item . query . substring ( 0 , 60 ) + '...' :
520603 item . query ;
521604
522- historyItem . innerHTML = `
523- <div class="history-query" title="${ item . query } ">${ queryPreview } </div>
524- <div class="history-time">${ timeString } </div>
525- ` ;
605+ // Set query preview
606+ const queryPreviewEl = document . createElement ( 'span' ) ;
607+ queryPreviewEl . slot = 'query-preview' ;
608+ queryPreviewEl . textContent = queryPreview ;
609+ historyItem . querySelector ( '.history-query' ) . appendChild ( queryPreviewEl ) ;
610+ historyItem . querySelector ( '.history-query' ) . title = item . query ;
611+
612+ // Set query time
613+ const queryTimeEl = document . createElement ( 'span' ) ;
614+ queryTimeEl . slot = 'query-time' ;
615+ queryTimeEl . textContent = timeString ;
616+ historyItem . querySelector ( '.history-time' ) . appendChild ( queryTimeEl ) ;
526617
527618 // Add click handler to load query
528619 historyItem . addEventListener ( 'click' , ( ) => {
@@ -572,17 +663,27 @@ document.addEventListener('DOMContentLoaded', function() {
572663 if ( editorHeader ) {
573664 const shortcuts = document . createElement ( 'div' ) ;
574665 shortcuts . className = 'shortcuts' ;
575- shortcuts . innerHTML = `
576- <span title="Execute: Ctrl/Cmd+Enter">
577- <span class="shortcut-key">Ctrl+Enter</span>
578- </span>
579- <span title="Save DB: Ctrl/Cmd+S">
580- <span class="shortcut-key">Ctrl+S</span>
581- </span>
582- <span title="Toggle History: Ctrl+Space">
583- <span class="shortcut-key">Ctrl+Space</span>
584- </span>
585- ` ;
666+
667+ // Create shortcut elements using template
668+ const addShortcut = ( title , keyText ) => {
669+ const shortcutTemplate = document . getElementById ( 'shortcut-template' ) ;
670+ const shortcutClone = shortcutTemplate . content . cloneNode ( true ) ;
671+ const shortcut = shortcutClone . querySelector ( 'span' ) ;
672+ shortcut . title = title ;
673+
674+ const keySlot = document . createElement ( 'span' ) ;
675+ keySlot . slot = 'key' ;
676+ keySlot . textContent = keyText ;
677+ shortcut . appendChild ( keySlot ) ;
678+
679+ shortcuts . appendChild ( shortcut ) ;
680+ } ;
681+
682+ // Add all shortcuts
683+ addShortcut ( 'Execute: Ctrl/Cmd+Enter' , 'Ctrl+Enter' ) ;
684+ addShortcut ( 'Save DB: Ctrl/Cmd+S' , 'Ctrl+S' ) ;
685+ addShortcut ( 'Toggle History: Ctrl+Space' , 'Ctrl+Space' ) ;
686+
586687 editorHeader . appendChild ( shortcuts ) ;
587688 }
588689} ) ;
0 commit comments