Compare commits

...

18 Commits

Author SHA1 Message Date
Fabio Di Stasio 957cb9e1a5 chore(release): 0.7.24 2024-05-03 14:21:14 +02:00
Fabio Di Stasio 09c274a724 fix: missing accent color change 2024-05-02 18:00:07 +02:00
Fabio Di Stasio 9bcd874e80 chore(release): 0.7.24-beta.1 2024-04-30 18:09:52 +02:00
Fabio Di Stasio ece2ee05cc perf(UI): improvements on light theme 2024-04-30 18:08:07 +02:00
Fabio Di Stasio 058fc2fc0b feat: accent color based on folder color, closes #762 2024-04-30 18:07:08 +02:00
Fabio Di Stasio 33bbc0e7e6 fix(PostgreSQL): better handle connection errors, should fix #794 2024-04-30 18:06:11 +02:00
Fabio Di Stasio 23c59b4d4e fix(PostgreSQL): issue with similar tabs on differend databases 2024-04-18 18:22:29 +02:00
Fabio Di Stasio 6600197b82 perf(UI): hide "insert row" button in read-only mode, closes #695 2024-04-14 16:23:56 +02:00
Fabio Di Stasio 33203aeb04 refactor(UI): change query tab buttons order 2024-04-12 18:03:17 +02:00
Fabio Di Stasio f4f385589f chore(release): 0.7.24-beta.0 2024-04-12 08:44:08 +02:00
Fabio Di Stasio 0565ae1204 fix(translation): missing translation for "Open notes" shortcut 2024-04-08 18:33:37 +02:00
Fabio Di Stasio 258fbc81f7 Merge branch 'master' of https://github.com/antares-sql/antares into develop 2024-04-08 18:30:23 +02:00
Fabio Di Stasio 8d8650fbe7 feat: unsaved file reminder closing file tabs 2024-04-08 18:29:05 +02:00
Fabio Di Stasio 099a71a189
Merge pull request #785 from bagusindrayana/feat-open-edit-save-file
Feat open, edit, and save file in query tab
2024-04-08 12:49:01 +02:00
Fabio Di Stasio e7efb9c616 refactor(UI): change to query tab icons to avoid ambiguity with new features 2024-04-08 09:52:46 +02:00
bagusindrayana c1e58eb695 feat: open,save, and save as file in query tab 2024-04-06 15:34:42 +08:00
bagusindrayana f7204dc0ae feat: add translation for open,save, and save as file 2024-04-06 15:34:18 +08:00
bagusindrayana 6b56c60b68 feat: add shortcut open,save, and save as file 2024-04-06 15:33:01 +08:00
30 changed files with 592 additions and 90 deletions

View File

