utility commit

This commit is contained in:
Fabio Di Stasio 2023-03-21 18:10:35 +01:00
parent 9f945c4b8f
commit f556bd3a8f
18 changed files with 54 additions and 1825 deletions

View File

@ -4,9 +4,9 @@ const fs = require('fs');
const path = require('path');
const https = require('https');
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 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`;

View File

@ -31,7 +31,7 @@ async function createMainWindow () {
x: mainWindowState.x,
y: mainWindowState.y,
minWidth: 900,
minHeight: 550,
minHeight: 500,
show: !isWindows,
title: 'Mizar',
icon: nativeImage.createFromDataURL(icon.default),
@ -124,8 +124,8 @@ else {
if (isWindows)
mainWindow.show();
// if (isDevelopment)
// mainWindow.webContents.openDevTools();
if (isDevelopment)
mainWindow.webContents.openDevTools();
process.on('uncaughtException', error => {
mainWindow.webContents.send('unhandled-exception', error);

View File

@ -1,179 +1,59 @@
<template>
<div id="wrapper" :class="[`theme-${applicationTheme}`, !disableBlur || 'no-blur']">
<TheTitleBar />
<div id="window-content">
<TheSettingBar @show-connections-modal="isAllConnectionsModal = true" />
<div id="main-content" class="container">
<div class="columns col-gapless">
<Workspace
v-for="connection in connections"
:key="connection.uid"
:connection="connection"
/>
<div class="connection-panel-wrapper p-relative">
<WorkspaceAddConnectionPanel v-if="selectedWorkspace === 'NEW'" />
</div>
</div>
<TheFooter />
<TheNotificationsBoard />
<TheScratchpad v-if="isScratchpad" />
<ModalSettings v-if="isSettingModal" />
<BaseTextEditor class="d-none" value="" />
</div>
<div id="wrapper">
<AppHeader
ref="header"
:sel-tab="selTab"
:client-status="clientStatus"
:server-status="serverStatus"
@select-tab="selectTab"
/>
<div id="main">
<Client
v-show="selTab === 0"
ref="client"
@client-status="clientUpdateStatus"
/>
<Server
v-show="selTab === 1"
ref="server"
@server-status="serverUpdateStatus"
/>
</div>
<ModalAllConnections v-if="isAllConnectionsModal" @close="isAllConnectionsModal = false" />
</div>
</template>
<script setup lang="ts">
import { defineAsyncComponent, onMounted, Ref, ref } from 'vue';
import { storeToRefs } from 'pinia';
import { ipcRenderer } from 'electron';
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { Menu, getCurrentWindow } from '@electron/remote';
import { useApplicationStore } from '@/stores/application';
import { useConnectionsStore } from '@/stores/connections';
import { useSettingsStore } from '@/stores/settings';
import { useWorkspacesStore } from '@/stores/workspaces';
import TheSettingBar from '@/components/TheSettingBar.vue';
import AppHeader from '@/components/app-header.vue';
import Client from '@/components/client.vue';
import Server from '@/components/server.vue';
const { t } = useI18n();
const TheTitleBar = defineAsyncComponent(() => import(/* webpackChunkName: "TheTitleBar" */'@/components/TheTitleBar.vue'));
const TheFooter = defineAsyncComponent(() => import(/* webpackChunkName: "TheFooter" */'@/components/TheFooter.vue'));
const TheNotificationsBoard = defineAsyncComponent(() => import(/* webpackChunkName: "TheNotificationsBoard" */'@/components/TheNotificationsBoard.vue'));
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 selTab = ref(0);
const clientStatus = ref(0);
const serverStatus = ref(0);
const applicationStore = useApplicationStore();
const connectionsStore = useConnectionsStore();
const settingsStore = useSettingsStore();
const workspacesStore = useWorkspacesStore();
const selectTab = (value: number) => {
selTab.value = value;
};
const {
isSettingModal,
isScratchpad
} = storeToRefs(applicationStore);
const { connections } = storeToRefs(connectionsStore);
const { applicationTheme, disableBlur } = storeToRefs(settingsStore);
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const clientUpdateStatus = (value: number) => {
clientStatus.value = value;
};
const { checkVersionUpdate } = applicationStore;
const { changeApplicationTheme } = settingsStore;
const serverUpdateStatus = (value: number) => {
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>
<style lang="scss">
html,
body {
height: 100%;
}
#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;
}
}
.fade-enter-active, .fade-leave-active {
transition: opacity .2s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
</style>

View File

@ -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>

View File

@ -1,30 +1,10 @@
import { createI18n } from 'vue-i18n';
import { enUS } from './en-US';
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 = {
'en-US': enUS,
'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
'it-IT': itIT
};
type NestedPartial<T> = {

View File

@ -1,14 +1,4 @@
export const localesNames: {[key: string]: string} = {
'en-US': 'English',
'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'
'it-IT': 'Italiano'
};

View File

@ -2,31 +2,19 @@
import { ipcRenderer } from 'electron';
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import { VueMaskDirective } from 'v-mask';
import * as FloatingVue from 'floating-vue';
import '@mdi/font/css/materialdesignicons.css';
import 'floating-vue/dist/style.css';
import 'leaflet/dist/leaflet.css';
import '@/scss/main.scss';
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';
// 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)
.directive('mask', vMaskV3)
.use(createPinia())
.use(i18n)
.use(FloatingVue)
@ -40,15 +28,6 @@ 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);
});
ipcRenderer.on('toggle-console', () => {
useConsoleStore().toggleConsole();
});
// IPC app updates
ipcRenderer.on('checking-for-update', () => {
useApplicationStore().updateStatus = 'checking';
@ -92,7 +71,3 @@ ipcRenderer.on('open-updates-preferences', () => {
useApplicationStore().showSettingModal('update');
ipcRenderer.send('check-for-updates');
});
ipcRenderer.on('update-shortcuts', (event, shortcuts) => {
useSettingsStore().updateShortcuts(shortcuts);
});

View File

@ -0,0 +1,3 @@
export function uidGen (prefix?: string) {
return (prefix ? `${prefix}:` : '') + Math.random().toString(36).substring(2, 11).toUpperCase();
}

View File

@ -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;
}
}
}
}

View File

@ -1,6 +1,5 @@
import { defineStore } from 'pinia';
import * as Store from 'electron-store';
import { Ace } from 'ace-builds';
const persistentStore = new Store({ name: 'settings' });
export const useApplicationStore = defineStore('application', {
@ -15,11 +14,9 @@ export const useApplicationStore = defineStore('application', {
selectedSettingTab: 'general',
selectedConection: {},
updateStatus: 'noupdate', // 'noupdate' | 'available' | 'checking' | 'nocheck' | 'downloading' | 'downloaded' | 'disabled'
downloadProgress: 0,
baseCompleter: [] as Ace.Completer[] // Needed to reset ace editor, due global-only ace completer
downloadProgress: 0
}),
getters: {
getBaseCompleter: state => state.baseCompleter,
getSelectedConnection: state => state.selectedConection,
getDownloadProgress: state => Number(state.downloadProgress.toFixed(1))
},
@ -34,9 +31,6 @@ export const useApplicationStore = defineStore('application', {
setLoadingStatus (payload: boolean) {
this.isLoading = payload;
},
setBaseCompleters (payload: Ace.Completer[]) {
this.baseCompleter = payload;
},
// Modals
showNewConnModal () {
this.isNewModal = true;

View File

@ -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);
}
}
});

View File

@ -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();
}
}
});

View File

@ -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);
}
}
});

View File

@ -1,5 +1,5 @@
import { defineStore } from 'pinia';
import { uidGen } from 'common/libs/uidGen';
import { uidGen } from '../libs/uidGen';
export interface Notification {
uid: string;

View File

@ -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);
}
}
});

View File

@ -2,10 +2,8 @@ import { defineStore } from 'pinia';
import { ipcRenderer } from 'electron';
import { i18n, AvailableLocale } from '@/i18n';
import * as Store from 'electron-store';
import { ShortcutRecord } from 'common/shortcuts';
const settingsStore = new Store({ name: 'settings' });
const shortcutsStore = new Store({ name: 'shortcuts' });
const isDarkTheme = window.matchMedia('(prefers-color-scheme: dark)');
const defaultAppTheme = isDarkTheme.matches ? 'dark' : 'light';
const defaultEditorTheme = isDarkTheme.matches ? 'twilight' : 'sqlserver';
@ -30,7 +28,6 @@ export const useSettingsStore = defineStore('settings', {
restoreTabs: settingsStore.get('restore_tabs', true) as boolean,
disableBlur: settingsStore.get('disable_blur', 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
}),
actions: {
@ -96,9 +93,6 @@ export const useSettingsStore = defineStore('settings', {
this.disableScratchpad = val;
settingsStore.set('disable_scratchpad', this.disableScratchpad);
},
updateShortcuts (shortcuts: ShortcutRecord[]) {
this.shortcuts = shortcuts;
},
changeDefaultCopyType (type: string) {
this.defaultCopyType = type;
settingsStore.set('default_copy_type', this.defaultCopyType);

View File

@ -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;
});
}
}
});

View File

@ -2,10 +2,8 @@
"include": [
"./tests/**/*",
"./src/main/**/*",
"./src/renderer/**/*",
"./src/common/**/*",
"./src/renderer/**/*"
],
"exclude": ["./src/renderer/libs/ext-language_tools.js"],
"compilerOptions": {
"baseUrl": "./",
"target": "es2021",
@ -21,7 +19,6 @@
"resolveJsonModule": true,
"removeComments": true,
"paths": {
"common/*": ["./src/common/*"],
"@/*": ["./src/renderer/*"],
}
}