mirror of https://github.com/Fabio286/mizar.git
utility commit
This commit is contained in:
parent
9f945c4b8f
commit
f556bd3a8f
|
@ -4,9 +4,9 @@ const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const https = require('https');
|
const https = require('https');
|
||||||
const unzip = require('unzip-crx-3');
|
const unzip = require('unzip-crx-3');
|
||||||
const { antares } = require('../package.json');
|
const { mizar } = require('../package.json');
|
||||||
|
|
||||||
const extensionID = antares.devtoolsId;
|
const extensionID = mizar.devtoolsId;
|
||||||
const destFolder = path.resolve(__dirname, `../misc/${extensionID}`);
|
const destFolder = path.resolve(__dirname, `../misc/${extensionID}`);
|
||||||
const filePath = path.resolve(__dirname, `${destFolder}${extensionID}.crx`);
|
const filePath = path.resolve(__dirname, `${destFolder}${extensionID}.crx`);
|
||||||
const fileUrl = `https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&x=id%3D${extensionID}%26uc&prodversion=32`;
|
const fileUrl = `https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&x=id%3D${extensionID}%26uc&prodversion=32`;
|
||||||
|
|
|
@ -31,7 +31,7 @@ async function createMainWindow () {
|
||||||
x: mainWindowState.x,
|
x: mainWindowState.x,
|
||||||
y: mainWindowState.y,
|
y: mainWindowState.y,
|
||||||
minWidth: 900,
|
minWidth: 900,
|
||||||
minHeight: 550,
|
minHeight: 500,
|
||||||
show: !isWindows,
|
show: !isWindows,
|
||||||
title: 'Mizar',
|
title: 'Mizar',
|
||||||
icon: nativeImage.createFromDataURL(icon.default),
|
icon: nativeImage.createFromDataURL(icon.default),
|
||||||
|
@ -124,8 +124,8 @@ else {
|
||||||
if (isWindows)
|
if (isWindows)
|
||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
|
|
||||||
// if (isDevelopment)
|
if (isDevelopment)
|
||||||
// mainWindow.webContents.openDevTools();
|
mainWindow.webContents.openDevTools();
|
||||||
|
|
||||||
process.on('uncaughtException', error => {
|
process.on('uncaughtException', error => {
|
||||||
mainWindow.webContents.send('unhandled-exception', error);
|
mainWindow.webContents.send('unhandled-exception', error);
|
||||||
|
|
|
@ -1,179 +1,59 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="wrapper" :class="[`theme-${applicationTheme}`, !disableBlur || 'no-blur']">
|
<div id="wrapper">
|
||||||
<TheTitleBar />
|
<AppHeader
|
||||||
<div id="window-content">
|
ref="header"
|
||||||
<TheSettingBar @show-connections-modal="isAllConnectionsModal = true" />
|
:sel-tab="selTab"
|
||||||
<div id="main-content" class="container">
|
:client-status="clientStatus"
|
||||||
<div class="columns col-gapless">
|
:server-status="serverStatus"
|
||||||
<Workspace
|
@select-tab="selectTab"
|
||||||
v-for="connection in connections"
|
/>
|
||||||
:key="connection.uid"
|
<div id="main">
|
||||||
:connection="connection"
|
<Client
|
||||||
/>
|
v-show="selTab === 0"
|
||||||
<div class="connection-panel-wrapper p-relative">
|
ref="client"
|
||||||
<WorkspaceAddConnectionPanel v-if="selectedWorkspace === 'NEW'" />
|
@client-status="clientUpdateStatus"
|
||||||
</div>
|
/>
|
||||||
</div>
|
<Server
|
||||||
<TheFooter />
|
v-show="selTab === 1"
|
||||||
<TheNotificationsBoard />
|
ref="server"
|
||||||
<TheScratchpad v-if="isScratchpad" />
|
@server-status="serverUpdateStatus"
|
||||||
<ModalSettings v-if="isSettingModal" />
|
/>
|
||||||
<BaseTextEditor class="d-none" value="" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<ModalAllConnections v-if="isAllConnectionsModal" @close="isAllConnectionsModal = false" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineAsyncComponent, onMounted, Ref, ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
|
||||||
import { ipcRenderer } from 'electron';
|
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { Menu, getCurrentWindow } from '@electron/remote';
|
import AppHeader from '@/components/app-header.vue';
|
||||||
import { useApplicationStore } from '@/stores/application';
|
import Client from '@/components/client.vue';
|
||||||
import { useConnectionsStore } from '@/stores/connections';
|
import Server from '@/components/server.vue';
|
||||||
import { useSettingsStore } from '@/stores/settings';
|
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
|
||||||
import TheSettingBar from '@/components/TheSettingBar.vue';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const TheTitleBar = defineAsyncComponent(() => import(/* webpackChunkName: "TheTitleBar" */'@/components/TheTitleBar.vue'));
|
const selTab = ref(0);
|
||||||
const TheFooter = defineAsyncComponent(() => import(/* webpackChunkName: "TheFooter" */'@/components/TheFooter.vue'));
|
const clientStatus = ref(0);
|
||||||
const TheNotificationsBoard = defineAsyncComponent(() => import(/* webpackChunkName: "TheNotificationsBoard" */'@/components/TheNotificationsBoard.vue'));
|
const serverStatus = ref(0);
|
||||||
const Workspace = defineAsyncComponent(() => import(/* webpackChunkName: "Workspace" */'@/components/Workspace.vue'));
|
|
||||||
const WorkspaceAddConnectionPanel = defineAsyncComponent(() => import(/* webpackChunkName: "WorkspaceAddConnectionPanel" */'@/components/WorkspaceAddConnectionPanel.vue'));
|
|
||||||
const ModalSettings = defineAsyncComponent(() => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings.vue'));
|
|
||||||
const ModalAllConnections = defineAsyncComponent(() => import(/* webpackChunkName: "ModalAllConnections" */'@/components/ModalAllConnections.vue'));
|
|
||||||
const TheScratchpad = defineAsyncComponent(() => import(/* webpackChunkName: "TheScratchpad" */'@/components/TheScratchpad.vue'));
|
|
||||||
const BaseTextEditor = defineAsyncComponent(() => import(/* webpackChunkName: "BaseTextEditor" */'@/components/BaseTextEditor.vue'));
|
|
||||||
|
|
||||||
const applicationStore = useApplicationStore();
|
const selectTab = (value: number) => {
|
||||||
const connectionsStore = useConnectionsStore();
|
selTab.value = value;
|
||||||
const settingsStore = useSettingsStore();
|
};
|
||||||
const workspacesStore = useWorkspacesStore();
|
|
||||||
|
|
||||||
const {
|
const clientUpdateStatus = (value: number) => {
|
||||||
isSettingModal,
|
clientStatus.value = value;
|
||||||
isScratchpad
|
};
|
||||||
} = storeToRefs(applicationStore);
|
|
||||||
const { connections } = storeToRefs(connectionsStore);
|
|
||||||
const { applicationTheme, disableBlur } = storeToRefs(settingsStore);
|
|
||||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
|
||||||
|
|
||||||
const { checkVersionUpdate } = applicationStore;
|
const serverUpdateStatus = (value: number) => {
|
||||||
const { changeApplicationTheme } = settingsStore;
|
serverStatus.value = value;
|
||||||
|
};
|
||||||
|
|
||||||
const isAllConnectionsModal: Ref<boolean> = ref(false);
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
changeApplicationTheme(applicationTheme.value);// Forces persistentStore to save on file and mail process
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
ipcRenderer.on('open-all-connections', () => {
|
|
||||||
isAllConnectionsModal.value = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on('open-scratchpad', () => {
|
|
||||||
isScratchpad.value = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on('open-settings', () => {
|
|
||||||
isSettingModal.value = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on('create-connection', () => {
|
|
||||||
workspacesStore.selectWorkspace('NEW');
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.send('check-for-updates');
|
|
||||||
checkVersionUpdate();
|
|
||||||
|
|
||||||
const InputMenu = Menu.buildFromTemplate([
|
|
||||||
{
|
|
||||||
label: t('word.cut'),
|
|
||||||
role: 'cut'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('word.copy'),
|
|
||||||
role: 'copy'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('word.paste'),
|
|
||||||
role: 'paste'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'separator'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('message.selectAll'),
|
|
||||||
role: 'selectAll'
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
document.body.addEventListener('contextmenu', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
let node: any = e.target;
|
|
||||||
|
|
||||||
while (node) {
|
|
||||||
if (node.nodeName.match(/^(input|textarea)$/i) || node.isContentEditable) {
|
|
||||||
InputMenu.popup({ window: getCurrentWindow() });
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
node = node.parentNode;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('keydown', e => {
|
|
||||||
if (e.altKey && e.key === 'Alt') { // Prevent Alt key to trigger hidden shortcut menu
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
html,
|
.fade-enter-active, .fade-leave-active {
|
||||||
body {
|
transition: opacity .2s;
|
||||||
height: 100%;
|
}
|
||||||
}
|
.fade-enter, .fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
#wrapper {
|
}
|
||||||
height: 100vh;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#window-content {
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main-content {
|
|
||||||
padding: 0;
|
|
||||||
justify-content: flex-start;
|
|
||||||
height: calc(100vh - #{$excluding-size});
|
|
||||||
width: calc(100% - #{$settingbar-width});
|
|
||||||
|
|
||||||
> .columns {
|
|
||||||
height: calc(100vh - #{$footer-height});
|
|
||||||
}
|
|
||||||
|
|
||||||
.connection-panel-wrapper {
|
|
||||||
height: calc(100vh - #{$excluding-size});
|
|
||||||
width: 100%;
|
|
||||||
padding-top: 10vh;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: flex-start;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
<style>
|
|
||||||
.fade-enter-active, .fade-leave-active {
|
|
||||||
transition: opacity .2s;
|
|
||||||
}
|
|
||||||
.fade-enter, .fade-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div id="wrapper">
|
|
||||||
<AppHeader
|
|
||||||
ref="header"
|
|
||||||
:sel-tab="selTab"
|
|
||||||
:client-status="clientStatus"
|
|
||||||
:server-status="serverStatus"
|
|
||||||
@selectTab="selectTab"
|
|
||||||
/>
|
|
||||||
<div id="main">
|
|
||||||
<Client
|
|
||||||
v-show="selTab === 0"
|
|
||||||
ref="client"
|
|
||||||
@clientStatus="clientUpdateStatus"
|
|
||||||
/>
|
|
||||||
<Server
|
|
||||||
v-show="selTab === 1"
|
|
||||||
ref="server"
|
|
||||||
@serverStatus="serverUpdateStatus"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import AppHeader from './app-header.vue';
|
|
||||||
import Client from './client.vue';
|
|
||||||
import Server from './server.vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'Main',
|
|
||||||
components: {
|
|
||||||
AppHeader,
|
|
||||||
Client,
|
|
||||||
Server
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
selTab: 0,
|
|
||||||
clientStatus: 0,
|
|
||||||
serverStatus: 0
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
selectTab (value) {
|
|
||||||
this.selTab = value;
|
|
||||||
},
|
|
||||||
clientUpdateStatus (value) {
|
|
||||||
this.clientStatus = value;
|
|
||||||
},
|
|
||||||
serverUpdateStatus (value) {
|
|
||||||
this.serverStatus = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -1,30 +1,10 @@
|
||||||
import { createI18n } from 'vue-i18n';
|
import { createI18n } from 'vue-i18n';
|
||||||
import { enUS } from './en-US';
|
import { enUS } from './en-US';
|
||||||
import { itIT } from './it-IT';
|
import { itIT } from './it-IT';
|
||||||
import { arSA } from './ar-SA';
|
|
||||||
import { esES } from './es-ES';
|
|
||||||
import { frFR } from './fr-FR';
|
|
||||||
import { ptBR } from './pt-BR';
|
|
||||||
import { deDE } from './de-DE';
|
|
||||||
import { viVN } from './vi-VN';
|
|
||||||
import { jaJP } from './ja-JP';
|
|
||||||
import { zhCN } from './zh-CN';
|
|
||||||
import { ruRU } from './ru-RU';
|
|
||||||
import { idID } from './id-ID';
|
|
||||||
|
|
||||||
const messages = {
|
const messages = {
|
||||||
'en-US': enUS,
|
'en-US': enUS,
|
||||||
'it-IT': itIT,
|
'it-IT': itIT
|
||||||
'ar-SA': arSA,
|
|
||||||
'es-ES': esES,
|
|
||||||
'fr-FR': frFR,
|
|
||||||
'pt-BR': ptBR,
|
|
||||||
'de-DE': deDE,
|
|
||||||
'vi-VN': viVN,
|
|
||||||
'ja-JP': jaJP,
|
|
||||||
'zh-CN': zhCN,
|
|
||||||
'ru-RU': ruRU,
|
|
||||||
'id-ID': idID
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type NestedPartial<T> = {
|
type NestedPartial<T> = {
|
||||||
|
|
|
@ -1,14 +1,4 @@
|
||||||
export const localesNames: {[key: string]: string} = {
|
export const localesNames: {[key: string]: string} = {
|
||||||
'en-US': 'English',
|
'en-US': 'English',
|
||||||
'it-IT': 'Italiano',
|
'it-IT': 'Italiano'
|
||||||
'ar-SA': 'العربية',
|
|
||||||
'es-ES': 'Español',
|
|
||||||
'fr-FR': 'Français',
|
|
||||||
'pt-BR': 'Português (Brasil)',
|
|
||||||
'de-DE': 'Deutsch (Deutschland)',
|
|
||||||
'vi-VN': 'Tiếng Việt',
|
|
||||||
'ja-JP': '日本語',
|
|
||||||
'zh-CN': '简体中文',
|
|
||||||
'ru-RU': 'Русский',
|
|
||||||
'id-ID': 'Bahasa Indonesia'
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,31 +2,19 @@
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
import { createPinia } from 'pinia';
|
import { createPinia } from 'pinia';
|
||||||
import { VueMaskDirective } from 'v-mask';
|
|
||||||
import * as FloatingVue from 'floating-vue';
|
import * as FloatingVue from 'floating-vue';
|
||||||
import '@mdi/font/css/materialdesignicons.css';
|
import '@mdi/font/css/materialdesignicons.css';
|
||||||
import 'floating-vue/dist/style.css';
|
import 'floating-vue/dist/style.css';
|
||||||
import 'leaflet/dist/leaflet.css';
|
|
||||||
import '@/scss/main.scss';
|
import '@/scss/main.scss';
|
||||||
|
|
||||||
import { useApplicationStore } from '@/stores/application';
|
import { useApplicationStore } from '@/stores/application';
|
||||||
import { useSettingsStore } from '@/stores/settings';
|
import { useSettingsStore } from '@/stores/settings';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useConsoleStore } from '@/stores/console';
|
|
||||||
|
|
||||||
import App from '@/App.vue';
|
import App from '@/App.vue';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
|
|
||||||
// https://github.com/probil/v-mask/issues/498#issuecomment-827027834
|
|
||||||
const vMaskV2 = VueMaskDirective;
|
|
||||||
const vMaskV3 = {
|
|
||||||
beforeMount: vMaskV2.bind,
|
|
||||||
updated: vMaskV2.componentUpdated,
|
|
||||||
unmounted: vMaskV2.unbind
|
|
||||||
};
|
|
||||||
|
|
||||||
createApp(App)
|
createApp(App)
|
||||||
.directive('mask', vMaskV3)
|
|
||||||
.use(createPinia())
|
.use(createPinia())
|
||||||
.use(i18n)
|
.use(i18n)
|
||||||
.use(FloatingVue)
|
.use(FloatingVue)
|
||||||
|
@ -40,15 +28,6 @@ ipcRenderer.on('unhandled-exception', (event, error) => {
|
||||||
useNotificationsStore().addNotification({ status: 'error', message: error.message });
|
useNotificationsStore().addNotification({ status: 'error', message: error.message });
|
||||||
});
|
});
|
||||||
|
|
||||||
// IPC query logs
|
|
||||||
ipcRenderer.on('query-log', (event, logRecord) => {
|
|
||||||
useConsoleStore().putLog(logRecord);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on('toggle-console', () => {
|
|
||||||
useConsoleStore().toggleConsole();
|
|
||||||
});
|
|
||||||
|
|
||||||
// IPC app updates
|
// IPC app updates
|
||||||
ipcRenderer.on('checking-for-update', () => {
|
ipcRenderer.on('checking-for-update', () => {
|
||||||
useApplicationStore().updateStatus = 'checking';
|
useApplicationStore().updateStatus = 'checking';
|
||||||
|
@ -92,7 +71,3 @@ ipcRenderer.on('open-updates-preferences', () => {
|
||||||
useApplicationStore().showSettingModal('update');
|
useApplicationStore().showSettingModal('update');
|
||||||
ipcRenderer.send('check-for-updates');
|
ipcRenderer.send('check-for-updates');
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcRenderer.on('update-shortcuts', (event, shortcuts) => {
|
|
||||||
useSettingsStore().updateShortcuts(shortcuts);
|
|
||||||
});
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function uidGen (prefix?: string) {
|
||||||
|
return (prefix ? `${prefix}:` : '') + Math.random().toString(36).substring(2, 11).toUpperCase();
|
||||||
|
}
|
|
@ -1,425 +0,0 @@
|
||||||
/* stylelint-disable selector-class-pattern */
|
|
||||||
@import "~spectre.css/src/variables";
|
|
||||||
@import "variables";
|
|
||||||
@import "transitions";
|
|
||||||
@import "data-types";
|
|
||||||
@import "table-keys";
|
|
||||||
@import "fake-tables";
|
|
||||||
@import "mdi-additions";
|
|
||||||
@import "db-icons";
|
|
||||||
@import "themes/dark-theme";
|
|
||||||
@import "themes/light-theme";
|
|
||||||
@import "~spectre.css/src/spectre";
|
|
||||||
@import "~spectre.css/src/spectre-exp";
|
|
||||||
|
|
||||||
body {
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
::selection,
|
|
||||||
option:hover,
|
|
||||||
option:focus,
|
|
||||||
option:active,
|
|
||||||
option:checked {
|
|
||||||
background-color: $primary-color;
|
|
||||||
color: $light-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Additions */
|
|
||||||
@include margin-variant(3, $unit-3);
|
|
||||||
@include margin-variant(4, $unit-4);
|
|
||||||
@include padding-variant(3, $unit-3);
|
|
||||||
@include padding-variant(4, $unit-4);
|
|
||||||
|
|
||||||
.p-vcentered {
|
|
||||||
display: flex !important;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c-help {
|
|
||||||
cursor: help;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-outline {
|
|
||||||
outline: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-radius {
|
|
||||||
border-radius: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-border {
|
|
||||||
outline: none !important;
|
|
||||||
border: none !important;
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cut-text {
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cancellable {
|
|
||||||
color: transparent !important;
|
|
||||||
min-height: 0.8rem;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
> .mdi,
|
|
||||||
> .span {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: "\2715";
|
|
||||||
color: $light-color;
|
|
||||||
font-weight: 700;
|
|
||||||
top: 36%;
|
|
||||||
display: block;
|
|
||||||
height: 0.8rem;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -0.4rem;
|
|
||||||
margin-top: -0.4rem;
|
|
||||||
opacity: 1;
|
|
||||||
padding: 0;
|
|
||||||
position: absolute;
|
|
||||||
width: 0.8rem;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.workspace-query-results {
|
|
||||||
overflow: auto;
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
.table {
|
|
||||||
width: auto;
|
|
||||||
border-collapse: separate;
|
|
||||||
|
|
||||||
.th {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
border: 2px solid;
|
|
||||||
border-left: none;
|
|
||||||
border-bottom-width: 2px;
|
|
||||||
padding: 0;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
padding: 0.1rem 0.2rem;
|
|
||||||
/* stylelint-disable-next-line value-no-vendor-prefix */
|
|
||||||
min-width: -webkit-fill-available;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.td {
|
|
||||||
border-right: 2px solid;
|
|
||||||
border-bottom: 2px solid;
|
|
||||||
padding: 0 0.2rem;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
max-width: 200px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.workspace-tabs {
|
|
||||||
align-content: baseline;
|
|
||||||
|
|
||||||
.workspace-query-runner {
|
|
||||||
.workspace-query-runner-footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0.3rem 0.6rem 0.4rem;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.workspace-query-buttons {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
display: flex;
|
|
||||||
align-self: center;
|
|
||||||
margin-right: 0.4rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.workspace-query-info {
|
|
||||||
display: flex;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
> div + div {
|
|
||||||
padding-left: 0.6rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.process-row .td:last-child {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scrollbars */
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Animations */
|
|
||||||
@keyframes rotation {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
transform: rotate(359deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.rotate {
|
|
||||||
animation: rotation 0.8s infinite linear;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Override */
|
|
||||||
.modal {
|
|
||||||
.modal-container,
|
|
||||||
.modal-sm .modal-container {
|
|
||||||
padding: 0;
|
|
||||||
border-radius: $border-radius;
|
|
||||||
|
|
||||||
.modal-header {
|
|
||||||
padding: 0.4rem 0.8rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: $border-radius $border-radius 0 0;
|
|
||||||
|
|
||||||
.modal-title {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-overlay {
|
|
||||||
background: rgb(255 255 255 / 10%);
|
|
||||||
box-shadow: 0 8px 32px 0 rgb(31 38 135 / 37%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#wrapper:not(.no-blur) {
|
|
||||||
.modal-overlay {
|
|
||||||
backdrop-filter: blur(4px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab {
|
|
||||||
.tab-item {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.tab-link {
|
|
||||||
min-width: 0;
|
|
||||||
transition: color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
.tab-link {
|
|
||||||
border-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: "";
|
|
||||||
height: 2px;
|
|
||||||
width: 0;
|
|
||||||
transition: width 0.2s;
|
|
||||||
background-color: $primary-color;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-clear {
|
|
||||||
margin-top: -0.1rem;
|
|
||||||
font-size: 0.6rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip:hover {
|
|
||||||
&::after {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
&[data-badge],
|
|
||||||
&:not([data-badge]) {
|
|
||||||
&::after {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.badge-connected::after {
|
|
||||||
background: $success-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.badge-connecting::after {
|
|
||||||
background: $warning-color;
|
|
||||||
animation-name: pulse;
|
|
||||||
animation-duration: 2s;
|
|
||||||
animation-iteration-count: infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.badge-failed::after {
|
|
||||||
background: $error-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-select {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&.small-select {
|
|
||||||
height: 21px;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
padding: 1px 0.4rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.select {
|
|
||||||
&.select--open {
|
|
||||||
border-color: $primary-color !important;
|
|
||||||
|
|
||||||
@include control-shadow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.select__list {
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0.3rem 0.8rem;
|
|
||||||
|
|
||||||
.select-sm &,
|
|
||||||
.small-select & {
|
|
||||||
padding: 0.05rem 0.3rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.select__list-wrapper {
|
|
||||||
z-index: 401 !important;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
border-radius: $border-radius;
|
|
||||||
box-shadow: 0 8px 17px 0 rgb(0 0 0 / 20%), 0 6px 20px 0 rgb(0 0 0 / 19%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.select__option--selected {
|
|
||||||
background: rgba($primary-color, 0.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
.select__option--highlight {
|
|
||||||
background: $primary-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-input[type="file"] {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group .input-group-addon {
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu {
|
|
||||||
font-size: 0.7rem;
|
|
||||||
|
|
||||||
.menu-item {
|
|
||||||
+ .menu-item {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.accordion-body {
|
|
||||||
max-height: 5000rem !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
&:focus {
|
|
||||||
box-shadow: 0 0 3px 1px rgba($primary-color, 90%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.btn-success:focus {
|
|
||||||
border-color: $primary-color;
|
|
||||||
box-shadow: 0 0 3px 1px rgba($primary-color, 90%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-group {
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn.loading {
|
|
||||||
> .mdi,
|
|
||||||
> span {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
margin: 0.15rem 0.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-dropdown {
|
|
||||||
.menu {
|
|
||||||
min-width: 100%;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
.menu-item {
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
> a {
|
|
||||||
margin: 0.2rem;
|
|
||||||
padding: 0.1rem 0.3rem;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ace Editor */
|
|
||||||
.ace_editor {
|
|
||||||
&.ace_autocomplete {
|
|
||||||
border-radius: $border-radius;
|
|
||||||
|
|
||||||
.ace_marker-layer {
|
|
||||||
.ace_active-line,
|
|
||||||
.ace_line-hover {
|
|
||||||
border-radius: $border-radius;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import * as Store from 'electron-store';
|
import * as Store from 'electron-store';
|
||||||
import { Ace } from 'ace-builds';
|
|
||||||
const persistentStore = new Store({ name: 'settings' });
|
const persistentStore = new Store({ name: 'settings' });
|
||||||
|
|
||||||
export const useApplicationStore = defineStore('application', {
|
export const useApplicationStore = defineStore('application', {
|
||||||
|
@ -15,11 +14,9 @@ export const useApplicationStore = defineStore('application', {
|
||||||
selectedSettingTab: 'general',
|
selectedSettingTab: 'general',
|
||||||
selectedConection: {},
|
selectedConection: {},
|
||||||
updateStatus: 'noupdate', // 'noupdate' | 'available' | 'checking' | 'nocheck' | 'downloading' | 'downloaded' | 'disabled'
|
updateStatus: 'noupdate', // 'noupdate' | 'available' | 'checking' | 'nocheck' | 'downloading' | 'downloaded' | 'disabled'
|
||||||
downloadProgress: 0,
|
downloadProgress: 0
|
||||||
baseCompleter: [] as Ace.Completer[] // Needed to reset ace editor, due global-only ace completer
|
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
getBaseCompleter: state => state.baseCompleter,
|
|
||||||
getSelectedConnection: state => state.selectedConection,
|
getSelectedConnection: state => state.selectedConection,
|
||||||
getDownloadProgress: state => Number(state.downloadProgress.toFixed(1))
|
getDownloadProgress: state => Number(state.downloadProgress.toFixed(1))
|
||||||
},
|
},
|
||||||
|
@ -34,9 +31,6 @@ export const useApplicationStore = defineStore('application', {
|
||||||
setLoadingStatus (payload: boolean) {
|
setLoadingStatus (payload: boolean) {
|
||||||
this.isLoading = payload;
|
this.isLoading = payload;
|
||||||
},
|
},
|
||||||
setBaseCompleters (payload: Ace.Completer[]) {
|
|
||||||
this.baseCompleter = payload;
|
|
||||||
},
|
|
||||||
// Modals
|
// Modals
|
||||||
showNewConnModal () {
|
showNewConnModal () {
|
||||||
this.isNewModal = true;
|
this.isNewModal = true;
|
||||||
|
|
|
@ -1,224 +0,0 @@
|
||||||
import { defineStore } from 'pinia';
|
|
||||||
import * as Store from 'electron-store';
|
|
||||||
import * as crypto from 'crypto';
|
|
||||||
import { ConnectionParams } from 'common/interfaces/antares';
|
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
|
||||||
const key = localStorage.getItem('key');
|
|
||||||
|
|
||||||
export interface SidebarElement {
|
|
||||||
isFolder: boolean;
|
|
||||||
uid: string;
|
|
||||||
client?: string;
|
|
||||||
connections?: string[];
|
|
||||||
color?: string;
|
|
||||||
name?: string;
|
|
||||||
icon?: null | string;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!key)
|
|
||||||
localStorage.setItem('key', crypto.randomBytes(16).toString('hex'));
|
|
||||||
else
|
|
||||||
localStorage.setItem('key', key);
|
|
||||||
|
|
||||||
const persistentStore = new Store({
|
|
||||||
name: 'connections',
|
|
||||||
encryptionKey: key,
|
|
||||||
clearInvalidConfig: true
|
|
||||||
});
|
|
||||||
|
|
||||||
export const useConnectionsStore = defineStore('connections', {
|
|
||||||
state: () => ({
|
|
||||||
connections: persistentStore.get('connections', []) as ConnectionParams[],
|
|
||||||
lastConnections: persistentStore.get('lastConnections', []) as {uid: string; time: number}[],
|
|
||||||
connectionsOrder: persistentStore.get('connectionsOrder', []) as SidebarElement[]
|
|
||||||
}),
|
|
||||||
getters: {
|
|
||||||
getConnectionByUid: state => (uid:string) => state.connections.find(connection => connection.uid === uid),
|
|
||||||
getConnectionName: state => (uid: string) => {
|
|
||||||
const connection = state.connections.find(connection => connection.uid === uid);
|
|
||||||
let connectionName = '';
|
|
||||||
if (connection) {
|
|
||||||
if (connection.name)
|
|
||||||
connectionName = connection.name;
|
|
||||||
else if (connection.ask)
|
|
||||||
connectionName = `${connection.host}:${connection.port}`;
|
|
||||||
else if (connection.databasePath) {
|
|
||||||
let string = connection.databasePath.split(/[/\\]+/).pop();
|
|
||||||
|
|
||||||
if (string.length >= 30)
|
|
||||||
string = `...${string.slice(-30)}`;
|
|
||||||
|
|
||||||
connectionName = string;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
connectionName = `${connection.user + '@'}${connection.host}:${connection.port}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return connectionName;
|
|
||||||
},
|
|
||||||
getConnectionOrderByUid: state => (uid:string) => state.connectionsOrder
|
|
||||||
.find(connection => connection.uid === uid),
|
|
||||||
getFolders: state => state.connectionsOrder.filter(conn => conn.isFolder),
|
|
||||||
getConnectionFolder: state => (uid:string) => state.connectionsOrder
|
|
||||||
.find(folder => folder.isFolder && folder.connections.includes(uid))
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
addConnection (connection: ConnectionParams) {
|
|
||||||
this.connections.push(connection);
|
|
||||||
persistentStore.set('connections', this.connections);
|
|
||||||
|
|
||||||
this.connectionsOrder.push({
|
|
||||||
isFolder: false,
|
|
||||||
uid: connection.uid,
|
|
||||||
client: connection.client,
|
|
||||||
icon: null,
|
|
||||||
name: null
|
|
||||||
});
|
|
||||||
persistentStore.set('connectionsOrder', this.connectionsOrder);
|
|
||||||
},
|
|
||||||
addFolder (params: {after: string; connections: [string, string]}) {
|
|
||||||
const index = this.connectionsOrder.findIndex((conn: SidebarElement) => conn.uid === params.after);
|
|
||||||
|
|
||||||
this.connectionsOrder.splice(index, 0, {
|
|
||||||
isFolder: true,
|
|
||||||
uid: uidGen('F'),
|
|
||||||
name: '',
|
|
||||||
color: '#E36929',
|
|
||||||
connections: params.connections
|
|
||||||
});
|
|
||||||
persistentStore.set('connectionsOrder', this.connectionsOrder);
|
|
||||||
},
|
|
||||||
addToFolder (params: {folder: string; connection: string}) {
|
|
||||||
this.connectionsOrder = this.connectionsOrder.map((conn: SidebarElement) => {
|
|
||||||
if (conn.uid === params.folder)
|
|
||||||
conn.connections.push(params.connection);
|
|
||||||
|
|
||||||
return conn;
|
|
||||||
});
|
|
||||||
persistentStore.set('connectionsOrder', this.connectionsOrder);
|
|
||||||
this.clearEmptyFolders();
|
|
||||||
},
|
|
||||||
deleteConnection (connection: SidebarElement | ConnectionParams) {
|
|
||||||
this.connectionsOrder = (this.connectionsOrder as SidebarElement[]).map(el => { // Removes connection from folders
|
|
||||||
if (el.isFolder && el.connections.includes(connection.uid))
|
|
||||||
el.connections = el.connections.filter(uid => uid !== connection.uid);
|
|
||||||
return el;
|
|
||||||
});
|
|
||||||
this.connectionsOrder = (this.connectionsOrder as SidebarElement[]).filter(el => el.uid !== connection.uid);
|
|
||||||
|
|
||||||
this.connections = (this.connections as SidebarElement[]).filter(el => el.uid !== connection.uid);
|
|
||||||
persistentStore.set('connections', this.connections);
|
|
||||||
this.clearEmptyFolders();
|
|
||||||
},
|
|
||||||
editConnection (connection: ConnectionParams) {
|
|
||||||
const editedConnections = (this.connections as ConnectionParams[]).map(conn => {
|
|
||||||
if (conn.uid === connection.uid) return connection;
|
|
||||||
return conn;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.connections = editedConnections;
|
|
||||||
persistentStore.set('connections', this.connections);
|
|
||||||
|
|
||||||
const editedConnectionsOrder = (this.connectionsOrder as SidebarElement[]).map(conn => {
|
|
||||||
if (conn.uid === connection.uid) {
|
|
||||||
return {
|
|
||||||
isFolder: false,
|
|
||||||
uid: connection.uid,
|
|
||||||
client: connection.client,
|
|
||||||
icon: conn.icon,
|
|
||||||
name: conn.name
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return conn;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.connectionsOrder = editedConnectionsOrder;
|
|
||||||
persistentStore.set('connectionsOrder', this.connectionsOrder);
|
|
||||||
},
|
|
||||||
updateConnections (connections: ConnectionParams[]) {
|
|
||||||
this.connections = connections;
|
|
||||||
persistentStore.set('connections', this.connections);
|
|
||||||
},
|
|
||||||
initConnectionsOrder () {
|
|
||||||
this.connectionsOrder = (this.connections as ConnectionParams[]).map<SidebarElement>(conn => {
|
|
||||||
return {
|
|
||||||
isFolder: false,
|
|
||||||
uid: conn.uid,
|
|
||||||
client: conn.client,
|
|
||||||
icon: null,
|
|
||||||
name: null
|
|
||||||
};
|
|
||||||
});
|
|
||||||
persistentStore.set('connectionsOrder', this.connectionsOrder);
|
|
||||||
},
|
|
||||||
updateConnectionsOrder (connections: SidebarElement[]) {
|
|
||||||
const invalidElements = connections.reduce<{index: number; uid: string}[]>((acc, curr, i) => {
|
|
||||||
if (typeof curr === 'string')
|
|
||||||
acc.push({ index: i, uid: curr });
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (invalidElements.length) {
|
|
||||||
invalidElements.forEach(el => {
|
|
||||||
let connIndex = connections.findIndex(conn => conn.uid === el.uid);
|
|
||||||
const conn = connections[connIndex];
|
|
||||||
|
|
||||||
if (connIndex === -1) return;
|
|
||||||
|
|
||||||
connections.splice(el.index, 1, { // Move to new position
|
|
||||||
isFolder: false,
|
|
||||||
client: conn.client,
|
|
||||||
uid: conn.uid,
|
|
||||||
icon: conn.icon,
|
|
||||||
name: conn.name
|
|
||||||
});
|
|
||||||
|
|
||||||
connIndex = connections.findIndex((conn, i) => conn.uid === el.uid && i !== el.index);
|
|
||||||
connections.splice(connIndex, 1);// Delete old object
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear empty folders
|
|
||||||
const emptyFolders = connections.reduce<string[]>((acc, curr) => {
|
|
||||||
if (curr.connections && curr.connections.length === 0)
|
|
||||||
acc.push(curr.uid);
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
connections = connections.filter(el => !emptyFolders.includes(el.uid));
|
|
||||||
|
|
||||||
this.connectionsOrder = connections;
|
|
||||||
persistentStore.set('connectionsOrder', this.connectionsOrder);
|
|
||||||
},
|
|
||||||
updateConnectionOrder (element: SidebarElement) {
|
|
||||||
this.connectionsOrder = (this.connectionsOrder as SidebarElement[]).map(el => {
|
|
||||||
if (el.uid === element.uid)
|
|
||||||
el = element;
|
|
||||||
return el;
|
|
||||||
});
|
|
||||||
persistentStore.set('connectionsOrder', this.connectionsOrder);
|
|
||||||
},
|
|
||||||
updateLastConnection (uid: string) {
|
|
||||||
const cIndex = (this.lastConnections as {uid: string; time: number}[]).findIndex((c) => c.uid === uid);
|
|
||||||
|
|
||||||
if (cIndex >= 0)
|
|
||||||
this.lastConnections[cIndex].time = new Date().getTime();
|
|
||||||
else
|
|
||||||
this.lastConnections.push({ uid, time: new Date().getTime() });
|
|
||||||
|
|
||||||
persistentStore.set('lastConnections', this.lastConnections);
|
|
||||||
},
|
|
||||||
clearEmptyFolders () {
|
|
||||||
// Clear empty folders
|
|
||||||
const emptyFolders = (this.connectionsOrder as SidebarElement[]).reduce<string[]>((acc, curr) => {
|
|
||||||
if (curr.connections && curr.connections.length === 0)
|
|
||||||
acc.push(curr.uid);
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
this.connectionsOrder = (this.connectionsOrder as SidebarElement[]).filter(el => !emptyFolders.includes(el.uid));
|
|
||||||
persistentStore.set('connectionsOrder', this.connectionsOrder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,58 +0,0 @@
|
||||||
import { defineStore } from 'pinia';
|
|
||||||
import { useWorkspacesStore } from './workspaces';
|
|
||||||
const logsSize = 1000;
|
|
||||||
|
|
||||||
export interface ConsoleRecord {
|
|
||||||
cUid: string;
|
|
||||||
sql: string;
|
|
||||||
date: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useConsoleStore = defineStore('console', {
|
|
||||||
state: () => ({
|
|
||||||
records: [] as ConsoleRecord[],
|
|
||||||
consolesHeight: new Map<string, number>(),
|
|
||||||
consolesOpened: new Set([])
|
|
||||||
}),
|
|
||||||
getters: {
|
|
||||||
getLogsByWorkspace: state => (uid: string) => state.records.filter(r => r.cUid === uid),
|
|
||||||
isConsoleOpen: state => (uid: string) => state.consolesOpened.has(uid),
|
|
||||||
consoleHeight: state => {
|
|
||||||
const uid = useWorkspacesStore().getSelected;
|
|
||||||
return state.consolesHeight.get(uid) || 0;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
putLog (record: ConsoleRecord) {
|
|
||||||
this.records.push(record);
|
|
||||||
|
|
||||||
if (this.records.length > logsSize)
|
|
||||||
this.records = this.records.slice(0, logsSize);
|
|
||||||
},
|
|
||||||
openConsole () {
|
|
||||||
const uid = useWorkspacesStore().getSelected;
|
|
||||||
this.consolesOpened.add(uid);
|
|
||||||
this.consolesHeight.set(uid, 250);
|
|
||||||
},
|
|
||||||
closeConsole () {
|
|
||||||
const uid = useWorkspacesStore().getSelected;
|
|
||||||
this.consolesOpened.delete(uid);
|
|
||||||
this.consolesHeight.set(uid, 0);
|
|
||||||
},
|
|
||||||
resizeConsole (height: number) {
|
|
||||||
const uid = useWorkspacesStore().getSelected;
|
|
||||||
if (height < 30)
|
|
||||||
this.closeConsole();
|
|
||||||
else
|
|
||||||
this.consolesHeight.set(uid, height);
|
|
||||||
},
|
|
||||||
toggleConsole () {
|
|
||||||
const uid = useWorkspacesStore().getSelected;
|
|
||||||
|
|
||||||
if (this.consolesOpened.has(uid))
|
|
||||||
this.closeConsole();
|
|
||||||
else
|
|
||||||
this.openConsole();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,52 +0,0 @@
|
||||||
import { defineStore } from 'pinia';
|
|
||||||
import * as Store from 'electron-store';
|
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
|
||||||
const persistentStore = new Store({ name: 'history' });
|
|
||||||
const historySize = 1000;
|
|
||||||
|
|
||||||
export interface HistoryRecord {
|
|
||||||
uid: string;
|
|
||||||
sql: string;
|
|
||||||
date: Date;
|
|
||||||
schema?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useHistoryStore = defineStore('history', {
|
|
||||||
state: () => ({
|
|
||||||
history: persistentStore.get('history', {}) as {[key: string]: HistoryRecord[]},
|
|
||||||
favorites: persistentStore.get('favorites', {})
|
|
||||||
}),
|
|
||||||
getters: {
|
|
||||||
getHistoryByWorkspace: state => (uid: string) => state.history[uid]
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
saveHistory (args: { uid: string; query: string; schema: string; tabUid: string }) {
|
|
||||||
if (this.getHistoryByWorkspace(args.uid) &&
|
|
||||||
this.getHistoryByWorkspace(args.uid).length &&
|
|
||||||
this.getHistoryByWorkspace(args.uid)[0].sql === args.query
|
|
||||||
) return;
|
|
||||||
|
|
||||||
if (!(args.uid in this.history))
|
|
||||||
this.history[args.uid] = [];
|
|
||||||
|
|
||||||
this.history[args.uid] = [
|
|
||||||
{
|
|
||||||
uid: uidGen('H'),
|
|
||||||
sql: args.query,
|
|
||||||
date: new Date(),
|
|
||||||
schema: args.schema
|
|
||||||
},
|
|
||||||
...this.history[args.uid]
|
|
||||||
];
|
|
||||||
|
|
||||||
if (this.history[args.uid].length > historySize)
|
|
||||||
this.history[args.uid] = this.history[args.uid].slice(0, historySize);
|
|
||||||
|
|
||||||
persistentStore.set('history', this.history);
|
|
||||||
},
|
|
||||||
deleteQueryFromHistory (query: Partial<HistoryRecord> & { workspace: string}) {
|
|
||||||
this.history[query.workspace] = (this.history[query.workspace] as HistoryRecord[]).filter(q => q.uid !== query.uid);
|
|
||||||
persistentStore.set('history', this.history);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from '../libs/uidGen';
|
||||||
|
|
||||||
export interface Notification {
|
export interface Notification {
|
||||||
uid: string;
|
uid: string;
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
import { defineStore } from 'pinia';
|
|
||||||
import * as Store from 'electron-store';
|
|
||||||
const persistentStore = new Store({ name: 'notes' });
|
|
||||||
|
|
||||||
export const useScratchpadStore = defineStore('scratchpad', {
|
|
||||||
state: () => ({
|
|
||||||
notes: persistentStore.get('notes', '# HOW TO SUPPORT ANTARES\n\n- [ ] Leave a star to Antares [GitHub repo](https://github.com/antares-sql/antares)\n- [ ] Send feedbacks and advices\n- [ ] Report for bugs\n- [ ] If you enjoy, share Antares with friends\n\n# ABOUT SCRATCHPAD\n\nThis is a scratchpad where you can save your **personal notes**. It supports `markdown` format, but you are free to use plain text.\nThis content is just a placeholder, feel free to clear it to make space for your notes.\n') as string
|
|
||||||
}),
|
|
||||||
actions: {
|
|
||||||
changeNotes (notes: string) {
|
|
||||||
this.notes = notes;
|
|
||||||
persistentStore.set('notes', this.notes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -2,10 +2,8 @@ import { defineStore } from 'pinia';
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import { i18n, AvailableLocale } from '@/i18n';
|
import { i18n, AvailableLocale } from '@/i18n';
|
||||||
import * as Store from 'electron-store';
|
import * as Store from 'electron-store';
|
||||||
import { ShortcutRecord } from 'common/shortcuts';
|
|
||||||
|
|
||||||
const settingsStore = new Store({ name: 'settings' });
|
const settingsStore = new Store({ name: 'settings' });
|
||||||
const shortcutsStore = new Store({ name: 'shortcuts' });
|
|
||||||
const isDarkTheme = window.matchMedia('(prefers-color-scheme: dark)');
|
const isDarkTheme = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
const defaultAppTheme = isDarkTheme.matches ? 'dark' : 'light';
|
const defaultAppTheme = isDarkTheme.matches ? 'dark' : 'light';
|
||||||
const defaultEditorTheme = isDarkTheme.matches ? 'twilight' : 'sqlserver';
|
const defaultEditorTheme = isDarkTheme.matches ? 'twilight' : 'sqlserver';
|
||||||
|
@ -30,7 +28,6 @@ export const useSettingsStore = defineStore('settings', {
|
||||||
restoreTabs: settingsStore.get('restore_tabs', true) as boolean,
|
restoreTabs: settingsStore.get('restore_tabs', true) as boolean,
|
||||||
disableBlur: settingsStore.get('disable_blur', false) as boolean,
|
disableBlur: settingsStore.get('disable_blur', false) as boolean,
|
||||||
disableScratchpad: settingsStore.get('disable_scratchpad', false) as boolean,
|
disableScratchpad: settingsStore.get('disable_scratchpad', false) as boolean,
|
||||||
shortcuts: shortcutsStore.get('shortcuts', []) as ShortcutRecord[],
|
|
||||||
defaultCopyType: settingsStore.get('default_copy_type', 'cell') as string
|
defaultCopyType: settingsStore.get('default_copy_type', 'cell') as string
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
|
@ -96,9 +93,6 @@ export const useSettingsStore = defineStore('settings', {
|
||||||
this.disableScratchpad = val;
|
this.disableScratchpad = val;
|
||||||
settingsStore.set('disable_scratchpad', this.disableScratchpad);
|
settingsStore.set('disable_scratchpad', this.disableScratchpad);
|
||||||
},
|
},
|
||||||
updateShortcuts (shortcuts: ShortcutRecord[]) {
|
|
||||||
this.shortcuts = shortcuts;
|
|
||||||
},
|
|
||||||
changeDefaultCopyType (type: string) {
|
changeDefaultCopyType (type: string) {
|
||||||
this.defaultCopyType = type;
|
this.defaultCopyType = type;
|
||||||
settingsStore.set('default_copy_type', this.defaultCopyType);
|
settingsStore.set('default_copy_type', this.defaultCopyType);
|
||||||
|
|
|
@ -1,745 +0,0 @@
|
||||||
import { defineStore } from 'pinia';
|
|
||||||
import * as Store from 'electron-store';
|
|
||||||
import Connection from '@/ipc-api/Connection';
|
|
||||||
import Schema from '@/ipc-api/Schema';
|
|
||||||
import Users from '@/ipc-api/Users';
|
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
|
||||||
|
|
||||||
import customizations from 'common/customizations';
|
|
||||||
|
|
||||||
import { useConnectionsStore } from '@/stores/connections';
|
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
|
||||||
import { useSettingsStore } from '@/stores/settings';
|
|
||||||
import {
|
|
||||||
ClientCode,
|
|
||||||
CollationInfos,
|
|
||||||
ConnectionParams,
|
|
||||||
EventInfos,
|
|
||||||
FunctionInfos,
|
|
||||||
RoutineInfos,
|
|
||||||
TableInfos,
|
|
||||||
TriggerFunctionInfos,
|
|
||||||
TriggerInfos,
|
|
||||||
TypesGroup
|
|
||||||
} from 'common/interfaces/antares';
|
|
||||||
import { Customizations } from 'common/interfaces/customizations';
|
|
||||||
|
|
||||||
export interface WorkspaceTab {
|
|
||||||
uid: string;
|
|
||||||
tab?: string;
|
|
||||||
index?: number;
|
|
||||||
selected?: boolean;
|
|
||||||
type?: string;
|
|
||||||
schema?: string;
|
|
||||||
elementName?: string;
|
|
||||||
elementNewName?: string;
|
|
||||||
elementType?: string;
|
|
||||||
isChanged?: boolean;
|
|
||||||
content?: string;
|
|
||||||
autorun?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WorkspaceStructure {
|
|
||||||
name: string;
|
|
||||||
functions: FunctionInfos[];
|
|
||||||
procedures: RoutineInfos[];
|
|
||||||
schedulers: EventInfos[];
|
|
||||||
tables: TableInfos[];
|
|
||||||
triggers: TriggerInfos[];
|
|
||||||
triggerFunctions: TriggerFunctionInfos[];
|
|
||||||
size: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Breadcrumb {
|
|
||||||
function?: string;
|
|
||||||
routine?: string;
|
|
||||||
query?: string;
|
|
||||||
scheduler?: string;
|
|
||||||
schema?: string;
|
|
||||||
table?: string;
|
|
||||||
trigger?: string;
|
|
||||||
triggerFunction?: string;
|
|
||||||
view?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Workspace {
|
|
||||||
uid: string;
|
|
||||||
client?: ClientCode;
|
|
||||||
connectionStatus: string;
|
|
||||||
selectedTab: string | number;
|
|
||||||
searchTerm: string;
|
|
||||||
tabs: WorkspaceTab[];
|
|
||||||
structure: WorkspaceStructure[];
|
|
||||||
variables: { name: string; value: string }[];
|
|
||||||
collations: CollationInfos[];
|
|
||||||
users: { host: string; name: string; password?: string }[];
|
|
||||||
breadcrumbs: Breadcrumb;
|
|
||||||
loadingElements: { name: string; schema: string; type: string }[];
|
|
||||||
loadedSchemas: Set<string>;
|
|
||||||
dataTypes?: { [key: string]: TypesGroup[] };
|
|
||||||
indexTypes?: string[];
|
|
||||||
customizations?: Customizations;
|
|
||||||
version?: {
|
|
||||||
number: string;
|
|
||||||
name: string;
|
|
||||||
arch: string;
|
|
||||||
os: string;
|
|
||||||
};
|
|
||||||
engines?: {[key: string]: string | boolean | number}[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const persistentStore = new Store({ name: 'tabs' });
|
|
||||||
const tabIndex: {[key: string]: number} = {};
|
|
||||||
|
|
||||||
export const useWorkspacesStore = defineStore('workspaces', {
|
|
||||||
state: () => ({
|
|
||||||
workspaces: [] as Workspace[],
|
|
||||||
selectedWorkspace: null as string
|
|
||||||
}),
|
|
||||||
getters: {
|
|
||||||
getSelected: state => {
|
|
||||||
if (!state.workspaces.length) return 'NEW';
|
|
||||||
if (state.selectedWorkspace) return state.selectedWorkspace;
|
|
||||||
const connectionsStore = useConnectionsStore();
|
|
||||||
if (connectionsStore.lastConnections.length) {
|
|
||||||
return connectionsStore.lastConnections.sort((a, b) => {
|
|
||||||
if (a.time < b.time) return 1;
|
|
||||||
else if (a.time > b.time) return -1;
|
|
||||||
return 0;
|
|
||||||
})[0].uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state.workspaces[0].uid;
|
|
||||||
},
|
|
||||||
getWorkspace: state => (uid: string) => {
|
|
||||||
return state.workspaces.find(workspace => workspace.uid === uid);
|
|
||||||
},
|
|
||||||
getDatabaseVariable: state => (uid: string, name: string) => {
|
|
||||||
return state.workspaces.find(workspace => workspace.uid === uid).variables.find(variable => variable.name === name);
|
|
||||||
},
|
|
||||||
getWorkspaceTab (state) {
|
|
||||||
return (tUid: string) => {
|
|
||||||
if (!this.getSelected) return;
|
|
||||||
const workspace = state.workspaces.find(workspace => workspace.uid === this.getSelected);
|
|
||||||
if ('tabs' in workspace)
|
|
||||||
return workspace.tabs.find(tab => tab.uid === tUid);
|
|
||||||
return {};
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getConnected: state => {
|
|
||||||
return state.workspaces
|
|
||||||
.filter(workspace => workspace.connectionStatus === 'connected')
|
|
||||||
.map(workspace => workspace.uid);
|
|
||||||
},
|
|
||||||
getLoadedSchemas: state => (uid: string) => {
|
|
||||||
return state.workspaces.find(workspace => workspace.uid === uid).loadedSchemas;
|
|
||||||
},
|
|
||||||
getSearchTerm: state => (uid: string) => {
|
|
||||||
return state.workspaces.find(workspace => workspace.uid === uid).searchTerm;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
selectWorkspace (uid: string) {
|
|
||||||
if (!uid)
|
|
||||||
this.selectedWorkspace = this.workspaces.length ? this.workspaces[0].uid : 'NEW';
|
|
||||||
else
|
|
||||||
this.selectedWorkspace = uid;
|
|
||||||
},
|
|
||||||
async connectWorkspace (connection: ConnectionParams & { pgConnString?: string }) {
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid
|
|
||||||
? {
|
|
||||||
...workspace,
|
|
||||||
structure: {},
|
|
||||||
breadcrumbs: {},
|
|
||||||
loadedSchemas: new Set(),
|
|
||||||
connectionStatus: 'connecting'
|
|
||||||
}
|
|
||||||
: workspace);
|
|
||||||
|
|
||||||
const connectionsStore = useConnectionsStore();
|
|
||||||
const notificationsStore = useNotificationsStore();
|
|
||||||
const settingsStore = useSettingsStore();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { status, response } = await Connection.connect(connection);
|
|
||||||
|
|
||||||
if (status === 'error') {
|
|
||||||
notificationsStore.addNotification({ status, message: response });
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid
|
|
||||||
? {
|
|
||||||
...workspace,
|
|
||||||
structure: {},
|
|
||||||
breadcrumbs: {},
|
|
||||||
loadedSchemas: new Set(),
|
|
||||||
connectionStatus: 'failed'
|
|
||||||
}
|
|
||||||
: workspace);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let clientCustomizations: Customizations;
|
|
||||||
const { updateLastConnection } = connectionsStore;
|
|
||||||
|
|
||||||
updateLastConnection(connection.uid);
|
|
||||||
|
|
||||||
switch (connection.client) {
|
|
||||||
case 'mysql':
|
|
||||||
case 'maria':
|
|
||||||
clientCustomizations = customizations.mysql;
|
|
||||||
break;
|
|
||||||
case 'pg':
|
|
||||||
clientCustomizations = customizations.pg;
|
|
||||||
break;
|
|
||||||
case 'sqlite':
|
|
||||||
clientCustomizations = customizations.sqlite;
|
|
||||||
break;
|
|
||||||
case 'firebird':
|
|
||||||
clientCustomizations = customizations.firebird;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const dataTypes = clientCustomizations.dataTypes;
|
|
||||||
const indexTypes = clientCustomizations.indexTypes;
|
|
||||||
|
|
||||||
const { status, response: version } = await Schema.getVersion(connection.uid);
|
|
||||||
|
|
||||||
if (status === 'error')
|
|
||||||
notificationsStore.addNotification({ status, message: version });
|
|
||||||
|
|
||||||
// Check if Maria or MySQL
|
|
||||||
const isMySQL = version.name.includes('MySQL');
|
|
||||||
const isMaria = version.name.includes('Maria');
|
|
||||||
|
|
||||||
if (isMySQL && connection.client !== 'mysql') {
|
|
||||||
const connProxy = Object.assign({}, connection);
|
|
||||||
connProxy.client = 'mysql';
|
|
||||||
connectionsStore.editConnection(connProxy);
|
|
||||||
}
|
|
||||||
else if (isMaria && connection.client === 'mysql') {
|
|
||||||
const connProxy = Object.assign({}, connection);
|
|
||||||
connProxy.client = 'maria';
|
|
||||||
connectionsStore.editConnection(connProxy);
|
|
||||||
}
|
|
||||||
|
|
||||||
const cachedTabs: WorkspaceTab[] = settingsStore.restoreTabs ? persistentStore.get(connection.uid, []) as WorkspaceTab[] : [];
|
|
||||||
|
|
||||||
if (cachedTabs.length) {
|
|
||||||
tabIndex[connection.uid] = cachedTabs.reduce((acc: number, curr) => {
|
|
||||||
if (curr.index > acc) acc = curr.index;
|
|
||||||
return acc;
|
|
||||||
}, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid
|
|
||||||
? {
|
|
||||||
...workspace,
|
|
||||||
client: connection.client,
|
|
||||||
dataTypes,
|
|
||||||
indexTypes,
|
|
||||||
customizations: clientCustomizations,
|
|
||||||
structure: response,
|
|
||||||
connectionStatus: 'connected',
|
|
||||||
tabs: cachedTabs,
|
|
||||||
selectedTab: cachedTabs.length ? cachedTabs[0].uid : null,
|
|
||||||
version
|
|
||||||
}
|
|
||||||
: workspace);
|
|
||||||
|
|
||||||
this.refreshCollations(connection.uid);
|
|
||||||
this.refreshVariables(connection.uid);
|
|
||||||
this.refreshEngines(connection.uid);
|
|
||||||
this.refreshUsers(connection.uid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
notificationsStore.addNotification({ status: 'error', message: err.stack });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async refreshStructure (uid: string) {
|
|
||||||
const notificationsStore = useNotificationsStore();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { status, response } = await Schema.getStructure({ uid, schemas: this.getLoadedSchemas(uid) });
|
|
||||||
|
|
||||||
if (status === 'error')
|
|
||||||
notificationsStore.addNotification({ status, message: response });
|
|
||||||
else {
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === uid
|
|
||||||
? {
|
|
||||||
...workspace,
|
|
||||||
structure: response
|
|
||||||
}
|
|
||||||
: workspace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
notificationsStore.addNotification({ status: 'error', message: err.stack });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async refreshSchema ({ uid, schema }: {uid: string; schema: string}) {
|
|
||||||
const notificationsStore = useNotificationsStore();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { status, response } = await Schema.getStructure({ uid, schemas: new Set([schema]) });
|
|
||||||
if (status === 'error')
|
|
||||||
notificationsStore.addNotification({ status, message: response });
|
|
||||||
else {
|
|
||||||
const schemaElements = (response as WorkspaceStructure[]).find(_schema => _schema.name === schema);
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => {
|
|
||||||
if (workspace.uid === uid) {
|
|
||||||
const schemaIndex = workspace.structure.findIndex(s => s.name === schema);
|
|
||||||
|
|
||||||
if (schemaIndex !== -1)
|
|
||||||
workspace.structure[schemaIndex] = schemaElements;
|
|
||||||
else
|
|
||||||
workspace.structure.push(schemaElements);
|
|
||||||
}
|
|
||||||
return workspace;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
notificationsStore.addNotification({ status: 'error', message: err.stack });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async refreshCollations (uid: string) {
|
|
||||||
const notificationsStore = useNotificationsStore();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { status, response } = await Schema.getCollations(uid);
|
|
||||||
if (status === 'error')
|
|
||||||
notificationsStore.addNotification({ status, message: response });
|
|
||||||
else {
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === uid
|
|
||||||
? {
|
|
||||||
...workspace,
|
|
||||||
collations: response
|
|
||||||
}
|
|
||||||
: workspace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
notificationsStore.addNotification({ status: 'error', message: err.stack });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async refreshVariables (uid: string) {
|
|
||||||
const notificationsStore = useNotificationsStore();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { status, response } = await Schema.getVariables(uid);
|
|
||||||
if (status === 'error')
|
|
||||||
notificationsStore.addNotification({ status, message: response });
|
|
||||||
else {
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === uid
|
|
||||||
? {
|
|
||||||
...workspace,
|
|
||||||
variables: response
|
|
||||||
}
|
|
||||||
: workspace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
notificationsStore.addNotification({ status: 'error', message: err.stack });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async refreshEngines (uid: string) {
|
|
||||||
const notificationsStore = useNotificationsStore();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { status, response } = await Schema.getEngines(uid);
|
|
||||||
if (status === 'error')
|
|
||||||
notificationsStore.addNotification({ status, message: response });
|
|
||||||
else {
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === uid
|
|
||||||
? {
|
|
||||||
...workspace,
|
|
||||||
engines: response
|
|
||||||
}
|
|
||||||
: workspace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
notificationsStore.addNotification({ status: 'error', message: err.stack });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async refreshUsers (uid: string) {
|
|
||||||
const notificationsStore = useNotificationsStore();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { status, response } = await Users.getUsers(uid);
|
|
||||||
if (status === 'error')
|
|
||||||
notificationsStore.addNotification({ status, message: response });
|
|
||||||
else {
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === uid
|
|
||||||
? {
|
|
||||||
...workspace,
|
|
||||||
users: response
|
|
||||||
}
|
|
||||||
: workspace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
notificationsStore.addNotification({ status: 'error', message: err.stack });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
removeConnected (uid: string) {
|
|
||||||
Connection.disconnect(uid);
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === uid
|
|
||||||
? {
|
|
||||||
...workspace,
|
|
||||||
structure: {},
|
|
||||||
breadcrumbs: {},
|
|
||||||
loadedSchemas: new Set(),
|
|
||||||
connectionStatus: 'disconnected'
|
|
||||||
}
|
|
||||||
: workspace);
|
|
||||||
|
|
||||||
this.selectTab({ uid, tab: 0 });
|
|
||||||
},
|
|
||||||
addWorkspace (uid: string) {
|
|
||||||
const workspace: Workspace = {
|
|
||||||
uid,
|
|
||||||
connectionStatus: 'disconnected',
|
|
||||||
selectedTab: 0,
|
|
||||||
searchTerm: '',
|
|
||||||
tabs: [],
|
|
||||||
structure: [],
|
|
||||||
variables: [],
|
|
||||||
collations: [],
|
|
||||||
users: [],
|
|
||||||
breadcrumbs: {},
|
|
||||||
loadingElements: [],
|
|
||||||
loadedSchemas: new Set()
|
|
||||||
};
|
|
||||||
|
|
||||||
this.workspaces.push(workspace);
|
|
||||||
},
|
|
||||||
changeBreadcrumbs (payload: Breadcrumb) {
|
|
||||||
const breadcrumbsObj: Breadcrumb = {
|
|
||||||
schema: null,
|
|
||||||
table: null,
|
|
||||||
trigger: null,
|
|
||||||
triggerFunction: null,
|
|
||||||
routine: null,
|
|
||||||
function: null,
|
|
||||||
scheduler: null,
|
|
||||||
view: null,
|
|
||||||
query: null
|
|
||||||
};
|
|
||||||
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === this.getSelected
|
|
||||||
? {
|
|
||||||
...workspace,
|
|
||||||
breadcrumbs: { ...breadcrumbsObj, ...payload }
|
|
||||||
}
|
|
||||||
: workspace);
|
|
||||||
},
|
|
||||||
addLoadedSchema (schema: string) {
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => {
|
|
||||||
if (workspace.uid === this.getSelected)
|
|
||||||
workspace.loadedSchemas.add(schema);
|
|
||||||
return workspace;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
addLoadingElement (element: { name: string; schema: string; type: string }) {
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => {
|
|
||||||
if (workspace.uid === this.getSelected)
|
|
||||||
workspace.loadingElements.push(element);
|
|
||||||
return workspace;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
removeLoadingElement (element: { name: string; schema: string; type: string }) {
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => {
|
|
||||||
if (workspace.uid === this.getSelected) {
|
|
||||||
const loadingElements = workspace.loadingElements.filter(el =>
|
|
||||||
el.schema !== element.schema &&
|
|
||||||
el.name !== element.name &&
|
|
||||||
el.type !== element.type
|
|
||||||
);
|
|
||||||
|
|
||||||
workspace = { ...workspace, loadingElements };
|
|
||||||
}
|
|
||||||
return workspace;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
setSearchTerm (term: string) {
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === this.getSelected
|
|
||||||
? {
|
|
||||||
...workspace,
|
|
||||||
searchTerm: term
|
|
||||||
}
|
|
||||||
: workspace);
|
|
||||||
},
|
|
||||||
_addTab ({ uid, tab, content, type, autorun, schema, elementName, elementType }: WorkspaceTab) {
|
|
||||||
if (type === 'query')
|
|
||||||
tabIndex[uid] = tabIndex[uid] ? ++tabIndex[uid] : 1;
|
|
||||||
|
|
||||||
const newTab: WorkspaceTab = {
|
|
||||||
uid: tab,
|
|
||||||
index: type === 'query' ? tabIndex[uid] : null,
|
|
||||||
selected: false,
|
|
||||||
type,
|
|
||||||
schema,
|
|
||||||
elementName,
|
|
||||||
elementType,
|
|
||||||
content: content || '',
|
|
||||||
autorun: !!autorun
|
|
||||||
};
|
|
||||||
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => {
|
|
||||||
if (workspace.uid === uid) {
|
|
||||||
return {
|
|
||||||
...workspace,
|
|
||||||
tabs: [...workspace.tabs, newTab]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return workspace;
|
|
||||||
});
|
|
||||||
|
|
||||||
persistentStore.set(uid, (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid).tabs);
|
|
||||||
},
|
|
||||||
_replaceTab ({ uid, tab: tUid, type, schema, content, elementName, elementType }: WorkspaceTab) {
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => {
|
|
||||||
if (workspace.uid === uid) {
|
|
||||||
return {
|
|
||||||
...workspace,
|
|
||||||
tabs: workspace.tabs.map(tab => {
|
|
||||||
if (tab.uid === tUid)
|
|
||||||
return { ...tab, type, schema, content, elementName, elementType };
|
|
||||||
|
|
||||||
return tab;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return workspace;
|
|
||||||
});
|
|
||||||
|
|
||||||
persistentStore.set(uid, (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid).tabs);
|
|
||||||
},
|
|
||||||
newTab ({ uid, content, type, autorun, schema, elementName, elementType }: WorkspaceTab) {
|
|
||||||
let tabUid;
|
|
||||||
const workspaceTabs = (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 'new-table':
|
|
||||||
case 'new-trigger':
|
|
||||||
case 'new-trigger-function':
|
|
||||||
case 'new-function':
|
|
||||||
case 'new-routine':
|
|
||||||
case 'new-scheduler':
|
|
||||||
tabUid = uidGen('T');
|
|
||||||
this._addTab({
|
|
||||||
uid,
|
|
||||||
tab: tabUid,
|
|
||||||
content,
|
|
||||||
type,
|
|
||||||
autorun,
|
|
||||||
schema,
|
|
||||||
elementName,
|
|
||||||
elementType
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'temp-data':
|
|
||||||
case 'temp-trigger-props':
|
|
||||||
case 'temp-trigger-function-props':
|
|
||||||
case 'temp-function-props':
|
|
||||||
case 'temp-routine-props':
|
|
||||||
case 'temp-scheduler-props': {
|
|
||||||
const existentTab = workspaceTabs
|
|
||||||
? workspaceTabs.tabs.find(tab =>
|
|
||||||
tab.schema === schema &&
|
|
||||||
tab.elementName === elementName &&
|
|
||||||
tab.elementType === elementType &&
|
|
||||||
[type, type.replace('temp-', '')].includes(tab.type))
|
|
||||||
: false;
|
|
||||||
|
|
||||||
if (existentTab) { // if tab exists
|
|
||||||
tabUid = existentTab.uid;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const tempTabs = workspaceTabs ? workspaceTabs.tabs.filter(tab => tab.type.includes('temp-')) : false;
|
|
||||||
|
|
||||||
if (tempTabs && tempTabs.length) { // if temp tab already opened
|
|
||||||
for (const tab of tempTabs) {
|
|
||||||
if (tab.isChanged) {
|
|
||||||
this._replaceTab({ // make permanent a temp table with unsaved changes
|
|
||||||
uid,
|
|
||||||
tab: tab.uid,
|
|
||||||
type: tab.type.replace('temp-', ''),
|
|
||||||
schema: tab.schema,
|
|
||||||
elementName: tab.elementName,
|
|
||||||
elementType: tab.elementType
|
|
||||||
});
|
|
||||||
|
|
||||||
tabUid = uidGen('T');
|
|
||||||
this._addTab({ uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this._replaceTab({ uid, tab: tab.uid, type, schema, elementName, elementType });
|
|
||||||
tabUid = tab.uid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
tabUid = uidGen('T');
|
|
||||||
this._addTab({ uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'data':
|
|
||||||
case 'table-props':
|
|
||||||
case 'trigger-props':
|
|
||||||
case 'trigger-function-props':
|
|
||||||
case 'function-props':
|
|
||||||
case 'routine-props':
|
|
||||||
case 'scheduler-props': {
|
|
||||||
const existentTab = workspaceTabs
|
|
||||||
? workspaceTabs.tabs.find(tab =>
|
|
||||||
tab.schema === schema &&
|
|
||||||
tab.elementName === elementName &&
|
|
||||||
tab.elementType === elementType &&
|
|
||||||
[`temp-${type}`, type].includes(tab.type))
|
|
||||||
: false;
|
|
||||||
|
|
||||||
if (existentTab) {
|
|
||||||
this._replaceTab({ uid, tab: existentTab.uid, type, schema, elementName, elementType });
|
|
||||||
tabUid = existentTab.uid;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
tabUid = uidGen('T');
|
|
||||||
this._addTab({ uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
tabUid = uidGen('T');
|
|
||||||
this._addTab({ uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectTab({ uid, tab: tabUid });
|
|
||||||
},
|
|
||||||
checkSelectedTabExists (uid: string) {
|
|
||||||
const workspace = (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid);
|
|
||||||
const isSelectedExistent = workspace
|
|
||||||
? workspace.tabs.some(tab => tab.uid === workspace.selectedTab)
|
|
||||||
: false;
|
|
||||||
|
|
||||||
if (!isSelectedExistent && workspace.tabs.length)
|
|
||||||
this.selectTab({ uid, tab: workspace.tabs[workspace.tabs.length - 1].uid });
|
|
||||||
},
|
|
||||||
updateTabContent ({ uid, tab, type, schema, content }: WorkspaceTab) {
|
|
||||||
this._replaceTab({ uid, tab, type, schema, content });
|
|
||||||
},
|
|
||||||
renameTabs ({ uid, schema, elementName, elementNewName }: WorkspaceTab) {
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => {
|
|
||||||
if (workspace.uid === uid) {
|
|
||||||
return {
|
|
||||||
...workspace,
|
|
||||||
tabs: workspace.tabs.map(tab => {
|
|
||||||
if (tab.elementName === elementName && tab.schema === schema) {
|
|
||||||
return {
|
|
||||||
...tab,
|
|
||||||
elementName: elementNewName
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return tab;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return workspace;
|
|
||||||
});
|
|
||||||
|
|
||||||
persistentStore.set(uid, (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid).tabs);
|
|
||||||
},
|
|
||||||
removeTab ({ uid, tab: tUid }: {uid: string; tab: string}) {
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => {
|
|
||||||
if (workspace.uid === uid) {
|
|
||||||
return {
|
|
||||||
...workspace,
|
|
||||||
tabs: workspace.tabs.filter(tab => tab.uid !== tUid)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return workspace;
|
|
||||||
});
|
|
||||||
|
|
||||||
persistentStore.set(uid, (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid).tabs);
|
|
||||||
this.checkSelectedTabExists(uid);
|
|
||||||
},
|
|
||||||
removeTabs ({ uid, schema, elementName, elementType }: WorkspaceTab) { // Multiple tabs based on schema and element name
|
|
||||||
if (elementType === 'procedure') elementType = 'routine'; // TODO: pass directly "routine"
|
|
||||||
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => {
|
|
||||||
if (workspace.uid === uid) {
|
|
||||||
return {
|
|
||||||
...workspace,
|
|
||||||
tabs: workspace.tabs.filter(tab =>
|
|
||||||
tab.schema !== schema ||
|
|
||||||
tab.elementName !== elementName ||
|
|
||||||
tab.elementType !== elementType
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return workspace;
|
|
||||||
});
|
|
||||||
|
|
||||||
persistentStore.set(uid, (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid).tabs);
|
|
||||||
this.checkSelectedTabExists(uid);
|
|
||||||
},
|
|
||||||
selectTab ({ uid, tab }: {uid: string; tab: string}) {
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === uid
|
|
||||||
? { ...workspace, selectedTab: tab }
|
|
||||||
: workspace
|
|
||||||
);
|
|
||||||
},
|
|
||||||
selectNextTab ({ uid }: {uid: string }) {
|
|
||||||
const workspace = (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid);
|
|
||||||
|
|
||||||
let newIndex = workspace.tabs.findIndex(tab => tab.selected || tab.uid === workspace.selectedTab) + 1;
|
|
||||||
|
|
||||||
if (newIndex > workspace.tabs.length -1)
|
|
||||||
newIndex = 0;
|
|
||||||
|
|
||||||
this.selectTab({ uid, tab: workspace.tabs[newIndex].uid });
|
|
||||||
},
|
|
||||||
selectPrevTab ({ uid }: {uid: string }) {
|
|
||||||
const workspace = (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid);
|
|
||||||
|
|
||||||
let newIndex = workspace.tabs.findIndex(tab => tab.selected || tab.uid === workspace.selectedTab) - 1;
|
|
||||||
|
|
||||||
if (newIndex < 0)
|
|
||||||
newIndex = workspace.tabs.length -1;
|
|
||||||
|
|
||||||
this.selectTab({ uid, tab: workspace.tabs[newIndex].uid });
|
|
||||||
},
|
|
||||||
updateTabs ({ uid, tabs }: {uid: string; tabs: WorkspaceTab[]}) {
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === uid
|
|
||||||
? { ...workspace, tabs }
|
|
||||||
: workspace
|
|
||||||
);
|
|
||||||
persistentStore.set(uid, (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid).tabs);
|
|
||||||
},
|
|
||||||
setUnsavedChanges ({ uid, tUid, isChanged }: { uid: string; tUid: string; isChanged: boolean }) {
|
|
||||||
this.workspaces = (this.workspaces as Workspace[]).map(workspace => {
|
|
||||||
if (workspace.uid === uid) {
|
|
||||||
return {
|
|
||||||
...workspace,
|
|
||||||
tabs: workspace.tabs.map(tab => {
|
|
||||||
if (tab.uid === tUid)
|
|
||||||
return { ...tab, isChanged };
|
|
||||||
|
|
||||||
return tab;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return workspace;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -2,10 +2,8 @@
|
||||||
"include": [
|
"include": [
|
||||||
"./tests/**/*",
|
"./tests/**/*",
|
||||||
"./src/main/**/*",
|
"./src/main/**/*",
|
||||||
"./src/renderer/**/*",
|
"./src/renderer/**/*"
|
||||||
"./src/common/**/*",
|
|
||||||
],
|
],
|
||||||
"exclude": ["./src/renderer/libs/ext-language_tools.js"],
|
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": "./",
|
"baseUrl": "./",
|
||||||
"target": "es2021",
|
"target": "es2021",
|
||||||
|
@ -21,7 +19,6 @@
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"removeComments": true,
|
"removeComments": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"common/*": ["./src/common/*"],
|
|
||||||
"@/*": ["./src/renderer/*"],
|
"@/*": ["./src/renderer/*"],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue