mirror of https://github.com/Fabio286/antares.git
feat: initial console implementation
This commit is contained in:
parent
f12a04b052
commit
6a6f43a718
|
@ -25,7 +25,7 @@ export interface IpcResponse<T = any> {
|
|||
*/
|
||||
export interface ClientParams {
|
||||
client: ClientCode;
|
||||
uid: string;
|
||||
uid?: string;
|
||||
params:
|
||||
mysql.ConnectionOptions & {schema: string; ssl?: mysql.SslOptions; ssh?: SSHConfig; readonly: boolean}
|
||||
| pg.ClientConfig & {schema: string; ssl?: mysql.SslOptions; ssh?: SSHConfig; readonly: boolean}
|
||||
|
|
|
@ -29,6 +29,11 @@ const shortcuts: ShortcutRecord[] = [
|
|||
event: 'open-connections-modal',
|
||||
keys: ['Shift+CommandOrControl+Space'],
|
||||
description: 'Show all connections'
|
||||
},
|
||||
{
|
||||
event: 'toggle-console',
|
||||
keys: ['CommandOrControl+F12', 'CommandOrControl+`'],
|
||||
description: 'Toggle console'
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ const queryLogger = ({ sql, cUid }: {sql: string; cUid: string}) => {
|
|||
// Remove comments, newlines and multiple spaces
|
||||
const escapedSql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '').replace(/\s\s+/g, ' ');
|
||||
const mainWindow = webContents.fromId(1);
|
||||
mainWindow.send('query-log', { cUid, sql: escapedSql });
|
||||
mainWindow.send('query-log', { cUid, sql: escapedSql, date: new Date() });
|
||||
console.log(escapedSql);
|
||||
};
|
||||
|
||||
|
|
|
@ -155,9 +155,11 @@ else {
|
|||
}
|
||||
}
|
||||
|
||||
// Main process shortcuts
|
||||
if (isDevelopment) {
|
||||
globalShortcut.register('CommandOrControl+F12', () => {
|
||||
if (isDevelopment) { // Dev shortcuts
|
||||
globalShortcut.register('Shift+CommandOrControl+F5', () => {
|
||||
mainWindow.reload();
|
||||
});
|
||||
globalShortcut.register('Shift+CommandOrControl+F12', () => {
|
||||
mainWindow.webContents.openDevTools();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -71,10 +71,6 @@ ipcRenderer.on('open-connections-modal', () => {
|
|||
isAllConnectionsModal.value = true;
|
||||
});
|
||||
|
||||
ipcRenderer.on('query-log', (e, sql: string) => {
|
||||
console.log(sql);
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
setTimeout(() => {
|
||||
changeApplicationTheme(applicationTheme.value);// Forces persistentStore to save on file and mail process
|
||||
|
@ -155,10 +151,10 @@ onMounted(() => {
|
|||
.connection-panel-wrapper {
|
||||
height: calc(100vh - #{$excluding-size});
|
||||
width: 100%;
|
||||
padding-top: 15vh;
|
||||
padding-top: 2rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
align-items: center;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,14 +11,30 @@
|
|||
|
||||
<div class="footer-right-elements">
|
||||
<ul class="footer-elements">
|
||||
<li
|
||||
v-if="workspace !== 'NEW'"
|
||||
class="footer-element footer-link"
|
||||
@click="toggleConsole"
|
||||
>
|
||||
<i class="mdi mdi-18px mdi-console-line mr-1" />
|
||||
<small>{{ t('word.console') }}</small>
|
||||
</li>
|
||||
<li class="footer-element footer-link" @click="openOutside('https://www.paypal.com/paypalme/fabiodistasio')">
|
||||
<i class="mdi mdi-18px mdi-coffee mr-1" />
|
||||
<small>{{ $t('word.donate') }}</small>
|
||||
<small>{{ t('word.donate') }}</small>
|
||||
</li>
|
||||
<li class="footer-element footer-link" @click="openOutside('https://github.com/antares-sql/antares/issues')">
|
||||
<li
|
||||
class="footer-element footer-link"
|
||||
:title="t('message.reportABug')"
|
||||
@click="openOutside('https://github.com/antares-sql/antares/issues')"
|
||||
>
|
||||
<i class="mdi mdi-18px mdi-bug" />
|
||||
</li>
|
||||
<li class="footer-element footer-link" @click="showSettingModal('about')">
|
||||
<li
|
||||
class="footer-element footer-link"
|
||||
:title="t('word.about')"
|
||||
@click="showSettingModal('about')"
|
||||
>
|
||||
<i class="mdi mdi-18px mdi-information-outline" />
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -29,9 +45,13 @@
|
|||
<script setup lang="ts">
|
||||
import { shell } from 'electron';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useApplicationStore } from '@/stores/application';
|
||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||
import { computed, ComputedRef } from 'vue';
|
||||
import { useConsoleStore } from '@/stores/console';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
interface DatabaseInfos {// TODO: temp
|
||||
name: string;
|
||||
|
@ -44,6 +64,7 @@ const applicationStore = useApplicationStore();
|
|||
const workspacesStore = useWorkspacesStore();
|
||||
|
||||
const { getSelected: workspace } = storeToRefs(workspacesStore);
|
||||
const { toggleConsole } = useConsoleStore();
|
||||
|
||||
const { showSettingModal } = applicationStore;
|
||||
const { getWorkspace } = workspacesStore;
|
||||
|
|
|
@ -451,9 +451,11 @@
|
|||
:schema="tab.schema"
|
||||
/>
|
||||
</template>
|
||||
<WorkspaceQueryConsole v-if="isConsoleOpen" :uid="workspace.uid" />
|
||||
</div>
|
||||
<div v-else class="connection-panel-wrapper">
|
||||
<div v-else class="connection-panel-wrapper p-relative">
|
||||
<WorkspaceEditConnectionPanel :connection="connection" />
|
||||
<WorkspaceQueryConsole v-if="isConsoleOpen" :uid="workspace.uid" />
|
||||
</div>
|
||||
<ModalProcessesList
|
||||
v-if="isProcessesModal"
|
||||
|
@ -476,6 +478,7 @@ import { storeToRefs } from 'pinia';
|
|||
import * as Draggable from 'vuedraggable';
|
||||
import Connection from '@/ipc-api/Connection';
|
||||
import { useWorkspacesStore, WorkspaceTab } from '@/stores/workspaces';
|
||||
import { useConsoleStore } from '@/stores/console';
|
||||
import { ConnectionParams } from 'common/interfaces/antares';
|
||||
|
||||
import WorkspaceEmptyState from '@/components/WorkspaceEmptyState.vue';
|
||||
|
@ -483,6 +486,7 @@ import WorkspaceExploreBar from '@/components/WorkspaceExploreBar.vue';
|
|||
import WorkspaceEditConnectionPanel from '@/components/WorkspaceEditConnectionPanel.vue';
|
||||
import WorkspaceTabQuery from '@/components/WorkspaceTabQuery.vue';
|
||||
import WorkspaceTabTable from '@/components/WorkspaceTabTable.vue';
|
||||
import WorkspaceQueryConsole from '@/components/WorkspaceQueryConsole.vue';
|
||||
|
||||
import WorkspaceTabNewTable from '@/components/WorkspaceTabNewTable.vue';
|
||||
import WorkspaceTabNewView from '@/components/WorkspaceTabNewView.vue';
|
||||
|
@ -518,6 +522,8 @@ const {
|
|||
selectPrevTab
|
||||
} = workspacesStore;
|
||||
|
||||
const { isConsoleOpen } = storeToRefs(useConsoleStore());
|
||||
|
||||
const props = defineProps({
|
||||
connection: Object as Prop<ConnectionParams>
|
||||
});
|
||||
|
@ -680,8 +686,9 @@ onMounted(() => {
|
|||
margin: 0;
|
||||
|
||||
.workspace-tabs {
|
||||
overflow: hidden;
|
||||
overflow-y: hidden;
|
||||
height: calc(100vh - #{$excluding-size});
|
||||
position: relative;
|
||||
|
||||
.tab-block {
|
||||
margin-top: 0;
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
<template>
|
||||
<div
|
||||
ref="wrapper"
|
||||
class="query-console-wrapper"
|
||||
@mouseenter="isHover = true"
|
||||
@mouseleave="isHover = false"
|
||||
>
|
||||
<div ref="resizer" class="query-console-resizer" />
|
||||
<div
|
||||
id="query-console"
|
||||
ref="queryConsole"
|
||||
class="query-console column col-12"
|
||||
:style="{height: localHeight ? localHeight+'px' : ''}"
|
||||
>
|
||||
<div class="query-console-header">
|
||||
<div>{{ t('word.console') }}</div>
|
||||
<i class="mdi mdi-close c-hand" @click="resizeConsole(0)" />
|
||||
</div>
|
||||
<div ref="queryConsoleBody" class="query-console-body">
|
||||
<div
|
||||
v-for="(wLog, i) in workspaceLogs"
|
||||
:key="i"
|
||||
class="query-console-log"
|
||||
>
|
||||
<span class="type-datetime">{{ moment(wLog.date).format('YYYY-MM-DD HH:mm:ss') }}</span>: <span class="type-string">{{ wLog.sql }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, ref, Ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import * as moment from 'moment';
|
||||
import { useConsoleStore } from '@/stores/console';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const consoleStore = useConsoleStore();
|
||||
|
||||
const { resizeConsole, getLogsByWorkspace } = consoleStore;
|
||||
const { consoleHeight } = storeToRefs(consoleStore);
|
||||
|
||||
const props = defineProps({
|
||||
uid: String
|
||||
});
|
||||
|
||||
const wrapper: Ref<HTMLInputElement> = ref(null);
|
||||
const queryConsole: Ref<HTMLInputElement> = ref(null);
|
||||
const queryConsoleBody: Ref<HTMLInputElement> = ref(null);
|
||||
const resizer: Ref<HTMLInputElement> = ref(null);
|
||||
const localHeight = ref(250);
|
||||
const isHover = ref(false);
|
||||
|
||||
const resize = (e: MouseEvent) => {
|
||||
const el = queryConsole.value;
|
||||
let consoleHeight = el.getBoundingClientRect().bottom - e.pageY;
|
||||
if (consoleHeight > 400) consoleHeight = 400;
|
||||
localHeight.value = consoleHeight;
|
||||
};
|
||||
|
||||
const workspaceLogs = computed(() => {
|
||||
return getLogsByWorkspace(props.uid);
|
||||
});
|
||||
|
||||
const stopResize = () => {
|
||||
if (localHeight.value < 0) localHeight.value = 0;
|
||||
resizeConsole(localHeight.value);
|
||||
window.removeEventListener('mousemove', resize);
|
||||
window.removeEventListener('mouseup', stopResize);
|
||||
};
|
||||
|
||||
watch(workspaceLogs, async () => {
|
||||
if (!isHover.value) {
|
||||
await nextTick();
|
||||
queryConsoleBody.value.scrollTop = queryConsoleBody.value.scrollHeight;
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
localHeight.value = consoleHeight.value;
|
||||
queryConsoleBody.value.scrollTop = queryConsoleBody.value.scrollHeight;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
resizer.value.addEventListener('mousedown', (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
window.addEventListener('mousemove', resize);
|
||||
window.addEventListener('mouseup', stopResize);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.query-console-wrapper{
|
||||
width: 100%;
|
||||
z-index: 9;
|
||||
margin-top: auto;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
|
||||
.query-console-resizer{
|
||||
height: 4px;
|
||||
top: -1px;
|
||||
width: 100%;
|
||||
cursor: ns-resize;
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
transition: background 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: rgba($primary-color, 50%);
|
||||
}
|
||||
}
|
||||
|
||||
.query-console {
|
||||
padding: 0;
|
||||
padding-bottom: $footer-height;
|
||||
.query-console-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 4px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.query-console-body {
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 100%;
|
||||
padding: 0 6px;
|
||||
|
||||
.query-console-log {
|
||||
padding: 1px 3px;
|
||||
user-select: text;
|
||||
border-radius: $border-radius;
|
||||
&:hover {
|
||||
background: $bg-color-gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
|
@ -372,6 +372,8 @@ const resize = (e: MouseEvent) => {
|
|||
editorHeight.value = localEditorHeight;
|
||||
};
|
||||
|
||||
const resizeResults = () => queryTable.value.resizeResults();
|
||||
|
||||
const onWindowResize = (e: MouseEvent) => {
|
||||
const el = queryEditor.value.$el;
|
||||
const queryFooterHeight = queryAreaFooter.value.clientHeight;
|
||||
|
@ -386,7 +388,7 @@ const onWindowResize = (e: MouseEvent) => {
|
|||
const stopResize = () => {
|
||||
window.removeEventListener('mousemove', resize);
|
||||
if (queryTable.value && results.value.length)
|
||||
queryTable.value.resizeResults();
|
||||
resizeResults();
|
||||
|
||||
if (queryEditor.value)
|
||||
queryEditor.value.editor.resize();
|
||||
|
@ -476,6 +478,8 @@ const rollbackTab = async () => {
|
|||
isQuering.value = false;
|
||||
};
|
||||
|
||||
defineExpose({ resizeResults });
|
||||
|
||||
query.value = props.tab.content as string;
|
||||
selectedSchema.value = props.tab.schema || breadcrumbsSchema.value;
|
||||
|
||||
|
|
|
@ -118,6 +118,7 @@ import { storeToRefs } from 'pinia';
|
|||
import { uidGen } from 'common/libs/uidGen';
|
||||
import { useSettingsStore } from '@/stores/settings';
|
||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||
import { useConsoleStore } from '@/stores/console';
|
||||
import { arrayToFile } from '../libs/arrayToFile';
|
||||
import { TEXT, LONG_TEXT, BLOB } from 'common/fieldTypes';
|
||||
import BaseVirtualScroll from '@/components/BaseVirtualScroll.vue';
|
||||
|
@ -132,10 +133,13 @@ import { TableUpdateParams } from 'common/interfaces/tableApis';
|
|||
const { t } = useI18n();
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const consoleStore = useConsoleStore();
|
||||
const { getWorkspace } = useWorkspacesStore();
|
||||
|
||||
const { dataTabLimit: pageSize } = storeToRefs(settingsStore);
|
||||
|
||||
const { consoleHeight } = storeToRefs(consoleStore);
|
||||
|
||||
const props = defineProps({
|
||||
results: Array as Prop<QueryResult[]>,
|
||||
connUid: String,
|
||||
|
@ -298,8 +302,13 @@ const resizeResults = () => {
|
|||
const el = tableWrapper.value;
|
||||
|
||||
if (el) {
|
||||
let sizeToSubtract = 0;
|
||||
const footer = document.getElementById('footer');
|
||||
const size = window.innerHeight - el.getBoundingClientRect().top - footer.offsetHeight;
|
||||
if (footer) sizeToSubtract += footer.offsetHeight;
|
||||
|
||||
sizeToSubtract += consoleHeight.value;
|
||||
|
||||
const size = window.innerHeight - el.getBoundingClientRect().top - sizeToSubtract;
|
||||
resultsSize.value = size;
|
||||
}
|
||||
resultTable.value.updateWindow();
|
||||
|
@ -664,6 +673,10 @@ watch(() => props.isSelected, async (val) => {
|
|||
}
|
||||
});
|
||||
|
||||
watch(consoleHeight, () => {
|
||||
resizeResults();
|
||||
});
|
||||
|
||||
onUpdated(() => {
|
||||
if (table.value)
|
||||
refreshScroller();
|
||||
|
|
|
@ -141,7 +141,8 @@ module.exports = {
|
|||
connectionString: 'Connection string',
|
||||
contributors: 'Contributors',
|
||||
pin: 'Pin',
|
||||
unpin: 'Unpin'
|
||||
unpin: 'Unpin',
|
||||
console: 'Console'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Welcome to Antares SQL Client!',
|
||||
|
@ -296,7 +297,8 @@ module.exports = {
|
|||
disableFKChecks: 'Disable foreigh key checks',
|
||||
allConnections: 'All connections',
|
||||
searchForConnections: 'Search for connections',
|
||||
disableScratchpad: 'Disable scratchpad'
|
||||
disableScratchpad: 'Disable scratchpad',
|
||||
reportABug: 'Report a bug'
|
||||
},
|
||||
faker: {
|
||||
address: 'Address',
|
||||
|
|
|
@ -10,6 +10,7 @@ import { VueMaskDirective } from 'v-mask';
|
|||
import { useApplicationStore } from '@/stores/application';
|
||||
import { useSettingsStore } from '@/stores/settings';
|
||||
import { useNotificationsStore } from '@/stores/notifications';
|
||||
import { useConsoleStore } from '@/stores/console';
|
||||
|
||||
import App from '@/App.vue';
|
||||
import i18n from '@/i18n';
|
||||
|
@ -36,6 +37,11 @@ ipcRenderer.on('unhandled-exception', (event, error) => {
|
|||
useNotificationsStore().addNotification({ status: 'error', message: error.message });
|
||||
});
|
||||
|
||||
// IPC query logs
|
||||
ipcRenderer.on('query-log', (event, logRecord) => {
|
||||
useConsoleStore().putLog(logRecord);
|
||||
});
|
||||
|
||||
// IPC app updates
|
||||
ipcRenderer.on('checking-for-update', () => {
|
||||
useApplicationStore().updateStatus = 'checking';
|
||||
|
|
|
@ -305,6 +305,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.query-console {
|
||||
border-top: 1px solid #444;
|
||||
background-color: $bg-color-dark;
|
||||
}
|
||||
|
||||
.tile {
|
||||
transition: background 0.2s;
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
import { ipcRenderer } from 'electron';
|
||||
import { defineStore } from 'pinia';
|
||||
const logsSize = 1000;
|
||||
|
||||
export interface ConsoleRecord {
|
||||
cUid: string;
|
||||
sql: string;
|
||||
date: Date;
|
||||
}
|
||||
|
||||
export const useConsoleStore = defineStore('console', {
|
||||
state: () => ({
|
||||
records: [] as ConsoleRecord[],
|
||||
consoleHeight: 0,
|
||||
isConsoleOpen: false
|
||||
}),
|
||||
getters: {
|
||||
getLogsByWorkspace: state => (uid: string) => state.records.filter(r => r.cUid === uid)
|
||||
},
|
||||
actions: {
|
||||
putLog (record: ConsoleRecord) {
|
||||
this.records.push(record);
|
||||
|
||||
if (this.records.length > logsSize)
|
||||
this.records = this.records.slice(0, logsSize);
|
||||
},
|
||||
resizeConsole (height: number) {
|
||||
if (height < 30) {
|
||||
this.consoleHeight = 0;
|
||||
this.isConsoleOpen = false;
|
||||
}
|
||||
else
|
||||
this.consoleHeight = height;
|
||||
},
|
||||
toggleConsole () {
|
||||
if (this.isConsoleOpen) {
|
||||
this.isConsoleOpen = false;
|
||||
this.consoleHeight = 0;
|
||||
}
|
||||
else {
|
||||
this.isConsoleOpen = true;
|
||||
this.consoleHeight = 250;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ipcRenderer.on('toggle-console', () => {
|
||||
useConsoleStore().toggleConsole();
|
||||
});
|
Loading…
Reference in New Issue