@@ -46,7 +46,20 @@ function error(e) {
46
46
// Clear loading spinner in the current tab when an error occurs
47
47
const tabOutputElm = document . querySelector ( `#${ currentTabId } .results-content` ) ;
48
48
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 ) ;
50
63
}
51
64
52
65
setTimeout ( ( ) => {
@@ -64,19 +77,38 @@ function noerror() {
64
77
65
78
// Status updates
66
79
function 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
+
67
87
switch ( type ) {
68
88
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...' ) ) ;
71
94
break ;
95
+
72
96
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 ) ) ;
75
102
break ;
103
+
76
104
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 ) ) ;
79
110
break ;
111
+
80
112
default :
81
113
editorStatusElm . textContent = message ;
82
114
break ;
@@ -93,12 +125,27 @@ function execute(commands, tabId = currentTabId) {
93
125
tic ( ) ;
94
126
updateStatus ( 'executing' ) ;
95
127
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
+
96
140
// Get the output element for the current tab
97
141
const tabOutputElm = document . querySelector ( `#${ tabId } .results-content` ) ;
98
142
if ( ! tabOutputElm ) return ;
99
143
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 ) ;
102
149
103
150
// Add to query history
104
151
addToHistory ( commands ) ;
@@ -116,7 +163,10 @@ function execute(commands, tabId = currentTabId) {
116
163
tabOutputElm . innerHTML = "" ;
117
164
118
165
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 ) ;
120
170
updateStatus ( 'success' , 'Query executed with no results' ) ;
121
171
return ;
122
172
}
@@ -140,36 +190,50 @@ function execute(commands, tabId = currentTabId) {
140
190
141
191
// Create an HTML table
142
192
var 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
- }
148
193
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
151
216
152
217
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 ) ;
154
225
} 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
+ } ) ;
157
235
}
158
236
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
-
173
237
return wrapper ;
174
238
}
175
239
} ( ) ;
@@ -178,11 +242,7 @@ var tableCreate = function () {
178
242
function execEditorContents ( ) {
179
243
noerror ( ) ;
180
244
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
186
246
try {
187
247
execute ( editor . getValue ( ) + ';' ) ;
188
248
} catch ( e ) {
@@ -237,8 +297,7 @@ dbFileElm.onchange = function () {
237
297
// Show the schema of the loaded database
238
298
editor . setValue ( "SELECT `name`, `sql`\n FROM `sqlite_master`\n WHERE type='table';" ) ;
239
299
240
- // Create a new tab for the results
241
- createNewTab ( ) ;
300
+ // Execute the query (this will create a new tab if needed)
242
301
execEditorContents ( ) ;
243
302
244
303
// Show success notification
@@ -300,8 +359,11 @@ function showNotification(message) {
300
359
document . body . appendChild ( notification ) ;
301
360
}
302
361
303
- // Set message and show
362
+ // Clear existing content and set new message
363
+ notification . textContent = '' ;
304
364
notification . textContent = message ;
365
+
366
+ // Show notification
305
367
notification . classList . add ( 'show' ) ;
306
368
307
369
// Hide after 3 seconds
@@ -397,6 +459,18 @@ function initTabs() {
397
459
// Initialize the first tab
398
460
const firstTab = document . querySelector ( '.tab[data-tab="tab1"]' ) ;
399
461
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
+
400
474
setActiveTab ( 'tab1' ) ;
401
475
}
402
476
}
@@ -406,26 +480,33 @@ function createNewTab() {
406
480
tabCounter ++ ;
407
481
const tabId = `tab${ tabCounter } ` ;
408
482
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' ) ;
412
487
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 ) ;
414
500
415
501
// Insert before the + button
416
502
resultsTabs . insertBefore ( tab , newTabBtn ) ;
417
503
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' ) ;
421
508
tabPanel . id = tabId ;
422
509
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 ) ;
429
510
document . querySelector ( '.results-panel .panel-content' ) . appendChild ( tabPanel ) ;
430
511
431
512
// Set as active
@@ -506,9 +587,11 @@ function addToHistory(query) {
506
587
function updateHistoryUI ( ) {
507
588
queryHistoryElm . innerHTML = '' ;
508
589
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' ) ;
512
595
513
596
// Format timestamp
514
597
const timestamp = item . timestamp ;
@@ -519,10 +602,18 @@ function updateHistoryUI() {
519
602
item . query . substring ( 0 , 60 ) + '...' :
520
603
item . query ;
521
604
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 ) ;
526
617
527
618
// Add click handler to load query
528
619
historyItem . addEventListener ( 'click' , ( ) => {
@@ -572,17 +663,27 @@ document.addEventListener('DOMContentLoaded', function() {
572
663
if ( editorHeader ) {
573
664
const shortcuts = document . createElement ( 'div' ) ;
574
665
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
+
586
687
editorHeader . appendChild ( shortcuts ) ;
587
688
}
588
689
} ) ;
0 commit comments