@ -2,6 +2,47 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [0.7.24](https://github.com/antares-sql/antares/compare/v0.7.24-beta.1...v0.7.24) (2024-05-03)
### Bug Fixes
* missing accent color change ([09c274a](https://github.com/antares-sql/antares/commit/09c274a724b5020efc650aaf7eecb2404343a6fc))
### [0.7.24-beta.1](https://github.com/antares-sql/antares/compare/v0.7.24-beta.0...v0.7.24-beta.1) (2024-04-30)
### Features
* accent color based on folder color, closes [#762](https://github.com/antares-sql/antares/issues/762) ([058fc2f](https://github.com/antares-sql/antares/commit/058fc2fc0b34cde5aa19233a4a999ef3624dae71))
### Bug Fixes
* **PostgreSQL:** better handle connection errors, should fix [#794](https://github.com/antares-sql/antares/issues/794) ([33bbc0e](https://github.com/antares-sql/antares/commit/33bbc0e7e6be370c944e979a34ab2cb19562d1e3))
* **PostgreSQL:** issue with similar tabs on differend databases ([23c59b4](https://github.com/antares-sql/antares/commit/23c59b4d4e8f250acad75f54d157c7c162e1c4f8))
### Improvements
* **UI:** hide "insert row" button in read-only mode, closes [#695](https://github.com/antares-sql/antares/issues/695) ([6600197](https://github.com/antares-sql/antares/commit/6600197b8286ced4c79378883594d21e69a83d8c))
* **UI:** improvements on light theme ([ece2ee0](https://github.com/antares-sql/antares/commit/ece2ee05cc90a58c1926e882e3ccf4f057f02d68))
### [0.7.24-beta.0](https://github.com/antares-sql/antares/compare/v0.7.23...v0.7.24-beta.0) (2024-04-12)
### Features
* add shortcut open,save, and save as file ([6b56c60](https://github.com/antares-sql/antares/commit/6b56c60b68647bc7182548a137cccc3413e3fbd5))
* add translation for open,save, and save as file ([f7204dc](https://github.com/antares-sql/antares/commit/f7204dc0ae721534eaefbde097d1c26c1d72ad41))
* open,save, and save as file in query tab ([c1e58eb](https://github.com/antares-sql/antares/commit/c1e58eb695de78fbf1d2b26c608692f0962373df))
* unsaved file reminder closing file tabs ([8d8650f](https://github.com/antares-sql/antares/commit/8d8650fbe76c79fd66be857d049b3baaa9ab1f9f))
### Bug Fixes
* **translation:** missing translation for "Open notes" shortcut ([0565ae1](https://github.com/antares-sql/antares/commit/0565ae12042901b9d67fe3e0ea269562ec444994))
### [0.7.23](https://github.com/antares-sql/antares/compare/v0.7.23-beta.1...v0.7.23) (2024-04-07) ### [0.7.23](https://github.com/antares-sql/antares/compare/v0.7.23-beta.1...v0.7.23) (2024-04-07)
### [0.7.23-beta.1](https://github.com/antares-sql/antares/compare/v0.7.23-beta.0...v0.7.23-beta.1) (2024-04-02) ### [0.7.23-beta.1](https://github.com/antares-sql/antares/compare/v0.7.23-beta.0...v0.7.23-beta.1) (2024-04-02)

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "antares", "name": "antares",
"version": "0.7.23", "version": "0.7.24",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "antares", "name": "antares",
"version": "0.7.23", "version": "0.7.24",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

View File

@ -1,7 +1,7 @@
{ {
"name": "antares", "name": "antares",
"productName": "Antares", "productName": "Antares",
"version": "0.7.23", "version": "0.7.24",
"description": "A modern, fast and productivity driven SQL client with a focus in UX.", "description": "A modern, fast and productivity driven SQL client with a focus in UX.",
"license": "MIT", "license": "MIT",
"repository": "https://github.com/antares-sql/antares.git", "repository": "https://github.com/antares-sql/antares.git",

View File

@ -6,6 +6,9 @@ export const shortcutEvents: Record<string, { l18n: string; l18nParam?: string |
'kill-query': { l18n: 'database.killQuery', context: 'tab' }, 'kill-query': { l18n: 'database.killQuery', context: 'tab' },
'query-history': { l18n: 'database.queryHistory', context: 'tab' }, 'query-history': { l18n: 'database.queryHistory', context: 'tab' },
'clear-query': { l18n: 'database.clearQuery', context: 'tab' }, 'clear-query': { l18n: 'database.clearQuery', context: 'tab' },
// 'save-file': { l18n: 'application.saveFile', context: 'tab' },
'open-file': { l18n: 'application.openFile', context: 'tab' },
'save-file-as': { l18n: 'application.saveFileAs', context: 'tab' },
'next-tab': { l18n: 'application.nextTab' }, 'next-tab': { l18n: 'application.nextTab' },
'prev-tab': { l18n: 'application.previousTab' }, 'prev-tab': { l18n: 'application.previousTab' },
'open-all-connections': { l18n: 'application.openAllConnections' }, 'open-all-connections': { l18n: 'application.openAllConnections' },
@ -16,7 +19,7 @@ export const shortcutEvents: Record<string, { l18n: string; l18nParam?: string |
'save-content': { l18n: 'application.saveContent' }, 'save-content': { l18n: 'application.saveContent' },
'create-connection': { l18n: 'connection.createNewConnection' }, 'create-connection': { l18n: 'connection.createNewConnection' },
'open-settings': { l18n: 'application.openSettings' }, 'open-settings': { l18n: 'application.openSettings' },
'open-scratchpad': { l18n: 'application.openScratchpad' } 'open-scratchpad': { l18n: 'application.openNotes' }
}; };
interface ShortcutRecord { interface ShortcutRecord {
@ -119,6 +122,21 @@ const shortcuts: ShortcutRecord[] = [
event: 'toggle-console', event: 'toggle-console',
keys: ['CommandOrControl+`'], keys: ['CommandOrControl+`'],
os: ['darwin', 'linux', 'win32'] os: ['darwin', 'linux', 'win32']
},
// {
// event: 'save-file',
// keys: ['CommandOrControl+S'],
// os: ['darwin', 'linux', 'win32']
// },
{
event: 'open-file',
keys: ['CommandOrControl+O'],
os: ['darwin', 'linux', 'win32']
},
{
event: 'save-file-as',
keys: ['Shift+CommandOrControl+S'],
os: ['darwin', 'linux', 'win32']
} }
]; ];

View File

@ -1,5 +1,6 @@
import { app, dialog, ipcMain, safeStorage } from 'electron'; import { app, dialog, ipcMain, safeStorage } from 'electron';
import * as Store from 'electron-store'; import * as Store from 'electron-store';
import * as fs from 'fs';
import { validateSender } from '../libs/misc/validateSender'; import { validateSender } from '../libs/misc/validateSender';
import { ShortcutRegister } from '../libs/ShortcutRegister'; import { ShortcutRegister } from '../libs/ShortcutRegister';
@ -52,6 +53,11 @@ export default () => {
return dialog.showOpenDialog(options); return dialog.showOpenDialog(options);
}); });
ipcMain.handle('show-save-dialog', (event, options) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
return dialog.showSaveDialog(options);
});
ipcMain.handle('get-download-dir-path', (event) => { ipcMain.handle('get-download-dir-path', (event) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' }; if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
return app.getPath('downloads'); return app.getPath('downloads');
@ -80,4 +86,26 @@ export default () => {
const shortCutRegister = ShortcutRegister.getInstance(); const shortCutRegister = ShortcutRegister.getInstance();
shortCutRegister.unregister(); shortCutRegister.unregister();
}); });
ipcMain.handle('read-file', (event, filePath) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const content = fs.readFileSync(filePath, 'utf-8');
return content;
}
catch (error) {
return { status: 'error', response: error.toString() };
}
});
ipcMain.handle('write-file', (event, filePath, content) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
fs.writeFileSync(filePath, content, 'utf-8');
return { status: 'success' };
}
catch (error) {
return { status: 'error', response: error.toString() };
}
});
}; };

View File

@ -210,6 +210,10 @@ export class PostgreSQLClient extends BaseClient {
if (this._params.readonly) if (this._params.readonly)
await connection.query('SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY'); await connection.query('SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY');
connection.on('error', err => { // Intercepts errors and converts to rejections
Promise.reject(err);
});
return connection; return connection;
} }

View File

@ -99,7 +99,7 @@ onMounted(() => {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
background: $primary-color; background: var(--primary-color);
border-radius: 50%; border-radius: 50%;
box-shadow: 0 0 5px 1px darken($body-font-color-dark, 40%); box-shadow: 0 0 5px 1px darken($body-font-color-dark, 40%);
} }

View File

@ -360,7 +360,7 @@ onBeforeUnmount(() => {
outline: none; outline: none;
&:focus { &:focus {
box-shadow: 0 0 3px 0.1rem rgba($primary-color, 80%); box-shadow: 0 0 3px 0.1rem rgba(var(--primary-color), 80%);
} }
&:hover { &:hover {

View File

@ -204,7 +204,7 @@ onBeforeUnmount(() => {
cursor: pointer; cursor: pointer;
&.selected { &.selected {
outline: 2px solid $primary-color; outline: 2px solid var(--primary-color);
border-radius: 8px; border-radius: 8px;
} }
} }

View File

@ -287,7 +287,7 @@ onBeforeUnmount(() => {
max-width: 100%; max-width: 100%;
display: inline-block; display: inline-block;
font-size: 100%; font-size: 100%;
// color: $primary-color; // color: var(--primary-color);
opacity: 0.8; opacity: 0.8;
font-weight: 600; font-weight: 600;
} }

View File

@ -703,7 +703,7 @@ onBeforeUnmount(() => {
&.selected { &.selected {
img { img {
box-shadow: 0 0 0 3px $primary-color; box-shadow: 0 0 0 3px var(--primary-color);
} }
} }
@ -731,7 +731,7 @@ onBeforeUnmount(() => {
.badge-update::after { .badge-update::after {
bottom: initial; bottom: initial;
background: $primary-color; background: var(--primary-color);
} }
.form-label { .form-label {

View File

@ -409,7 +409,7 @@ defineExpose({ editor });
position: absolute; position: absolute;
left: 3px; left: 3px;
top: 2px; top: 2px;
color: $primary-color; color: var(--primary-color);
display: inline-block; display: inline-block;
font: normal normal normal 24px/1 "Material Design Icons", sans-serif; font: normal normal normal 24px/1 "Material Design Icons", sans-serif;
font-size: inherit; font-size: inherit;

View File

@ -14,7 +14,7 @@
<div class="tile-icon"> <div class="tile-icon">
<BaseIcon <BaseIcon
:icon-name="note.type === 'query' :icon-name="note.type === 'query'
? 'mdiStarOutline' ? 'mdiHeartOutline'
: note.type === 'todo' : note.type === 'todo'
? note.isArchived ? note.isArchived
? 'mdiCheckboxMarkedOutline' ? 'mdiCheckboxMarkedOutline'

View File

@ -1,8 +1,8 @@
<template> <template>
<div <div
id="footer" id="footer"
:class="[lightColors.includes(footerColor) ? 'text-dark' : 'text-light']" :class="[lightColors.includes(accentColor) ? 'text-dark' : 'text-light']"
:style="`background-color: ${footerColor};`" :style="`background-color: ${accentColor};`"
> >
<div class="footer-left-elements"> <div class="footer-left-elements">
<ul class="footer-elements"> <ul class="footer-elements">
@ -85,10 +85,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { shell } from 'electron'; import { shell } from 'electron';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { computed, ComputedRef } from 'vue'; import { computed, ComputedRef, watch } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import BaseIcon from '@/components/BaseIcon.vue'; import BaseIcon from '@/components/BaseIcon.vue';
import { hexToRGBA } from '@/libs/hexToRgba';
import { useApplicationStore } from '@/stores/application'; import { useApplicationStore } from '@/stores/application';
import { useConnectionsStore } from '@/stores/connections'; import { useConnectionsStore } from '@/stores/connections';
import { useConsoleStore } from '@/stores/console'; import { useConsoleStore } from '@/stores/console';
@ -117,7 +118,11 @@ const { getWorkspace } = workspacesStore;
const { getConnectionFolder, getConnectionByUid } = connectionsStore; const { getConnectionFolder, getConnectionByUid } = connectionsStore;
const workspace = computed(() => getWorkspace(workspaceUid.value)); const workspace = computed(() => getWorkspace(workspaceUid.value));
const footerColor = computed(() => getConnectionFolder(workspaceUid.value)?.color || '#E36929'); const accentColor = computed(() => {
if (getConnectionFolder(workspaceUid.value)?.color)
return getConnectionFolder(workspaceUid.value).color;
return '#E36929';
});
const connectionInfos = computed(() => getConnectionByUid(workspaceUid.value)); const connectionInfos = computed(() => getConnectionByUid(workspaceUid.value));
const version: ComputedRef<DatabaseInfos> = computed(() => { const version: ComputedRef<DatabaseInfos> = computed(() => {
return getWorkspace(workspaceUid.value) ? workspace.value.version : null; return getWorkspace(workspaceUid.value) ? workspace.value.version : null;
@ -129,7 +134,17 @@ const versionString = computed(() => {
return ''; return '';
}); });
watch(accentColor, () => {
changeAccentColor();
});
const openOutside = (link: string) => shell.openExternal(link); const openOutside = (link: string) => shell.openExternal(link);
const changeAccentColor = () => {
document.querySelector<HTMLBodyElement>(':root').style.setProperty('--primary-color', accentColor.value);
document.querySelector<HTMLBodyElement>(':root').style.setProperty('--primary-color-shadow', hexToRGBA(accentColor.value, 0.2));
};
changeAccentColor();
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -233,6 +233,7 @@ if (!connectionsArr.value.length)
border-radius: 0; border-radius: 0;
padding: 0; padding: 0;
position: relative; position: relative;
border: none;
&:hover { &:hover {
opacity: 1; opacity: 1;

View File

@ -42,11 +42,11 @@
> >
<BaseIcon <BaseIcon
class="mt-1 mr-1" class="mt-1 mr-1"
icon-name="mdiCodeTags" :icon-name="element.filePath ? 'mdiFileCodeOutline' : 'mdiCodeTags'"
:size="18" :size="18"
/> />
<span> <span>
<span>{{ cutText(element.content || 'Query', 20, true) }} #{{ element.index }}</span> <span>{{ cutText(element.elementName || element.content || 'Query', 20, true) }} #{{ element.index }}</span>
<span <span
class="btn btn-clear" class="btn btn-clear"
:title="t('general.close')" :title="t('general.close')"

View File

@ -501,7 +501,7 @@ const toggleSearchMethod = () => {
transition: background 0.2s; transition: background 0.2s;
&:hover { &:hover {
background: rgba($primary-color, 50%); background: rgba(var(--primary-color), 50%);
} }
} }

View File

@ -513,7 +513,13 @@ const selectMisc = ({ schema, misc, type }: { schema: string; misc: { name: stri
}; };
const openDataTab = ({ schema, table }: { schema: string; table: TableInfos }) => { const openDataTab = ({ schema, table }: { schema: string; table: TableInfos }) => {
newTab({ uid: props.connection.uid, elementName: table.name, schema: props.database.name, type: 'data', elementType: table.type }); newTab({
uid: props.connection.uid,
elementName: table.name,
schema: props.database.name,
type: 'data',
elementType: table.type
});
setBreadcrumbs({ schema, [table.type]: table.name }); setBreadcrumbs({ schema, [table.type]: table.name });
}; };

View File

@ -145,7 +145,7 @@ onMounted(() => {
transition: background 0.2s; transition: background 0.2s;
&:hover { &:hover {
background: rgba($primary-color, 50%); background: rgba(var(--primary-color), 50%);
} }
} }

View File

@ -19,7 +19,10 @@
<div ref="resizer" class="query-area-resizer" /> <div ref="resizer" class="query-area-resizer" />
<div ref="queryAreaFooter" class="workspace-query-runner-footer"> <div ref="queryAreaFooter" class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
<div @mouseenter="setCancelButtonVisibility(true)" @mouseleave="setCancelButtonVisibility(false)"> <div
@mouseenter="setCancelButtonVisibility(true)"
@mouseleave="setCancelButtonVisibility(false)"
>
<button <button
v-if="showCancel && isQuering" v-if="showCancel && isQuering"
class="btn btn-primary btn-sm cancellable" class="btn btn-primary btn-sm cancellable"
@ -94,6 +97,48 @@
> >
<BaseIcon icon-name="mdiBrush" :size="24" /> <BaseIcon icon-name="mdiBrush" :size="24" />
</button> </button>
<div class="btn-group">
<button
class="btn btn-dark btn-sm mr-0"
:disabled="!filePath || lastSavedQuery === query"
:title="t('application.saveFile')"
@click="saveFile()"
>
<BaseIcon icon-name="mdiContentSaveCheckOutline" :size="24" />
</button>
<button
class="btn btn-dark btn-sm mr-0"
:title="t('application.saveFileAs')"
@click="saveFileAs()"
>
<BaseIcon icon-name="mdiContentSavePlusOutline" :size="24" />
</button>
<button
class="btn btn-dark btn-sm"
:title="t('application.openFile')"
@click="openFile()"
>
<BaseIcon icon-name="mdiFolderOpenOutline" :size="24" />
</button>
</div>
<div class="btn-group">
<button
class="btn btn-dark btn-sm mr-0"
:disabled="isQuering || (isQuerySaved || query.length < 5)"
:title="t('application.saveAsNote')"
@click="saveQuery()"
>
<BaseIcon icon-name="mdiHeartPlusOutline" :size="24" />
</button>
<button
class="btn btn-dark btn-sm"
:disabled="isQuering"
:title="t('database.savedQueries')"
@click="openSavedModal()"
>
<BaseIcon icon-name="mdiNotebookHeartOutline" :size="24" />
</button>
</div>
<button <button
class="btn btn-dark btn-sm" class="btn btn-dark btn-sm"
:disabled="isQuering" :disabled="isQuering"
@ -102,24 +147,6 @@
> >
<BaseIcon icon-name="mdiHistory" :size="24" /> <BaseIcon icon-name="mdiHistory" :size="24" />
</button> </button>
<div class="btn-group">
<button
class="btn btn-dark btn-sm mr-0"
:disabled="isQuering || (isQuerySaved || query.length < 5)"
:title="t('general.save')"
@click="saveQuery()"
>
<BaseIcon icon-name="mdiContentSaveOutline" :size="24" />
</button>
<button
class="btn btn-dark btn-sm"
:disabled="isQuering"
:title="t('database.savedQueries')"
@click="openSavedModal()"
>
<BaseIcon icon-name="mdiStarOutline" :size="24" />
</button>
</div>
<div class="dropdown table-dropdown pr-2"> <div class="dropdown table-dropdown pr-2">
<button <button
:disabled="!hasResults || isQuering" :disabled="!hasResults || isQuering"
@ -251,7 +278,7 @@ import { uidGen } from 'common/libs/uidGen';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { format } from 'sql-formatter'; import { format } from 'sql-formatter';
import { Component, computed, onBeforeUnmount, onMounted, Prop, Ref, ref, watch } from 'vue'; import { Component, computed, onBeforeUnmount, onMounted, Prop, Ref, ref, toRaw, watch } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import BaseIcon from '@/components/BaseIcon.vue'; import BaseIcon from '@/components/BaseIcon.vue';
@ -262,6 +289,7 @@ import QueryEditor from '@/components/QueryEditor.vue';
import WorkspaceTabQueryEmptyState from '@/components/WorkspaceTabQueryEmptyState.vue'; import WorkspaceTabQueryEmptyState from '@/components/WorkspaceTabQueryEmptyState.vue';
import WorkspaceTabQueryTable from '@/components/WorkspaceTabQueryTable.vue'; import WorkspaceTabQueryTable from '@/components/WorkspaceTabQueryTable.vue';
import { useResultTables } from '@/composables/useResultTables'; import { useResultTables } from '@/composables/useResultTables';
import Application from '@/ipc-api/Application';
import Schema from '@/ipc-api/Schema'; import Schema from '@/ipc-api/Schema';
import { useApplicationStore } from '@/stores/application'; import { useApplicationStore } from '@/stores/application';
import { useConsoleStore } from '@/stores/console'; import { useConsoleStore } from '@/stores/console';
@ -302,14 +330,18 @@ const {
getWorkspace, getWorkspace,
changeBreadcrumbs, changeBreadcrumbs,
updateTabContent, updateTabContent,
setUnsavedChanges setUnsavedChanges,
newTab
} = workspacesStore; } = workspacesStore;
const queryEditor: Ref<Component & { editor: Ace.Editor; $el: HTMLElement }> = ref(null); const queryEditor: Ref<Component & { editor: Ace.Editor; $el: HTMLElement }> = ref(null);
const queryAreaFooter: Ref<HTMLDivElement> = ref(null); const queryAreaFooter: Ref<HTMLDivElement> = ref(null);
const resizer: Ref<HTMLDivElement> = ref(null); const resizer: Ref<HTMLDivElement> = ref(null);
const queryName = ref('');
const query = ref(''); const query = ref('');
const filePath = ref('');
const lastQuery = ref(''); const lastQuery = ref('');
const lastSavedQuery = ref('');
const isCancelling = ref(false); const isCancelling = ref(false);
const showCancel = ref(false); const showCancel = ref(false);
const autocommit = ref(true); const autocommit = ref(true);
@ -333,17 +365,41 @@ const databaseSchemas = computed(() => {
}); });
const hasResults = computed(() => results.value.length && results.value[0].rows); const hasResults = computed(() => results.value.length && results.value[0].rows);
const hasAffected = computed(() => affectedCount.value || (!resultsCount.value && affectedCount.value !== null)); const hasAffected = computed(() => affectedCount.value || (!resultsCount.value && affectedCount.value !== null));
const isChanged = computed(() => {
return filePath.value && lastSavedQuery.value !== query.value;
});
watch(query, (val) => { watch(query, (val) => {
clearTimeout(debounceTimeout.value); clearTimeout(debounceTimeout.value);
debounceTimeout.value = setTimeout(() => { debounceTimeout.value = setTimeout(() => {
updateTabContent({ updateTabContent({
elementName: queryName.value,
filePath: filePath.value,
uid: props.connection.uid, uid: props.connection.uid,
tab: props.tab.uid, tab: props.tab.uid,
type: 'query', type: 'query',
schema: selectedSchema.value, schema: selectedSchema.value,
content: val content: val
});
isQuerySaved.value = false;
}, 200);
});
watch(queryName, (val) => {
clearTimeout(debounceTimeout.value);
debounceTimeout.value = setTimeout(() => {
updateTabContent({
elementName: val,
filePath: filePath.value,
uid: props.connection.uid,
tab: props.tab.uid,
type: 'query',
schema: selectedSchema.value,
content: query.value
}); });
isQuerySaved.value = false; isQuerySaved.value = false;
@ -377,6 +433,10 @@ watch(() => props.tab.content, () => {
queryEditor.value.editor.session.setValue(query.value); queryEditor.value.editor.session.setValue(query.value);
}); });
watch(isChanged, (val) => {
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
});
const runQuery = async (query: string) => { const runQuery = async (query: string) => {
if (!query || isQuering.value) return; if (!query || isQuering.value) return;
isQuering.value = true; isQuering.value = true;
@ -529,7 +589,8 @@ const saveQuery = () => {
type: 'query', type: 'query',
date: new Date(), date: new Date(),
note: query.value, note: query.value,
isArchived: false isArchived: false,
title: queryName.value
}); });
isQuerySaved.value = true; isQuerySaved.value = true;
}; };
@ -596,6 +657,8 @@ const rollbackTab = async () => {
defineExpose({ resizeResults }); defineExpose({ resizeResults });
query.value = props.tab.content as string; query.value = props.tab.content as string;
queryName.value = props.tab.elementName as string;
filePath.value = props.tab.filePath as string;
selectedSchema.value = props.tab.schema || breadcrumbsSchema.value; selectedSchema.value = props.tab.schema || breadcrumbsSchema.value;
window.addEventListener('resize', onWindowResize); window.addEventListener('resize', onWindowResize);
@ -630,6 +693,73 @@ const historyListener = () => {
openHistoryModal(); openHistoryModal();
}; };
const openFileListener = () => {
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
if (props.isSelected && !hasModalOpen)
openFile();
};
const saveFileAsListener = () => {
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
if (props.isSelected && !hasModalOpen)
saveFileAs();
};
const saveContentListener = () => {
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
if (props.isSelected && !hasModalOpen && filePath)
saveFile();
};
const openFile = async () => {
const result = await Application.showOpenDialog({ properties: ['openFile'], filters: [{ name: 'SQL', extensions: ['sql', 'txt'] }] });
if (result && !result.canceled) {
const file = result.filePaths[0];
const content = await Application.readFile(file);
const fileName = file.split('/').pop().split('\\').pop();
if (props.tab.filePath && props.tab.filePath !== file) {
newTab({
uid: props.connection.uid,
type: 'query',
filePath: file,
content: '',
schema: selectedSchema.value,
elementName: fileName
});
}
else {
filePath.value = file;
queryName.value = fileName;
query.value = content;
lastSavedQuery.value = content;
}
}
};
const saveFileAs = async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const result: any = await Application.showSaveDialog({ filters: [{ name: 'SQL', extensions: ['sql'] }], defaultPath: `${queryName.value || 'query'}.sql` });
if (result && !result.canceled) {
await Application.writeFile(result.filePath, query.value);
addNotification({ status: 'success', message: t('general.actionSuccessful', { action: t('application.saveFile') }) });
queryName.value = result.filePath.split('/').pop().split('\\').pop();
filePath.value = result.filePath;
lastSavedQuery.value = toRaw(query.value);
}
};
const saveFile = async () => {
await Application.writeFile(filePath.value, query.value);
addNotification({ status: 'success', message: t('general.actionSuccessful', { action: t('application.saveFile') }) });
lastSavedQuery.value = toRaw(query.value);
};
const loadFileContent = async (file: string) => {
const content = await Application.readFile(file);
query.value = content;
lastSavedQuery.value = content;
};
onMounted(() => { onMounted(() => {
const localResizer = resizer.value; const localResizer = resizer.value;
@ -638,6 +768,9 @@ onMounted(() => {
ipcRenderer.on('kill-query', killQueryListener); ipcRenderer.on('kill-query', killQueryListener);
ipcRenderer.on('clear-query', clearQueryListener); ipcRenderer.on('clear-query', clearQueryListener);
ipcRenderer.on('query-history', historyListener); ipcRenderer.on('query-history', historyListener);
ipcRenderer.on('open-file', openFileListener);
ipcRenderer.on('save-file-as', saveFileAsListener);
ipcRenderer.on('save-content', saveContentListener);
localResizer.addEventListener('mousedown', (e: MouseEvent) => { localResizer.addEventListener('mousedown', (e: MouseEvent) => {
e.preventDefault(); e.preventDefault();
@ -648,6 +781,9 @@ onMounted(() => {
if (props.tab.autorun) if (props.tab.autorun)
runQuery(query.value); runQuery(query.value);
if (props.tab.filePath)
loadFileContent(props.tab.filePath);
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
@ -663,6 +799,9 @@ onBeforeUnmount(() => {
ipcRenderer.removeListener('kill-query', killQueryListener); ipcRenderer.removeListener('kill-query', killQueryListener);
ipcRenderer.removeListener('clear-query', clearQueryListener); ipcRenderer.removeListener('clear-query', clearQueryListener);
ipcRenderer.removeListener('query-history', historyListener); ipcRenderer.removeListener('query-history', historyListener);
ipcRenderer.removeListener('open-file', openFileListener);
ipcRenderer.removeListener('save-file-as', saveFileAsListener);
ipcRenderer.removeListener('save-content', saveContentListener);
}); });
</script> </script>
@ -682,7 +821,7 @@ onBeforeUnmount(() => {
transition: background 0.2s; transition: background 0.2s;
&:hover { &:hover {
background: rgba($primary-color, 50%); background: rgba(var(--primary-color), 50%);
} }
} }
@ -721,4 +860,4 @@ onBeforeUnmount(() => {
min-height: 200px; min-height: 200px;
} }
} }
</style> </style>filePathsfilePathsfilePaths

View File

@ -91,7 +91,7 @@
<BaseIcon icon-name="mdiMagnify" :size="24" /> <BaseIcon icon-name="mdiMagnify" :size="24" />
</button> </button>
<button <button
v-if="isTable" v-if="isTable && !connection.readonly"
class="btn btn-dark btn-sm" class="btn btn-dark btn-sm"
:disabled="isQuering" :disabled="isQuering"
@click="showFakerModal()" @click="showFakerModal()"

View File

@ -397,9 +397,15 @@ export const enUS = {
thereAreNoNotesYet: 'There are no notes yet', thereAreNoNotesYet: 'There are no notes yet',
addNote: 'Add note', addNote: 'Add note',
editNote: 'Edit note', editNote: 'Edit note',
saveAsNote: 'Save as note',
showArchivedNotes: 'Show archived notes', showArchivedNotes: 'Show archived notes',
hideArchivedNotes: 'Hide archived notes', hideArchivedNotes: 'Hide archived notes',
tag: 'Tag' // Note tag tag: 'Tag', // Note tag,
saveFile: 'Save file',
saveFileAs: 'Save file as',
openFile: 'Open file',
openNotes: 'Open notes'
}, },
faker: { // Faker.js methods, used in random generated content faker: { // Faker.js methods, used in random generated content
address: 'Address', address: 'Address',

View File

@ -8,6 +8,10 @@ export default class {
return ipcRenderer.invoke('show-open-dialog', unproxify(options)); return ipcRenderer.invoke('show-open-dialog', unproxify(options));
} }
static showSaveDialog (options: OpenDialogOptions): Promise<OpenDialogReturnValue> {
return ipcRenderer.invoke('show-save-dialog', unproxify(options));
}
static getDownloadPathDirectory (): Promise<string> { static getDownloadPathDirectory (): Promise<string> {
return ipcRenderer.invoke('get-download-dir-path'); return ipcRenderer.invoke('get-download-dir-path');
} }
@ -27,4 +31,12 @@ export default class {
static unregisterShortcuts () { static unregisterShortcuts () {
return ipcRenderer.invoke('unregister-shortcuts'); return ipcRenderer.invoke('unregister-shortcuts');
} }
static readFile (path: string): Promise<string> {
return ipcRenderer.invoke('read-file', path);
}
static writeFile (path: string, content: unknown) {
return ipcRenderer.invoke('write-file', path, content);
}
} }

View File

@ -0,0 +1,18 @@
export const colorShade = (color: string, amount: number) => {
color = color.replaceAll('#', '');
if (color.length === 3) color = color[0] + color[0] + color[1] + color[1] + color[2] + color[2];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let [r, g, b] = color.match(/.{2}/g) as any;
([r, g, b] = [parseInt(r, 16) + amount, parseInt(g, 16) + amount, parseInt(b, 16) + amount]);
r = Math.max(Math.min(255, r), 0).toString(16);
g = Math.max(Math.min(255, g), 0).toString(16);
b = Math.max(Math.min(255, b), 0).toString(16);
const rr = (r.length < 2 ? '0' : '') + r;
const gg = (g.length < 2 ? '0' : '') + g;
const bb = (b.length < 2 ? '0' : '') + b;
return `#${rr}${gg}${bb}`;
};

View File

@ -0,0 +1,16 @@
export const hexToRGBA = (hexCode: string, opacity = 1) => {
let hex = hexCode.replace('#', '');
if (hex.length === 3)
hex = `${hex[0]}${hex[0]}${hex[1]}${hex[1]}${hex[2]}${hex[2]}`;
const r = parseInt(hex.substring(0, 2), 16);
const g = parseInt(hex.substring(2, 4), 16);
const b = parseInt(hex.substring(4, 6), 16);
/* Backward compatibility for whole number based opacity values. */
if (opacity > 1 && opacity <= 100)
opacity = opacity / 100;
return `rgba(${r},${g},${b},${opacity})`;
};

View File

@ -1,5 +1,5 @@
/* Colors */ /* Colors */
$body-bg: #fdfdfd; $body-bg: #f3f3f3;
$body-bg-dark: #1d1d1d; $body-bg-dark: #1d1d1d;
$body-font-color-dark: #fff; $body-font-color-dark: #fff;
$bg-color-dark: #1d1d1d; $bg-color-dark: #1d1d1d;

View File

@ -1,4 +1,10 @@
/* stylelint-disable */ /* stylelint-disable */
:root {
--primary-color: #e36929;
--primary-color-dark: color-mix(in srgb, var(--primary-color), #000 30%);
--primary-color-shadow: 0 0 0 0.1rem rgba(227, 105, 41, 0.2);
}
@import "~spectre.css/src/variables"; @import "~spectre.css/src/variables";
@import "variables"; @import "variables";
@import "transitions"; @import "transitions";
@ -16,12 +22,20 @@ body {
user-select: none; user-select: none;
} }
a {
color: var(--primary-color);
&:hover {
color: var(--primary-color-dark)
}
}
::selection, ::selection,
option:hover, option:hover,
option:focus, option:focus,
option:active, option:active,
option:checked { option:checked {
background-color: $primary-color; background-color: var(--primary-color);
color: $light-color; color: $light-color;
} }
@ -189,6 +203,14 @@ option:checked {
animation: rotation 0.8s infinite linear; animation: rotation 0.8s infinite linear;
} }
.loading {
&::after {
border: 0.1rem solid var(--primary-color);
border-right-color: transparent;
border-top-color: transparent;
}
}
/* Override */ /* Override */
.modal { .modal {
.modal-container, .modal-container,
@ -248,7 +270,7 @@ option:checked {
height: 2px; height: 2px;
width: 0; width: 0;
transition: width 0.2s; transition: width 0.2s;
background-color: $primary-color; background-color: var(--primary-color);
position: absolute; position: absolute;
bottom: 0; bottom: 0;
} }
@ -300,9 +322,23 @@ option:checked {
height: auto; height: auto;
} }
.form-checkbox input:checked + .form-icon, .form-radio input:checked + .form-icon, .form-switch input:checked + .form-icon {
background: var(--primary-color);
border-color: var(--primary-color);
}
.form-checkbox input:focus + .form-icon, .form-radio input:focus + .form-icon, .form-switch input:focus + .form-icon {
box-shadow: 0 0 0 0.1rem var(--primary-color-shadow);
border-color: var(--primary-color);
}
.form-select { .form-select {
cursor: pointer; cursor: pointer;
&:focus {
box-shadow: 0 0 0 0.1rem var(--primary-color-shadow);
}
&.small-select { &.small-select {
height: 21px; height: 21px;
font-size: 0.7rem; font-size: 0.7rem;
@ -311,7 +347,8 @@ option:checked {
&.select { &.select {
&.select--open { &.select--open {
border-color: $primary-color !important; border-color: var(--primary-color) !important;
box-shadow: 0 0 0 0.1rem var(--primary-color-shadow) !important;
@include control-shadow(); @include control-shadow();
} }
@ -336,19 +373,28 @@ option:checked {
z-index: 401 !important; z-index: 401 !important;
border: 1px solid transparent; border: 1px solid transparent;
border-radius: $border-radius; border-radius: $border-radius;
box-shadow: 0 8px 17px 0 rgb(0 0 0 / 20%), 0 6px 20px 0 rgb(0 0 0 / 19%); box-shadow:
0 8px 17px 0 rgb(0 0 0 / 20%),
0 6px 20px 0 rgb(0 0 0 / 19%);
} }
.select__option--selected { .select__option--selected {
background: rgba($primary-color, 0.25); background: rgba(var(--primary-color), 0.25);
} }
.select__option--highlight { .select__option--highlight {
background: $primary-color; background: var(--primary-color);
text-shadow: 0 0 15px #000;
} }
.form-input[type="file"] { .form-input {
overflow: hidden; &[type="file"] {
overflow: hidden;
}
&:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.1rem var(--primary-color-shadow);
}
} }
.input-group .input-group-addon { .input-group .input-group-addon {
@ -370,13 +416,34 @@ option:checked {
} }
.btn { .btn {
color: var(--primary-color);
border-color: var(--primary-color);
&:not(.btn-link) {
text-shadow: 0 0 15px #000;
}
&:hover {
border-color: var(--primary-color-dark);
}
&:focus { &:focus {
box-shadow: 0 0 3px 1px rgba($primary-color, 90%); box-shadow: 0 0 3px 1px rgba(var(--primary-color), 90%);
} }
&.btn-success:focus { &.btn-success:focus {
border-color: $primary-color; border-color: var(--primary-color);
box-shadow: 0 0 3px 1px rgba($primary-color, 90%); box-shadow: 0 0 3px 1px rgba(var(--primary-color), 90%);
}
&.btn-primary {
background: var(--primary-color);
border-color: var(--primary-color-dark);
&:hover {
background: var(--primary-color-dark);
border-color: var(--primary-color-dark);
}
} }
} }
@ -435,7 +502,7 @@ code.sql {
} }
.sql-hl-keyword { .sql-hl-keyword {
color: $primary-color; color: var(--primary-color);
} }
.sql-hl-function { .sql-hl-function {
@ -456,4 +523,4 @@ code.sql {
.sql-hl-bracket { .sql-hl-bracket {
color: darkorchid; color: darkorchid;
} }

View File

@ -33,12 +33,41 @@
.menu-item a { .menu-item a {
&:hover { &:hover {
color: $primary-color; color: var(--primary-color);
background: $bg-color-gray; background: $bg-color-gray;
} }
} }
} }
.tab {
.tab-item {
a {
color: $body-font-color-dark;
opacity: .7;
&:hover {
color: $body-font-color-dark;
opacity: 1;
}
}
&.active {
a {
color: $body-font-color-dark;
opacity: 1;
}
.tab-link {
border-color: transparent;
}
&::after {
width: 100%;
}
}
}
}
.btn { .btn {
&.btn-link { &.btn-link {
color: rgba($body-font-color-dark, 0.8); color: rgba($body-font-color-dark, 0.8);
@ -67,7 +96,7 @@
} }
&.active { &.active {
background-color: $primary-color; background-color: var(--primary-color);
} }
} }
@ -124,7 +153,7 @@
} }
.form-select:not([multiple], [size]):focus { .form-select:not([multiple], [size]):focus {
border-color: $primary-color; border-color: var(--primary-color);
} }
.select { .select {
@ -432,7 +461,7 @@
.settingbar-element { .settingbar-element {
.settingbar-element-icon { .settingbar-element-icon {
&.badge-update::after { &.badge-update::after {
background: $primary-color; background: var(--primary-color);
} }
} }
} }
@ -447,7 +476,7 @@
} }
#footer { #footer {
background: $primary-color; background: var(--primary-color);
box-shadow: 0 0 1px 0 #000; box-shadow: 0 0 1px 0 #000;
.footer-elements { .footer-elements {

View File

@ -12,6 +12,14 @@
} }
} }
.form-select,
.form-input,
.form-select:not([multiple], [size]),
.form-checkbox .form-icon,
.form-radio .form-icon {
background-color: #f5f5f5;
}
.form-input:disabled, .form-input:disabled,
.form-input.disabled, .form-input.disabled,
.form-select:disabled, .form-select:disabled,
@ -44,7 +52,43 @@
} }
} }
.tab {
border-bottom: 0.05rem solid #c5c5c5;
.tab-item {
a {
color: $body-font-color;
opacity: .7;
&:hover {
color: $body-font-color;
opacity: 1;
}
}
&.active {
a {
color: $body-font-color;
opacity: 1;
}
.tab-link {
border-color: transparent;
}
&::after {
width: 100%;
}
}
}
}
.btn { .btn {
&.btn-clear:focus, &.btn-clear:hover {
background: #e0e0e0;
opacity: 0.95;
}
&.btn-link { &.btn-link {
color: rgba($body-font-color, 0.8); color: rgba($body-font-color, 0.8);
@ -72,7 +116,7 @@
} }
&.active { &.active {
background-color: $primary-color; background-color: var(--primary-color);
} }
} }
} }
@ -181,7 +225,7 @@
.settingbar-element { .settingbar-element {
.settingbar-element-icon { .settingbar-element-icon {
&.badge-update::after { &.badge-update::after {
background: $primary-color; background: var(--primary-color);
} }
} }
} }
@ -230,6 +274,10 @@
.workspace-tabs { .workspace-tabs {
.tab-block { .tab-block {
.tab-item { .tab-item {
> a {
color: $body-font-color;
}
&.tools-dropdown { &.tools-dropdown {
background-color: $body-bg; background-color: $body-bg;
} }
@ -241,19 +289,25 @@
.workspace-query-results { .workspace-query-results {
.table { .table {
.th { .th {
background: $body-bg; background: #D8D8D8;
border-color: lighten($bg-color-light-gray, 2%); border-color: transparent;
border-radius: 0;
}
.th:first-child {
border-left: 2px solid transparent;
} }
.tr { .tr {
background-color: lighten($bg-color-light-gray, 2%); background-color: lighten($bg-color-light-gray, 2%);
.td:first-child { .td:first-child {
border-left: 2px solid $body-bg; border-left: 2px solid #0000001f;
} }
.td { .td {
border-color: $body-bg; border-color: #0000001f;
border-radius: 1px;
&:focus, &:focus,
&.selected { &.selected {
@ -272,7 +326,7 @@
.connection-panel { .connection-panel {
.panel { .panel {
background: rgba($bg-color-light-gray, 100%); background: #e0e0e0;
} }
} }
@ -343,7 +397,7 @@
} }
#footer { #footer {
background: $primary-color; background: var(--primary-color);
box-shadow: 0 0 1px 0 #000; box-shadow: 0 0 1px 0 #000;
.footer-elements { .footer-elements {

View File

@ -37,6 +37,7 @@ export interface WorkspaceTab {
isChanged?: boolean; isChanged?: boolean;
content?: string; content?: string;
autorun?: boolean; autorun?: boolean;
filePath?: string;
} }
export interface WorkspaceStructure { export interface WorkspaceStructure {
@ -492,7 +493,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
} }
: workspace); : workspace);
}, },
_addTab ({ uid, tab, content, type, autorun, schema, database, elementName, elementType }: WorkspaceTab) { _addTab ({ uid, tab, content, type, autorun, schema, database, elementName, elementType, filePath }: WorkspaceTab) {
if (type === 'query') if (type === 'query')
tabIndex[uid] = tabIndex[uid] ? ++tabIndex[uid] : 1; tabIndex[uid] = tabIndex[uid] ? ++tabIndex[uid] : 1;
@ -506,7 +507,8 @@ export const useWorkspacesStore = defineStore('workspaces', {
elementName, elementName,
elementType, elementType,
content: content || '', content: content || '',
autorun: !!autorun autorun: !!autorun,
filePath: filePath || ''
}; };
this.workspaces = (this.workspaces as Workspace[]).map(workspace => { this.workspaces = (this.workspaces as Workspace[]).map(workspace => {
@ -522,14 +524,23 @@ export const useWorkspacesStore = defineStore('workspaces', {
persistentStore.set(uid, (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid).tabs); persistentStore.set(uid, (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid).tabs);
}, },
_replaceTab ({ uid, tab: tUid, type, schema, content, elementName, elementType }: WorkspaceTab) { _replaceTab ({ uid, tab: tUid, type, schema, content, elementName, elementType, filePath }: WorkspaceTab) {
this.workspaces = (this.workspaces as Workspace[]).map(workspace => { this.workspaces = (this.workspaces as Workspace[]).map(workspace => {
if (workspace.uid === uid) { if (workspace.uid === uid) {
return { return {
...workspace, ...workspace,
tabs: workspace.tabs.map(tab => { tabs: workspace.tabs.map(tab => {
if (tab.uid === tUid) if (tab.uid === tUid) {
return { ...tab, type, schema, content, elementName, elementType }; return {
...tab,
type,
schema,
content,
elementName,
elementType,
filePath
};
}
return tab; return tab;
}) })
@ -541,7 +552,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
persistentStore.set(uid, (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid).tabs); persistentStore.set(uid, (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid).tabs);
}, },
newTab ({ uid, content, type, autorun, schema, elementName, elementType }: WorkspaceTab) { newTab ({ uid, content, type, autorun, schema, elementName, elementType, filePath }: WorkspaceTab) {
let tabUid; let tabUid;
const workspaceTabs = (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid); const workspaceTabs = (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid);
@ -562,7 +573,8 @@ export const useWorkspacesStore = defineStore('workspaces', {
database: workspaceTabs.database, database: workspaceTabs.database,
schema, schema,
elementName, elementName,
elementType elementType,
filePath
}); });
break; break;
case 'temp-data': case 'temp-data':
@ -576,6 +588,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
tab.schema === schema && tab.schema === schema &&
tab.elementName === elementName && tab.elementName === elementName &&
tab.elementType === elementType && tab.elementType === elementType &&
tab.database === workspaceTabs.database &&
[type, type.replace('temp-', '')].includes(tab.type)) [type, type.replace('temp-', '')].includes(tab.type))
: false; : false;
@ -594,21 +607,52 @@ export const useWorkspacesStore = defineStore('workspaces', {
type: tab.type.replace('temp-', ''), type: tab.type.replace('temp-', ''),
schema: tab.schema, schema: tab.schema,
elementName: tab.elementName, elementName: tab.elementName,
elementType: tab.elementType elementType: tab.elementType,
filePath: tab.filePath
}); });
tabUid = uidGen('T'); tabUid = uidGen('T');
this._addTab({ uid, tab: tabUid, content, type, autorun, database: workspaceTabs.database, schema, elementName, elementType }); this._addTab({
uid,
tab: tabUid,
content,
type,
autorun,
database: workspaceTabs.database,
schema,
elementName,
elementType,
filePath
});
} }
else { else {
this._replaceTab({ uid, tab: tab.uid, type, schema, elementName, elementType }); this._replaceTab({
uid,
tab: tab.uid,
type,
schema,
elementName,
elementType,
filePath
});
tabUid = tab.uid; tabUid = tab.uid;
} }
} }
} }
else { else {
tabUid = uidGen('T'); tabUid = uidGen('T');
this._addTab({ uid, tab: tabUid, content, type, autorun, database: workspaceTabs.database, schema, elementName, elementType }); this._addTab({
uid,
tab: tabUid,
content,
type,
autorun,
database: workspaceTabs.database,
schema,
elementName,
elementType,
filePath
});
} }
} }
} }
@ -625,6 +669,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
tab.schema === schema && tab.schema === schema &&
tab.elementName === elementName && tab.elementName === elementName &&
tab.elementType === elementType && tab.elementType === elementType &&
tab.database === workspaceTabs.database &&
[`temp-${type}`, type].includes(tab.type)) [`temp-${type}`, type].includes(tab.type))
: false; : false;
@ -635,7 +680,8 @@ export const useWorkspacesStore = defineStore('workspaces', {
database: workspaceTabs.database, database: workspaceTabs.database,
schema, schema,
elementName, elementName,
elementType elementType,
filePath
}); });
tabUid = existentTab.uid; tabUid = existentTab.uid;
} }
@ -649,7 +695,8 @@ export const useWorkspacesStore = defineStore('workspaces', {
database: workspaceTabs.database, database: workspaceTabs.database,
schema, schema,
elementName, elementName,
elementType elementType,
filePath
}); });
} }
} }
@ -664,7 +711,8 @@ export const useWorkspacesStore = defineStore('workspaces', {
database: workspaceTabs.database, database: workspaceTabs.database,
schema, schema,
elementName, elementName,
elementType elementType,
filePath
}); });
break; break;
} }
@ -687,8 +735,8 @@ export const useWorkspacesStore = defineStore('workspaces', {
this.selectTab({ uid, tab: workspace.tabs[workspace.tabs.length - 1].uid }); this.selectTab({ uid, tab: workspace.tabs[workspace.tabs.length - 1].uid });
} }
}, },
updateTabContent ({ uid, tab, type, schema, content }: WorkspaceTab) { updateTabContent ({ uid, tab, type, schema, content, elementName, filePath }: WorkspaceTab) {
this._replaceTab({ uid, tab, type, schema, content }); this._replaceTab({ uid, tab, type, schema, content, elementName, filePath });
}, },
renameTabs ({ uid, schema, elementName, elementNewName }: WorkspaceTab) { renameTabs ({ uid, schema, elementName, elementNewName }: WorkspaceTab) {
this.workspaces = (this.workspaces as Workspace[]).map(workspace => { this.workspaces = (this.workspaces as Workspace[]).map(workspace => {