diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 11e7a9fd..8f3b34af 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -31,11 +31,6 @@ jobs: # a pull request then we can checkout the head. fetch-depth: 2 - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} - # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 diff --git a/README.md b/README.md index a3e30ee9..cade9c91 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ An application created with minimalism and semplicity in mind, with features in - Database management (add/edit/delete). - Full tables management, including indexes and foreign keys. - Run queries on multiple tabs. -- Query suggestions. +- Query suggestions and auto complete. - Native dark theme. - Multi language. - Secure password storage. @@ -49,8 +49,6 @@ This is a roadmap with major features will come in near future. - SSL and SSH tunnel support. - Support for other databases. - UI/UX improvements. -- Improvements of query editor area. -- Improvements of query suggestions. - Query history. - More context menu shortcuts. - More keyboard shortcuts. diff --git a/src/renderer/components/ModalSettings.vue b/src/renderer/components/ModalSettings.vue index 7bf6778e..1ac2e129 100644 --- a/src/renderer/components/ModalSettings.vue +++ b/src/renderer/components/ModalSettings.vue @@ -96,8 +96,65 @@
-
-

In future releases

+
+
+
+ {{ $t('message.applicationTheme') }} +
+
+ +
+ +
+ {{ $t('word.dark') }} +
+
+
+
+
+ +
+ {{ $t('word.light') }} (Coming) +
+
+
+
+ +
+
+ {{ $t('message.editorTheme') }} +
+
+ +
+
+ +
+
@@ -127,18 +184,70 @@ import { mapActions, mapGetters } from 'vuex'; import localesNames from '@/i18n/supported-locales'; import ModalSettingsUpdate from '@/components/ModalSettingsUpdate'; +import QueryEditor from '@/components/QueryEditor'; const { shell } = require('electron'); export default { name: 'ModalSettings', components: { - ModalSettingsUpdate + ModalSettingsUpdate, + QueryEditor }, data () { return { localLocale: null, localTimeout: null, - selectedTab: 'general' + localEditorTheme: null, + selectedTab: 'general', + editorThemes: [ + { + group: this.$t('word.light'), + themes: [ + { code: 'chrome', name: 'Chrome' }, + { code: 'clouds', name: 'Clouds' }, + { code: 'crimson_editor', name: 'Crimson Editor' }, + { code: 'dawn', name: 'Dawn' }, + { code: 'dreamweaver', name: 'Dreamweaver' }, + { code: 'eclupse', name: 'Eclipse' }, + { code: 'github', name: 'GitHub' }, + { code: 'iplastic', name: 'IPlastic' }, + { code: 'solarized_light', name: 'Solarized Light' }, + { code: 'textmate', name: 'TextMate' }, + { code: 'tomorrow', name: 'Tomorrow' }, + { code: 'xcode', name: 'Xcode' }, + { code: 'kuroir', name: 'Kuroir' }, + { code: 'katzenmilch', name: 'KatzenMilch' }, + { code: 'sqlserver', name: 'SQL Server' } + ] + }, + { + group: this.$t('word.dark'), + themes: [ + { code: 'ambiance', name: 'Ambiance' }, + { code: 'chaos', name: 'Chaos' }, + { code: 'clouds_midnight', name: 'Clouds Midnight' }, + { code: 'dracula', name: 'Dracula' }, + { code: 'cobalt', name: 'Cobalt' }, + { code: 'gruvbox', name: 'Gruvbox' }, + { code: 'gob', name: 'Green on Black' }, + { code: 'idle_fingers', name: 'Idle Fingers' }, + { code: 'kr_theme', name: 'krTheme' }, + { code: 'merbivore', name: 'Merbivore' }, + { code: 'mono_industrial', name: 'Mono Industrial' }, + { code: 'monokai', name: 'Monokai' }, + { code: 'nord_dark', name: 'Nord Dark' }, + { code: 'pastel_on_dark', name: 'Pastel on Dark' }, + { code: 'solarized_dark', name: 'Solarized Dark' }, + { code: 'terminal', name: 'Terminal' }, + { code: 'tomorrow_night', name: 'Tomorrow Night' }, + { code: 'tomorrow_night_blue', name: 'Tomorrow Night Blue' }, + { code: 'tomorrow_night_bright', name: 'Tomorrow Night Bright' }, + { code: 'tomorrow_night_eighties', name: 'Tomorrow Night 80s' }, + { code: 'twilight', name: 'Twilight' }, + { code: 'vibrant_ink', name: 'Vibrant Ink' } + ] + } + ] }; }, computed: { @@ -148,7 +257,11 @@ export default { selectedSettingTab: 'application/selectedSettingTab', selectedLocale: 'settings/getLocale', notificationsTimeout: 'settings/getNotificationsTimeout', - updateStatus: 'application/getUpdateStatus' + applicationTheme: 'settings/getApplicationTheme', + editorTheme: 'settings/getEditorTheme', + updateStatus: 'application/getUpdateStatus', + selectedWorkspace: 'workspaces/getSelected', + getWorkspace: 'workspaces/getWorkspace' }), locales () { const locales = []; @@ -159,11 +272,32 @@ export default { }, hasUpdates () { return ['available', 'downloading', 'downloaded'].includes(this.updateStatus); + }, + workspace () { + return this.getWorkspace(this.selectedWorkspace); + }, + exampleQuery () { + return `-- This is an example +SELECT + employee.id, + employee.first_name, + employee.last_name, + SUM(DATEDIFF("SECOND", call.start, call.end)) AS call_duration +FROM call +INNER JOIN employee ON call.employee_id = employee.id +GROUP BY + employee.id, + employee.first_name, + employee.last_name +ORDER BY + employee.id ASC; +`; } }, created () { this.localLocale = this.selectedLocale; this.localTimeout = this.notificationsTimeout; + this.localEditorTheme = this.editorTheme; this.selectedTab = this.selectedSettingTab; window.addEventListener('keydown', this.onKey); }, @@ -174,6 +308,7 @@ export default { ...mapActions({ closeModal: 'application/hideSettingModal', changeLocale: 'settings/changeLocale', + changeEditorTheme: 'settings/changeEditorTheme', updateNotificationsTimeout: 'settings/updateNotificationsTimeout' }), selectTab (tab) { @@ -205,6 +340,34 @@ export default { .panel-body { height: calc(70vh - 70px); overflow: auto; + + .theme-block { + position: relative; + text-align: center; + + &.selected { + img { + box-shadow: 0 0 0 3px $primary-color; + } + } + + &.disabled { + cursor: not-allowed; + opacity: 0.5; + } + + .theme-name { + position: absolute; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + top: 0; + height: 100%; + width: 100%; + text-shadow: 0 0 8px #000; + } + } } .badge::after { diff --git a/src/renderer/components/QueryEditor.vue b/src/renderer/components/QueryEditor.vue index 0557219a..9db5f6dc 100644 --- a/src/renderer/components/QueryEditor.vue +++ b/src/renderer/components/QueryEditor.vue @@ -1,6 +1,10 @@ @@ -8,15 +12,18 @@ import * as ace from 'ace-builds'; import 'ace-builds/webpack-resolver'; import '../libs/ext-language_tools'; +import { mapGetters } from 'vuex'; import Tables from '@/ipc-api/Tables'; export default { name: 'QueryEditor', props: { value: String, - schema: String, + workspace: Object, + schema: { type: String, default: '' }, autoFocus: { type: Boolean, default: false }, - workspace: Object + readOnly: { type: Boolean, default: false }, + height: { type: Number, default: 200 } }, data () { return { @@ -26,6 +33,9 @@ export default { }; }, computed: { + ...mapGetters({ + editorTheme: 'settings/getEditorTheme' + }), tables () { return this.workspace.structure.filter(schema => schema.name === this.schema) .reduce((acc, curr) => { @@ -36,7 +46,7 @@ export default { name: table.name, comment: table.comment, type: table.type, - fields: ['TODO'] + fields: [] }; }); }, @@ -76,13 +86,20 @@ export default { }; } }, + watch: { + editorTheme () { + if (this.editor) + this.editor.setTheme(`ace/theme/${this.editorTheme}`); + } + }, mounted () { this.editor = ace.edit(this.$refs.editor, { mode: `ace/mode/${this.mode}`, - theme: 'ace/theme/twilight', + theme: `ace/theme/${this.editorTheme}`, value: this.value, fontSize: '14px', - printMargin: false + printMargin: false, + readOnly: this.readOnly }); this.editor.setOptions({ @@ -157,7 +174,6 @@ export default { border-bottom: 1px solid #444; .editor { - height: 200px; width: 100%; } } diff --git a/src/renderer/components/Workspace.vue b/src/renderer/components/Workspace.vue index f77dbc76..4bea670d 100644 --- a/src/renderer/components/Workspace.vue +++ b/src/renderer/components/Workspace.vue @@ -193,6 +193,7 @@ export default { align-items: flex-start; flex-wrap: nowrap; overflow: auto; + margin-bottom: 0; &::-webkit-scrollbar { width: 2px; diff --git a/src/renderer/i18n/en-US.js b/src/renderer/i18n/en-US.js index 19ff198f..c09450b6 100644 --- a/src/renderer/i18n/en-US.js +++ b/src/renderer/i18n/en-US.js @@ -62,7 +62,9 @@ module.exports = { table: 'Table', discard: 'Discard', stay: 'Stay', - author: 'Author' + author: 'Author', + light: 'Light', + dark: 'Dark' }, message: { appWelcome: 'Welcome to Antares SQL Client!', @@ -124,7 +126,9 @@ module.exports = { referenceField: 'Ref. field', foreignFields: 'Foreign fields', invalidDefault: 'Invalid default', - onDelete: 'On delete' + onDelete: 'On delete', + applicationTheme: 'Application Theme', + editorTheme: 'Editor Theme' }, // Date and Time short: { diff --git a/src/renderer/images/dark.png b/src/renderer/images/dark.png new file mode 100644 index 00000000..136134cf Binary files /dev/null and b/src/renderer/images/dark.png differ diff --git a/src/renderer/store/modules/settings.store.js b/src/renderer/store/modules/settings.store.js index 1b5cb6a5..e2d88cde 100644 --- a/src/renderer/store/modules/settings.store.js +++ b/src/renderer/store/modules/settings.store.js @@ -9,12 +9,16 @@ export default { state: { locale: persistentStore.get('locale') || 'en-US', explorebar_size: persistentStore.get('explorebar_size') || null, - notifications_timeout: persistentStore.get('notifications_timeout') || 5 + notifications_timeout: persistentStore.get('notifications_timeout') || 5, + application_theme: persistentStore.get('application_theme') || 'dark', + editor_theme: persistentStore.get('editor_theme') || 'twilight' }, getters: { getLocale: state => state.locale, getExplorebarSize: state => state.explorebar_size, - getNotificationsTimeout: state => state.notifications_timeout + getNotificationsTimeout: state => state.notifications_timeout, + getApplicationTheme: state => state.application_theme, + getEditorTheme: state => state.editor_theme }, mutations: { SET_LOCALE (state, locale) { @@ -29,6 +33,9 @@ export default { SET_EXPLOREBAR_SIZE (state, size) { state.explorebar_size = size; persistentStore.set('explorebar_size', state.explorebar_size); + }, + SET_EDITOR_THEME (state, theme) { + state.editor_theme = theme; } }, actions: { @@ -40,6 +47,9 @@ export default { }, changeExplorebarSize ({ commit }, size) { commit('SET_EXPLOREBAR_SIZE', size); + }, + changeEditorTheme ({ commit }, theme) { + commit('SET_EDITOR_THEME', theme); } } };