mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-11-01 05:21:32 +08:00 
			
		
		
		
	refactoring of sql console into separate widgets
This commit is contained in:
		
							parent
							
								
									1d64129572
								
							
						
					
					
						commit
						02d9752abf
					
				
							
								
								
									
										32
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										32
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -1792,9 +1792,9 @@ | |||||||
|       "dev": true |       "dev": true | ||||||
|     }, |     }, | ||||||
|     "caniuse-lite": { |     "caniuse-lite": { | ||||||
|       "version": "1.0.30001168", |       "version": "1.0.30001170", | ||||||
|       "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001168.tgz", |       "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001170.tgz", | ||||||
|       "integrity": "sha512-P2zmX7swIXKu+GMMR01TWa4csIKELTNnZKc+f1CjebmZJQtTAEXmpQSoKVJVVcvPGAA0TEYTOUp3VehavZSFPQ==", |       "integrity": "sha512-Dd4d/+0tsK0UNLrZs3CvNukqalnVTRrxb5mcQm8rHL49t7V5ZaTygwXkrq+FB+dVDf++4ri8eJnFEJAB8332PA==", | ||||||
|       "dev": true |       "dev": true | ||||||
|     }, |     }, | ||||||
|     "caseless": { |     "caseless": { | ||||||
| @ -3209,9 +3209,9 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "electron-to-chromium": { |     "electron-to-chromium": { | ||||||
|       "version": "1.3.629", |       "version": "1.3.633", | ||||||
|       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.629.tgz", |       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.633.tgz", | ||||||
|       "integrity": "sha512-iSPPJtPvHrMAvYOt+9cdbDmTasPqwnwz4lkP8Dn200gDNUBQOLQ96xUsWXBwXslAo5XxdoXAoQQ3RAy4uao9IQ==", |       "integrity": "sha512-bsVCsONiVX1abkWdH7KtpuDAhsQ3N3bjPYhROSAXE78roJKet0Y5wznA14JE9pzbwSZmSMAW6KiKYf1RvbTJkA==", | ||||||
|       "dev": true |       "dev": true | ||||||
|     }, |     }, | ||||||
|     "electron-window-state": { |     "electron-window-state": { | ||||||
| @ -3250,13 +3250,13 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "enhanced-resolve": { |     "enhanced-resolve": { | ||||||
|       "version": "5.4.0", |       "version": "5.4.1", | ||||||
|       "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.4.0.tgz", |       "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.4.1.tgz", | ||||||
|       "integrity": "sha512-ZmqfWURB2lConOBM1JdCVfPyMRv5RdKWktLXO6123p97ovVm2CLBgw9t5MBj3jJWA6eHyOeIws9iJQoGFR4euQ==", |       "integrity": "sha512-4GbyIMzYktTFoRSmkbgZ1LU+RXwf4AQ8Z+rSuuh1dC8plp0PPeaWvx6+G4hh4KnUJ48VoxKbNyA1QQQIUpXjYA==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|       "requires": { |       "requires": { | ||||||
|         "graceful-fs": "^4.2.4", |         "graceful-fs": "^4.2.4", | ||||||
|         "tapable": "^2.0.0" |         "tapable": "^2.2.0" | ||||||
|       }, |       }, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "graceful-fs": { |         "graceful-fs": { | ||||||
| @ -4142,9 +4142,9 @@ | |||||||
|       "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" |       "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" | ||||||
|     }, |     }, | ||||||
|     "helmet": { |     "helmet": { | ||||||
|       "version": "4.2.0", |       "version": "4.3.1", | ||||||
|       "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.2.0.tgz", |       "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.3.1.tgz", | ||||||
|       "integrity": "sha512-aoiSxXMd0ks1ojYpSCFoCRzgv4rY/uB9jKStaw8PkXwsdLYa/Gq+Nc5l0soH0cwBIsLAlujPnx4HLQs+LaXCrQ==" |       "integrity": "sha512-WsafDyKsIexB0+pUNkq3rL1rB5GVAghR68TP8ssM9DPEMzfBiluEQlVzJ/FEj6Vq2Ag3CNuxf7aYMjXrN0X49Q==" | ||||||
|     }, |     }, | ||||||
|     "hosted-git-info": { |     "hosted-git-info": { | ||||||
|       "version": "2.8.5", |       "version": "2.8.5", | ||||||
| @ -7597,9 +7597,9 @@ | |||||||
|       "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" |       "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" | ||||||
|     }, |     }, | ||||||
|     "webpack": { |     "webpack": { | ||||||
|       "version": "5.11.0", |       "version": "5.11.1", | ||||||
|       "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.11.0.tgz", |       "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.11.1.tgz", | ||||||
|       "integrity": "sha512-ubWv7iP54RqAC/VjixgpnLLogCFbAfSOREcSWnnOlZEU8GICC5eKmJSu6YEnph2N2amKqY9rvxSwgyHxVqpaRw==", |       "integrity": "sha512-tNUIdAmYJv+nupRs/U/gqmADm6fgrf5xE+rSlSsf2PgsGO7j2WG7ccU6AWNlOJlHFl+HnmXlBmHIkiLf+XA9mQ==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|       "requires": { |       "requires": { | ||||||
|         "@types/eslint-scope": "^3.7.0", |         "@types/eslint-scope": "^3.7.0", | ||||||
|  | |||||||
| @ -41,7 +41,7 @@ | |||||||
|     "express": "4.17.1", |     "express": "4.17.1", | ||||||
|     "express-session": "1.17.1", |     "express-session": "1.17.1", | ||||||
|     "fs-extra": "9.0.1", |     "fs-extra": "9.0.1", | ||||||
|     "helmet": "4.2.0", |     "helmet": "4.3.1", | ||||||
|     "html": "1.0.0", |     "html": "1.0.0", | ||||||
|     "html2plaintext": "2.1.2", |     "html2plaintext": "2.1.2", | ||||||
|     "http-proxy-agent": "4.0.1", |     "http-proxy-agent": "4.0.1", | ||||||
| @ -86,7 +86,7 @@ | |||||||
|     "jsdoc": "3.6.6", |     "jsdoc": "3.6.6", | ||||||
|     "lorem-ipsum": "2.0.3", |     "lorem-ipsum": "2.0.3", | ||||||
|     "rcedit": "3.0.0", |     "rcedit": "3.0.0", | ||||||
|     "webpack": "5.11.0", |     "webpack": "5.11.1", | ||||||
|     "webpack-cli": "4.3.0" |     "webpack-cli": "4.3.0" | ||||||
|   }, |   }, | ||||||
|   "optionalDependencies": { |   "optionalDependencies": { | ||||||
|  | |||||||
| @ -5,7 +5,6 @@ import TitleBarButtonsWidget from "../widgets/title_bar_buttons.js"; | |||||||
| import NoteTreeWidget from "../widgets/note_tree.js"; | import NoteTreeWidget from "../widgets/note_tree.js"; | ||||||
| import TabCachingWidget from "../widgets/tab_caching_widget.js"; | import TabCachingWidget from "../widgets/tab_caching_widget.js"; | ||||||
| import NoteTitleWidget from "../widgets/note_title.js"; | import NoteTitleWidget from "../widgets/note_title.js"; | ||||||
| import RunScriptButtonsWidget from "../widgets/run_script_buttons.js"; |  | ||||||
| import NoteTypeWidget from "../widgets/note_type.js"; | import NoteTypeWidget from "../widgets/note_type.js"; | ||||||
| import NoteActionsWidget from "../widgets/note_actions.js"; | import NoteActionsWidget from "../widgets/note_actions.js"; | ||||||
| import NoteDetailWidget from "../widgets/note_detail.js"; | import NoteDetailWidget from "../widgets/note_detail.js"; | ||||||
| @ -35,7 +34,6 @@ export default class DesktopExtraWindowLayout { | |||||||
|                         .overflowing() |                         .overflowing() | ||||||
|                         .cssBlock('.title-row > * { margin: 5px 5px 0 5px; }') |                         .cssBlock('.title-row > * { margin: 5px 5px 0 5px; }') | ||||||
|                         .child(new NoteTitleWidget()) |                         .child(new NoteTitleWidget()) | ||||||
|                         .child(new RunScriptButtonsWidget().hideInZenMode()) |  | ||||||
|                         .child(new NoteTypeWidget().hideInZenMode()) |                         .child(new NoteTypeWidget().hideInZenMode()) | ||||||
|                         .child(new NoteActionsWidget().hideInZenMode()) |                         .child(new NoteActionsWidget().hideInZenMode()) | ||||||
|                     ) |                     ) | ||||||
|  | |||||||
| @ -9,7 +9,6 @@ import TabCachingWidget from "../widgets/tab_caching_widget.js"; | |||||||
| import NotePathsWidget from "../widgets/note_paths.js"; | import NotePathsWidget from "../widgets/note_paths.js"; | ||||||
| import NoteTitleWidget from "../widgets/note_title.js"; | import NoteTitleWidget from "../widgets/note_title.js"; | ||||||
| import OwnedAttributeListWidget from "../widgets/attribute_widgets/owned_attribute_list.js"; | import OwnedAttributeListWidget from "../widgets/attribute_widgets/owned_attribute_list.js"; | ||||||
| import RunScriptButtonsWidget from "../widgets/run_script_buttons.js"; |  | ||||||
| import NoteTypeWidget from "../widgets/note_type.js"; | import NoteTypeWidget from "../widgets/note_type.js"; | ||||||
| import NoteActionsWidget from "../widgets/note_actions.js"; | import NoteActionsWidget from "../widgets/note_actions.js"; | ||||||
| import NoteDetailWidget from "../widgets/note_detail.js"; | import NoteDetailWidget from "../widgets/note_detail.js"; | ||||||
| @ -27,6 +26,8 @@ import InheritedAttributesWidget from "../widgets/inherited_attribute_list.js"; | |||||||
| import NoteListWidget from "../widgets/note_list.js"; | import NoteListWidget from "../widgets/note_list.js"; | ||||||
| import SearchDefinitionWidget from "../widgets/search_definition.js"; | import SearchDefinitionWidget from "../widgets/search_definition.js"; | ||||||
| import Container from "../widgets/container.js"; | import Container from "../widgets/container.js"; | ||||||
|  | import SqlResultWidget from "../widgets/sql_result.js"; | ||||||
|  | import SqlTableSchemasWidget from "../widgets/sql_table_schemas.js"; | ||||||
| 
 | 
 | ||||||
| const RIGHT_PANE_CSS = ` | const RIGHT_PANE_CSS = ` | ||||||
| <style> | <style> | ||||||
| @ -149,7 +150,6 @@ export default class DesktopMainWindowLayout { | |||||||
|                         .cssBlock('.title-row > * { margin: 5px 5px 0 5px; }') |                         .cssBlock('.title-row > * { margin: 5px 5px 0 5px; }') | ||||||
|                         .overflowing() |                         .overflowing() | ||||||
|                         .child(new NoteTitleWidget()) |                         .child(new NoteTitleWidget()) | ||||||
|                         .child(new RunScriptButtonsWidget().hideInZenMode()) |  | ||||||
|                         .child(new NoteTypeWidget().hideInZenMode()) |                         .child(new NoteTypeWidget().hideInZenMode()) | ||||||
|                         .child(new NoteActionsWidget().hideInZenMode()) |                         .child(new NoteActionsWidget().hideInZenMode()) | ||||||
|                     ) |                     ) | ||||||
| @ -163,8 +163,10 @@ export default class DesktopMainWindowLayout { | |||||||
|                     ) |                     ) | ||||||
|                     .child(new Container() |                     .child(new Container() | ||||||
|                         .css('height: 100%; overflow: auto;') |                         .css('height: 100%; overflow: auto;') | ||||||
|  |                         .child(new TabCachingWidget(() => new SqlTableSchemasWidget())) | ||||||
|                         .child(new TabCachingWidget(() => new NoteDetailWidget())) |                         .child(new TabCachingWidget(() => new NoteDetailWidget())) | ||||||
|                         .child(new TabCachingWidget(() => new NoteListWidget())) |                         .child(new TabCachingWidget(() => new NoteListWidget())) | ||||||
|  |                         .child(new TabCachingWidget(() => new SqlResultWidget())) | ||||||
|                     ) |                     ) | ||||||
|                     .child(new TabCachingWidget(() => new SimilarNotesWidget())) |                     .child(new TabCachingWidget(() => new SimilarNotesWidget())) | ||||||
|                     .child(...this.customWidgets.get('center-pane')) |                     .child(...this.customWidgets.get('center-pane')) | ||||||
|  | |||||||
| @ -211,10 +211,12 @@ export default class Entrypoints extends Component { | |||||||
| 
 | 
 | ||||||
|         if (note.mime.endsWith("env=frontend")) { |         if (note.mime.endsWith("env=frontend")) { | ||||||
|             await bundleService.getAndExecuteBundle(note.noteId); |             await bundleService.getAndExecuteBundle(note.noteId); | ||||||
|         } |         } else if (note.mime.endsWith("env=backend")) { | ||||||
| 
 |  | ||||||
|         if (note.mime.endsWith("env=backend")) { |  | ||||||
|             await server.post('script/run/' + note.noteId); |             await server.post('script/run/' + note.noteId); | ||||||
|  |         } else if (note.mime === 'text/x-sqlite;schema=trilium') { | ||||||
|  |             const result = await server.post("sql/execute/" + note.noteId); | ||||||
|  | 
 | ||||||
|  |             this.triggerEvent('sqlQueryResults', {results: result.results}); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         toastService.showMessage("Note executed"); |         toastService.showMessage("Note executed"); | ||||||
|  | |||||||
| @ -22,8 +22,10 @@ const TPL = ` | |||||||
| 
 | 
 | ||||||
| export default class NoteListWidget extends TabAwareWidget { | export default class NoteListWidget extends TabAwareWidget { | ||||||
|     isEnabled() { |     isEnabled() { | ||||||
|         return super.isEnabled() && ( |         return super.isEnabled() | ||||||
|             ['book', 'search'].includes(this.note.type) |             && this.note.mime !== 'text/x-sqlite;schema=trilium' | ||||||
|  |             && ( | ||||||
|  |                 ['book', 'search', 'code'].includes(this.note.type) | ||||||
|                 || (this.note.type === 'text' && this.note.hasChildren()) |                 || (this.note.type === 'text' && this.note.hasChildren()) | ||||||
|             ); |             ); | ||||||
|     } |     } | ||||||
| @ -46,10 +48,6 @@ export default class NoteListWidget extends TabAwareWidget { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     checkRenderStatus() { |     checkRenderStatus() { | ||||||
|         console.log("this.isIntersecting", this.isIntersecting); |  | ||||||
|         console.log("this.noteIdRefreshed === this.noteId", this.noteIdRefreshed === this.noteId); |  | ||||||
|         console.log("this.shownNoteId !== this.noteId", this.shownNoteId !== this.noteId); |  | ||||||
| 
 |  | ||||||
|         if (this.isIntersecting |         if (this.isIntersecting | ||||||
|             && this.noteIdRefreshed === this.noteId |             && this.noteIdRefreshed === this.noteId | ||||||
|             && this.shownNoteId !== this.noteId) { |             && this.shownNoteId !== this.noteId) { | ||||||
|  | |||||||
| @ -1,33 +0,0 @@ | |||||||
| import TabAwareWidget from "./tab_aware_widget.js"; |  | ||||||
| 
 |  | ||||||
| const TPL = ` |  | ||||||
| <div style="display: inline-flex;"> |  | ||||||
|     <button class="btn btn-sm icon-button bx bx-play-circle render-button" |  | ||||||
|             data-trigger-command="renderActiveNote" |  | ||||||
|             title="Render"></button> |  | ||||||
|      |  | ||||||
|     <button class="btn btn-sm icon-button bx bx-play-circle execute-script-button" |  | ||||||
|             data-trigger-command="runActiveNote" |  | ||||||
|             title="Execute"></button> |  | ||||||
| </div>`; |  | ||||||
| 
 |  | ||||||
| export default class RunScriptButtonsWidget extends TabAwareWidget { |  | ||||||
|     doRender() { |  | ||||||
|         this.$widget = $(TPL); |  | ||||||
|         this.contentSized(); |  | ||||||
| 
 |  | ||||||
|         this.$renderButton = this.$widget.find('.render-button'); |  | ||||||
|         this.$executeScriptButton = this.$widget.find('.execute-script-button'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     refreshWithNote(note) { |  | ||||||
|         this.$renderButton.toggle(note.type === 'render'); |  | ||||||
|         this.$executeScriptButton.toggle(note.type === 'code' && note.mime.startsWith('application/javascript')); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     async entitiesReloadedEvent({loadResults}) { |  | ||||||
|         if (loadResults.isNoteReloaded(this.noteId)) { |  | ||||||
|             this.refresh(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										64
									
								
								src/public/app/widgets/sql_result.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/public/app/widgets/sql_result.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | |||||||
|  | import TabAwareWidget from "./tab_aware_widget.js"; | ||||||
|  | import treeService from "../services/tree.js"; | ||||||
|  | import linkService from "../services/link.js"; | ||||||
|  | import hoistedNoteService from "../services/hoisted_note.js"; | ||||||
|  | import server from "../services/server.js"; | ||||||
|  | import toastService from "../services/toast.js"; | ||||||
|  | 
 | ||||||
|  | const TPL = ` | ||||||
|  | <div class="sql-result-widget"> | ||||||
|  |     <style> | ||||||
|  |     .sql-result-widget { | ||||||
|  |         padding: 15px; | ||||||
|  |     } | ||||||
|  |     </style> | ||||||
|  |     | ||||||
|  |     <div class="sql-console-result-container"></div> | ||||||
|  | </div>`; | ||||||
|  | 
 | ||||||
|  | export default class SqlResultWidget extends TabAwareWidget { | ||||||
|  |     isEnabled() { | ||||||
|  |         return this.note | ||||||
|  |             && this.note.mime === 'text/x-sqlite;schema=trilium' | ||||||
|  |             && super.isEnabled(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     doRender() { | ||||||
|  |         this.$widget = $(TPL); | ||||||
|  |         this.overflowing(); | ||||||
|  | 
 | ||||||
|  |         this.$sqlConsoleResultContainer = this.$widget.find('.sql-console-result-container'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async sqlQueryResultsEvent({results}) { | ||||||
|  |         this.$sqlConsoleResultContainer.empty(); | ||||||
|  | 
 | ||||||
|  |         for (const rows of results) { | ||||||
|  |             if (rows.length === 0) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             const $table = $('<table class="table table-striped">'); | ||||||
|  |             this.$sqlConsoleResultContainer.append($table); | ||||||
|  | 
 | ||||||
|  |             const result = rows[0]; | ||||||
|  |             const $row = $("<tr>"); | ||||||
|  | 
 | ||||||
|  |             for (const key in result) { | ||||||
|  |                 $row.append($("<th>").html(key)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             $table.append($row); | ||||||
|  | 
 | ||||||
|  |             for (const result of rows) { | ||||||
|  |                 const $row = $("<tr>"); | ||||||
|  | 
 | ||||||
|  |                 for (const key in result) { | ||||||
|  |                     $row.append($("<td>").html(result[key])); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 $table.append($row); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										83
									
								
								src/public/app/widgets/sql_table_schemas.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/public/app/widgets/sql_table_schemas.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | |||||||
|  | import TabAwareWidget from "./tab_aware_widget.js"; | ||||||
|  | import treeService from "../services/tree.js"; | ||||||
|  | import linkService from "../services/link.js"; | ||||||
|  | import hoistedNoteService from "../services/hoisted_note.js"; | ||||||
|  | import server from "../services/server.js"; | ||||||
|  | import toastService from "../services/toast.js"; | ||||||
|  | 
 | ||||||
|  | const TPL = ` | ||||||
|  | <div class="sql-table-schemas-widget"> | ||||||
|  |     <style> | ||||||
|  |     .sql-table-schemas button { | ||||||
|  |         padding: 0.25rem 0.4rem; | ||||||
|  |         font-size: 0.875rem; | ||||||
|  |         line-height: 0.5; | ||||||
|  |         border-radius: 0.2rem; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     .sql-console-result-container { | ||||||
|  |         width: 100%;  | ||||||
|  |         font-size: smaller;  | ||||||
|  |         margin-top: 10px; | ||||||
|  |         flex-grow: 1; | ||||||
|  |         overflow: auto; | ||||||
|  |         min-height: 0; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     .table-schema td { | ||||||
|  |         padding: 5px; | ||||||
|  |     } | ||||||
|  |     </style> | ||||||
|  |      | ||||||
|  |     Tables: | ||||||
|  |     <span class="sql-table-schemas"></span> | ||||||
|  | </div>`; | ||||||
|  | 
 | ||||||
|  | export default class SqlTableSchemasWidget extends TabAwareWidget { | ||||||
|  |     isEnabled() { | ||||||
|  |         return this.note | ||||||
|  |             && this.note.mime === 'text/x-sqlite;schema=trilium' | ||||||
|  |             && super.isEnabled(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     doRender() { | ||||||
|  |         this.$widget = $(TPL); | ||||||
|  |         this.overflowing(); | ||||||
|  | 
 | ||||||
|  |         this.$sqlConsoleTableSchemas = this.$widget.find('.sql-table-schemas'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async refreshWithNote(note) { | ||||||
|  |         if (this.tableSchemasShown) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.tableSchemasShown = true; | ||||||
|  | 
 | ||||||
|  |         const tableSchema = await server.get('sql/schema'); | ||||||
|  | 
 | ||||||
|  |         for (const table of tableSchema) { | ||||||
|  |             const $tableLink = $('<button class="btn">').text(table.name); | ||||||
|  | 
 | ||||||
|  |             const $table = $('<table class="table-schema">'); | ||||||
|  | 
 | ||||||
|  |             for (const column of table.columns) { | ||||||
|  |                 $table.append( | ||||||
|  |                     $("<tr>") | ||||||
|  |                         .append($("<td>").text(column.name)) | ||||||
|  |                         .append($("<td>").text(column.type)) | ||||||
|  |                 ); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             this.$sqlConsoleTableSchemas.append($tableLink).append(" "); | ||||||
|  | 
 | ||||||
|  |             $tableLink.tooltip({ | ||||||
|  |                 html: true, | ||||||
|  |                 placement: 'bottom', | ||||||
|  |                 boundary: 'window', | ||||||
|  |                 title: $table[0].outerHTML, | ||||||
|  |                 sanitize: false | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -9,65 +9,24 @@ const TPL = ` | |||||||
| <div class="note-detail-code note-detail-printable"> | <div class="note-detail-code note-detail-printable"> | ||||||
|     <style> |     <style> | ||||||
|     .note-detail-code { |     .note-detail-code { | ||||||
|         overflow: auto; |  | ||||||
|         height: 100%; |  | ||||||
|         display: flex; |         display: flex; | ||||||
|         flex-direction: column; |         flex-direction: column; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     .note-detail-code-editor { |     .note-detail-code-editor { | ||||||
|         flex-basis: 200px; |         min-height: 300px; | ||||||
|         min-height: 200px; |  | ||||||
|         flex-grow: 1; |  | ||||||
|         overflow: auto; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     .sql-console-table-schemas button { |  | ||||||
|         padding: 0.25rem 0.4rem; |  | ||||||
|         font-size: 0.875rem; |  | ||||||
|         line-height: 0.5; |  | ||||||
|         border-radius: 0.2rem; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     .sql-console-result-wrapper { |  | ||||||
|         flex-grow: 100; |  | ||||||
|         display: flex; |  | ||||||
|         flex-direction: column; |  | ||||||
|         min-height: 0; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     .sql-console-result-container { |  | ||||||
|         width: 100%;  |  | ||||||
|         font-size: smaller;  |  | ||||||
|         margin-top: 10px; |  | ||||||
|         flex-grow: 1; |  | ||||||
|         overflow: auto; |  | ||||||
|         min-height: 0; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     .table-schema td { |  | ||||||
|         padding: 5px; |  | ||||||
|     } |     } | ||||||
|     </style> |     </style> | ||||||
| 
 | 
 | ||||||
|     <div class="sql-console-area"> |     <button data-trigger-command="runActiveNote" | ||||||
|         Tables: |             class="no-print execute-button btn btn-sm" | ||||||
|         <span class="sql-console-table-schemas"></span> |             style="position: absolute; top: 0px; right: 10px; z-index: 1000;"> | ||||||
|     </div> |         Execute | ||||||
|  |     </button> | ||||||
| 
 | 
 | ||||||
|     <div class="note-detail-code-editor"></div> |     <div class="note-detail-code-editor"></div> | ||||||
|      |  | ||||||
|     <div class="sql-console-area sql-console-result-wrapper"> |  | ||||||
|         <div style="text-align: center"> |  | ||||||
|             <button class="btn btn-danger sql-console-execute">Execute query <kbd>Ctrl+Enter</kbd></button> |  | ||||||
|         </div> |  | ||||||
| 
 |  | ||||||
|         <div class="sql-console-result-container"></div> |  | ||||||
|     </div> |  | ||||||
| </div>`; | </div>`; | ||||||
| 
 | 
 | ||||||
| let TABLE_SCHEMA; |  | ||||||
| 
 |  | ||||||
| export default class EditableCodeTypeWidget extends TypeWidget { | export default class EditableCodeTypeWidget extends TypeWidget { | ||||||
|     static getType() { return "editable-code"; } |     static getType() { return "editable-code"; } | ||||||
| 
 | 
 | ||||||
| @ -75,17 +34,10 @@ export default class EditableCodeTypeWidget extends TypeWidget { | |||||||
|         this.$widget = $(TPL); |         this.$widget = $(TPL); | ||||||
|         this.contentSized(); |         this.contentSized(); | ||||||
|         this.$editor = this.$widget.find('.note-detail-code-editor'); |         this.$editor = this.$widget.find('.note-detail-code-editor'); | ||||||
|         this.$sqlConsoleArea = this.$widget.find('.sql-console-area'); |         this.$executeButton = this.$widget.find('.execute-button'); | ||||||
|         this.$sqlConsoleTableSchemas = this.$widget.find('.sql-console-table-schemas'); |  | ||||||
|         this.$sqlConsoleExecuteButton = this.$widget.find('.sql-console-execute'); |  | ||||||
|         this.$sqlConsoleResultContainer = this.$widget.find('.sql-console-result-container'); |  | ||||||
| 
 | 
 | ||||||
|         keyboardActionService.setupActionsForElement('code-detail', this.$widget, this); |         keyboardActionService.setupActionsForElement('code-detail', this.$widget, this); | ||||||
| 
 | 
 | ||||||
|         utils.bindElShortcut(this.$editor, 'ctrl+return', () => this.execute()); |  | ||||||
| 
 |  | ||||||
|         this.$sqlConsoleExecuteButton.on('click', () => this.execute()); |  | ||||||
| 
 |  | ||||||
|         this.initialized = this.initEditor(); |         this.initialized = this.initEditor(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -122,6 +74,11 @@ export default class EditableCodeTypeWidget extends TypeWidget { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async doRefresh(note) { |     async doRefresh(note) { | ||||||
|  |         this.$executeButton.toggle( | ||||||
|  |             note.mime.startsWith('application/javascript') | ||||||
|  |             || note.mime === 'text/x-sqlite;schema=trilium' | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|         const noteComplement = await this.tabContext.getNoteComplement(); |         const noteComplement = await this.tabContext.getNoteComplement(); | ||||||
| 
 | 
 | ||||||
|         await this.spacedUpdate.allowUpdateWithoutChange(() => { |         await this.spacedUpdate.allowUpdateWithoutChange(() => { | ||||||
| @ -138,104 +95,9 @@ export default class EditableCodeTypeWidget extends TypeWidget { | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         const isSqlConsole = note.mime === 'text/x-sqlite;schema=trilium'; |  | ||||||
| 
 |  | ||||||
|         this.$sqlConsoleArea.toggle(isSqlConsole); |  | ||||||
| 
 |  | ||||||
|         if (isSqlConsole) { |  | ||||||
|             await this.showTableSchemas(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         this.show(); |         this.show(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async showTableSchemas() { |  | ||||||
|         if (!TABLE_SCHEMA) { |  | ||||||
|             TABLE_SCHEMA = await server.get('sql/schema'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         this.$sqlConsoleTableSchemas.empty(); |  | ||||||
| 
 |  | ||||||
|         for (const table of TABLE_SCHEMA) { |  | ||||||
|             const $tableLink = $('<button class="btn">').text(table.name); |  | ||||||
| 
 |  | ||||||
|             const $table = $('<table class="table-schema">'); |  | ||||||
| 
 |  | ||||||
|             for (const column of table.columns) { |  | ||||||
|                 $table.append( |  | ||||||
|                     $("<tr>") |  | ||||||
|                         .append($("<td>").text(column.name)) |  | ||||||
|                         .append($("<td>").text(column.type)) |  | ||||||
|                 ); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             this.$sqlConsoleTableSchemas.append($tableLink).append(" "); |  | ||||||
| 
 |  | ||||||
|             $tableLink |  | ||||||
|                 .tooltip({ |  | ||||||
|                     html: true, |  | ||||||
|                     placement: 'bottom', |  | ||||||
|                     boundary: 'window', |  | ||||||
|                     title: $table[0].outerHTML, |  | ||||||
|                     sanitize: false |  | ||||||
|                 }) |  | ||||||
|                 .on('click', () => this.codeEditor.setValue("SELECT * FROM " + table.name + " LIMIT 100")); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     async execute() { |  | ||||||
|         // execute the selected text or the whole content if there's no selection
 |  | ||||||
|         let sqlQuery = this.codeEditor.getSelection(); |  | ||||||
| 
 |  | ||||||
|         if (!sqlQuery) { |  | ||||||
|             sqlQuery = this.codeEditor.getValue(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         const result = await server.post("sql/execute", { |  | ||||||
|             query: sqlQuery |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         if (!result.success) { |  | ||||||
|             toastService.showError(result.error); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             toastService.showMessage("Query was executed successfully."); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         const results = result.results; |  | ||||||
| 
 |  | ||||||
|         this.$sqlConsoleResultContainer.empty(); |  | ||||||
| 
 |  | ||||||
|         for (const rows of results) { |  | ||||||
|             if (rows.length === 0) { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             const $table = $('<table class="table table-striped">'); |  | ||||||
|             this.$sqlConsoleResultContainer.append($table); |  | ||||||
| 
 |  | ||||||
|             const result = rows[0]; |  | ||||||
|             const $row = $("<tr>"); |  | ||||||
| 
 |  | ||||||
|             for (const key in result) { |  | ||||||
|                 $row.append($("<th>").html(key)); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             $table.append($row); |  | ||||||
| 
 |  | ||||||
|             for (const result of rows) { |  | ||||||
|                 const $row = $("<tr>"); |  | ||||||
| 
 |  | ||||||
|                 for (const key in result) { |  | ||||||
|                     $row.append($("<td>").html(result[key])); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 $table.append($row); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     show() { |     show() { | ||||||
|         this.$widget.show(); |         this.$widget.show(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const sql = require('../../services/sql'); | const sql = require('../../services/sql'); | ||||||
|  | const repository = require('../../services/repository'); | ||||||
| 
 | 
 | ||||||
| function getSchema() { | function getSchema() { | ||||||
|     const tableNames = sql.getColumn(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`); |     const tableNames = sql.getColumn(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`); | ||||||
| @ -17,7 +18,13 @@ function getSchema() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function execute(req) { | function execute(req) { | ||||||
|     const queries = req.body.query.split("\n---"); |     const note = repository.getNote(req.params.noteId); | ||||||
|  | 
 | ||||||
|  |     if (!note) { | ||||||
|  |         return [404, `Note ${req.params.noteId} was not found.`]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const queries = note.getContent().split("\n---"); | ||||||
| 
 | 
 | ||||||
|     try { |     try { | ||||||
|         const results = []; |         const results = []; | ||||||
|  | |||||||
| @ -232,7 +232,7 @@ function register(app) { | |||||||
|     route(POST, '/api/setup/sync-seed', [auth.checkAppNotInitialized], setupApiRoute.saveSyncSeed, apiResultHandler, false); |     route(POST, '/api/setup/sync-seed', [auth.checkAppNotInitialized], setupApiRoute.saveSyncSeed, apiResultHandler, false); | ||||||
| 
 | 
 | ||||||
|     apiRoute(GET, '/api/sql/schema', sqlRoute.getSchema); |     apiRoute(GET, '/api/sql/schema', sqlRoute.getSchema); | ||||||
|     apiRoute(POST, '/api/sql/execute', sqlRoute.execute); |     apiRoute(POST, '/api/sql/execute/:noteId', sqlRoute.execute); | ||||||
|     route(POST, '/api/database/anonymize', [auth.checkApiAuthOrElectron, csrfMiddleware], databaseRoute.anonymize, apiResultHandler, false); |     route(POST, '/api/database/anonymize', [auth.checkApiAuthOrElectron, csrfMiddleware], databaseRoute.anonymize, apiResultHandler, false); | ||||||
| 
 | 
 | ||||||
|     // backup requires execution outside of transaction
 |     // backup requires execution outside of transaction
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 zadam
						zadam