1
1
mirror of https://github.com/Fabio286/antares.git synced 2025-06-05 21:59:22 +02:00

Compare commits

..

89 Commits

Author SHA1 Message Date
6c9792f512 chore: update FUNDING.yml 2025-04-24 10:08:38 +02:00
8f84892f07 fix: handle id type correctly in update-table-cell where clause, fixes #974 2025-04-14 09:10:05 +02:00
a95a76480c chore(release): 0.7.35-beta.0 2025-04-04 16:43:07 +02:00
1d1be55d3d fix: custom connection icon disappears during connection, fixes #939 2025-03-31 14:24:34 +02:00
d912faa850 fix: improved handling of query comments, fixes #963 and #580 2025-03-31 13:57:41 +02:00
ba63b049a3 fix: escape SQL parameters in update and delete for where clauses, fixes #964 2025-03-31 13:03:49 +02:00
fcd7e404ba Merge branch 'master' of https://github.com/antares-sql/antares into develop 2025-03-31 11:00:33 +02:00
8eb4d2e114 chore: update GitHub Actions runner to Ubuntu 22.04 2025-03-31 11:00:01 +02:00
acea18e6f0 perf(translation): update Spanish translations, closes #962 2025-03-31 10:49:14 +02:00
973b0fc4be fix: use custom elements wrapper for foreign column and description in query 2025-03-28 12:50:01 +01:00
4adbc575c2 chore(release): 0.7.34 2025-02-14 20:33:55 +01:00
eb706c3e51 fix: issue with some SSH connections, definitely 2025-02-14 20:30:47 +01:00
971df3a989 chore(release): 0.7.33 2025-02-14 18:03:57 +01:00
3129bf4baa fix: issue with some SSH connections, fixes #947 2025-02-14 17:58:49 +01:00
c6d67cef01 chore(release): 0.7.32 2025-02-14 09:12:05 +01:00
1d7053ce03 fix: black background with light theme, fixes #945 2025-02-14 09:08:45 +01:00
41e797f9e2 fix(PostgreSQL): error with materialized view tabs 2025-02-13 18:01:19 +01:00
704f70819b fix: improve error handling in SSH connection 2025-02-12 18:10:27 +01:00
49a3589536 fix: enhance SVG support in connection customization, fixes #939 2025-02-12 18:09:11 +01:00
49ada059bc chore(release): 0.7.31 2025-02-11 18:22:37 +01:00
8003d3eb1e chore(release): 0.7.31-beta.5 2025-02-09 10:19:09 +01:00
48cfa67889 perf: improve button styles of notes 2025-02-09 10:17:38 +01:00
9cda38e9d1 perf(MySQL): long loading in table settings when no checks present 2025-02-06 18:25:27 +01:00
72f8d4249f fix: improve BLOB primary fields management, fixes #938 2025-02-06 13:28:12 +01:00
0f93d70417 fix: unable to delete rows from context menu 2025-02-06 13:27:02 +01:00
580bef76ba fix: replace 'this.addNotification' with 'addNotification' in useResultTables.ts 2025-02-06 13:25:57 +01:00
7595e89223 fix(devtoolsInstaller): improve file path handling and increase chromium version 2025-02-06 12:50:45 +01:00
7af44d4a2c refactor: add ciaplu for pattern matching in language detection and MIME type resolution 2025-02-05 15:34:01 +01:00
0479e5307c fix(Linux): restored AppImage auto updates 2025-02-03 18:14:38 +01:00
d03c1b90ce chore(release): 0.7.31-beta.4 2025-01-31 18:06:58 +01:00
d34e56a517 fix(Linux): missing window management icons 2025-01-31 18:06:16 +01:00
0f35814ca0 chore(release): 0.7.31-beta.3 2025-01-31 17:54:25 +01:00
96ae09feca feat: implement a better query splitter for SQL queries, fixes #926 2025-01-31 17:28:58 +01:00
e3b30359bf refactor: disable auto opening dev tools in development mode 2025-01-31 13:33:42 +01:00
f3c3284fd1 ci: add GitHub Actions workflow for creating Windows APPX artifacts 2025-01-31 13:32:44 +01:00
27387f18a1 fix(MySQL): adjust utf8mb3 encoding to resolve compatibility issue, fixes #646 2025-01-31 13:32:06 +01:00
8e54f7b801 feat(Linux): update title bar for better Linux experience 2025-01-30 18:01:56 +01:00
70aae2f194 chore(release): 0.7.31-beta.2 2025-01-30 16:16:14 +01:00
592d7b3517 feat: add developer tools and refresh buttons to console in development mode 2025-01-23 18:10:37 +01:00
2b743a2c79 Merge pull request #925 from dyaskur/develop
fix: fail to fill cell to datetime column(Postgre) fixes #924
2025-01-23 09:17:40 +01:00
️Yaskur Dyas⚔⚔️⚔
e493db5112 fix: reorder condition when format the update data 2025-01-23 08:16:52 +07:00
️Yaskur Dyas⚔⚔️⚔
d3d7ab38c0 fix: fail to fill cell to datetime column(Postgre) fixes #924 2025-01-23 06:32:22 +07:00
7a66c11868 chore(release): 0.7.31-beta.1 2025-01-22 10:56:01 +01:00
8544bb5378 refactor: reorder import statements in sqlUtils.ts for consistency 2025-01-22 10:53:58 +01:00
6709a75298 Merge pull request #921 from curiouslad/develop
Zoom in/out and fullscreen shortcuts
2025-01-20 09:07:08 +01:00
f25f6659d5 refactor: enhancement of new shortcuts implementation 2025-01-17 13:45:46 +01:00
8d0ff4953e Merge pull request #922 from jimcat8/cn_trans
Update localization
2025-01-16 09:45:33 +01:00
tianci
fbe28f0ff0 Update localization 2025-01-16 12:22:07 +08:00
0d8bcf5cd6 Merge pull request #920 from dyaskur/develop
fix: Cannot update column value with composite primary key and JSON column #916
2025-01-15 09:12:26 +01:00
mladen
47ac729d2f feat: zoom in/out and fullscreen shortcuts 2025-01-14 05:24:33 +01:00
️Yaskur Dyas⚔⚔️⚔
450c4c47f3 refactor: improve update cell condition and move whereJson formatter to sqlUtils 2025-01-14 05:44:12 +07:00
️Yaskur Dyas⚔⚔️⚔
110dcd335a fix: cannot update on JSON column in MariaDB and PostgreSQL 2025-01-14 05:15:46 +07:00
️Yaskur Dyas⚔⚔️⚔
0029967619 fix: cannot update column value with composite primary key and JSON column, fixes #916 2025-01-14 04:44:29 +07:00
34848e8dc3 Merge branch 'develop' of https://github.com/antares-sql/antares into develop 2025-01-13 10:23:23 +01:00
c32add76e8 Merge pull request #919 from dyaskur/develop
fix: fail to duplicate JSON row
2025-01-13 10:23:17 +01:00
️Yaskur Dyas⚔⚔️⚔
507dc7d55b fix: fail to duplicate JSON row 2025-01-12 10:30:31 +07:00
4a2b5926f4 fix: saved connections lost opening a second window after first app run 2025-01-10 18:20:36 +01:00
ed90b12a7b Merge pull request #918 from antares-sql/all-contributors/add-JoseGonzalez84
docs: add JoseGonzalez84 as a contributor for translation
2025-01-10 08:46:59 +01:00
allcontributors[bot]
00ce76a12e docs: update .all-contributorsrc [skip ci] 2025-01-10 07:46:47 +00:00
allcontributors[bot]
77b3a8a354 docs: update README.md [skip ci] 2025-01-10 07:46:46 +00:00
d3ae45ec94 perf(translation): update spanish translation 2025-01-10 08:45:58 +01:00
ad4478a822 Merge pull request #917 from antares-sql/all-contributors/add-salvymc
docs: add salvymc as a contributor for code
2025-01-09 16:50:59 +01:00
allcontributors[bot]
ba5dd9ff15 docs: update .all-contributorsrc [skip ci] 2025-01-09 15:50:45 +00:00
allcontributors[bot]
5aab824fe9 docs: update README.md [skip ci] 2025-01-09 15:50:44 +00:00
87ab58c50f Merge pull request #912 from salvymc/patch-1
Update WorkspaceExploreBarSchema.vue - Changed search to not be case sensitive
2025-01-09 16:48:37 +01:00
e986f287c6 chore(release): 0.7.31-beta.0 2025-01-06 11:11:23 +01:00
Salvatore Forino
39a30e48dd Update WorkspaceExploreBarSchema.vue
Changed search to not be case sensitive
2025-01-03 13:09:15 +01:00
46165d2f4f Merge pull request #910 from antares-sql/all-contributors/add-r4f4dev
docs: add r4f4dev as a contributor for translation
2024-12-28 23:46:00 +01:00
allcontributors[bot]
d0e56e4eb6 docs: update .all-contributorsrc [skip ci] 2024-12-28 22:45:32 +00:00
allcontributors[bot]
c803c072d1 docs: update README.md [skip ci] 2024-12-28 22:45:31 +00:00
232211811b Merge pull request #909 from r4f4dev/feat/locale-uzbek
feat(language): add uzbek language support
2024-12-28 15:22:23 +01:00
r4f4dev
fb9c258cc1 feat(language): add uzbek language support 2024-12-28 19:15:41 +05:00
8de99dae7b fix: prevent delete confirmation modal from triggering on non-delete key presses, fixes #906 2024-12-24 11:03:27 +01:00
2bd69c6263 refactor: removed software-side sorting logic, fixes #904 2024-12-24 09:54:47 +01:00
4d1a81033d chore(release): 0.7.30 2024-12-04 18:14:31 +01:00
5887eea2ed Merge branch 'master' of https://github.com/antares-sql/antares 2024-12-04 18:14:11 +01:00
6c69583c90 Merge pull request #899 from antares-sql/all-contributors/add-carvalhods
docs: add carvalhods as a contributor for platform
2024-11-22 09:16:26 +01:00
allcontributors[bot]
03461522b7 docs: update .all-contributorsrc [skip ci] 2024-11-22 08:13:36 +00:00
allcontributors[bot]
11807e3bb6 docs: update README.md [skip ci] 2024-11-22 08:13:35 +00:00
a54b8d719c fix: issue saving queries as file 2024-11-21 13:28:33 +01:00
d1f68da495 chore(release): 0.7.30-beta.1 2024-11-15 14:27:20 +01:00
d666281daa Update README.md 2024-11-11 15:50:55 +01:00
481ae842dd Merge pull request #894 from leaked/master
Update README.md
2024-11-11 15:49:43 +01:00
Mohsen Nasiri
acf5d459e2 Update README.md 2024-11-10 02:47:52 +03:30
2ee9cfcf0b fix: missing support check for table check features 2024-11-08 18:12:02 +01:00
f0d312fb59 perf(PostgreSQL): improved support of connection strings, closes #893 2024-11-08 18:09:37 +01:00
24605d01e1 Merge pull request #887 from antares-sql/all-contributors/add-SawGoD
docs: add SawGoD as a contributor for translation
2024-10-22 15:53:15 +02:00
allcontributors[bot]
f639bc7983 docs: update .all-contributorsrc [skip ci] 2024-10-22 13:26:01 +00:00
allcontributors[bot]
5e51997e5b docs: update README.md [skip ci] 2024-10-22 13:26:00 +00:00
70 changed files with 2075 additions and 450 deletions

View File

@@ -320,6 +320,51 @@
"contributions": [ "contributions": [
"translation" "translation"
] ]
},
{
"login": "SawGoD",
"name": "Nikita Karelikov",
"avatar_url": "https://avatars.githubusercontent.com/u/67802757?v=4",
"profile": "http://telegram.dog/SawGoD",
"contributions": [
"translation"
]
},
{
"login": "carvalhods",
"name": "David Carvalho",
"avatar_url": "https://avatars.githubusercontent.com/u/6569255?v=4",
"profile": "https://github.com/carvalhods",
"contributions": [
"platform"
]
},
{
"login": "r4f4dev",
"name": "r4f4dev",
"avatar_url": "https://avatars.githubusercontent.com/u/65920592?v=4",
"profile": "https://github.com/r4f4dev",
"contributions": [
"translation"
]
},
{
"login": "salvymc",
"name": "Salvatore Forino",
"avatar_url": "https://avatars.githubusercontent.com/u/10051897?v=4",
"profile": "https://github.com/salvymc",
"contributions": [
"code"
]
},
{
"login": "JoseGonzalez84",
"name": "José González",
"avatar_url": "https://avatars.githubusercontent.com/u/16820141?v=4",
"profile": "https://gadev.com.es/",
"contributions": [
"translation"
]
} }
], ],
"contributorsPerLine": 7, "contributorsPerLine": 7,

View File

@@ -2,4 +2,5 @@ node_modules
assets assets
out out
dist dist
build build
misc

4
.github/FUNDING.yml vendored
View File

@@ -1,6 +1,6 @@
# These are supported funding model platforms # These are supported funding model platforms
github: [antares-sql,fabio286] github: # [antares-sql,fabio286]
patreon: #fabio286 patreon: #fabio286
open_collective: # Replace with a single Open Collective username open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username ko_fi: # Replace with a single Ko-fi username
@@ -9,4 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
liberapay: # Replace with a single Liberapay username liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username otechie: # Replace with a single Otechie username
custom: ['https://paypal.me/fabiodistasio'] custom: # ['https://paypal.me/fabiodistasio']

View File

@@ -5,7 +5,7 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-22.04
steps: steps:
- name: Check out Git repository - name: Check out Git repository
uses: actions/checkout@v4 uses: actions/checkout@v4

View File

@@ -0,0 +1,32 @@
name: Create artifact [WINDOWS APPX]
on:
workflow_dispatch: {}
jobs:
build:
runs-on: windows-2022
steps:
- name: Check out Git repository
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm i
- name: "Build"
run: npm run build:appx
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: windows-build
retention-days: 3
path: |
build
!build/*-unpacked
!build/.icon-ico

View File

@@ -2,6 +2,147 @@
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.35-beta.0](https://github.com/antares-sql/antares/compare/v0.7.34...v0.7.35-beta.0) (2025-04-04)
### Bug Fixes
* custom connection icon disappears during connection, fixes [#939](https://github.com/antares-sql/antares/issues/939) ([1d1be55](https://github.com/antares-sql/antares/commit/1d1be55d3d4ea621364c37e75de616046371feeb))
* escape SQL parameters in update and delete for where clauses, fixes [#964](https://github.com/antares-sql/antares/issues/964) ([ba63b04](https://github.com/antares-sql/antares/commit/ba63b049a3a059e77256141dc7b761efbbbf8c1e))
* improved handling of query comments, fixes [#963](https://github.com/antares-sql/antares/issues/963) and [#580](https://github.com/antares-sql/antares/issues/580) ([d912faa](https://github.com/antares-sql/antares/commit/d912faa85042219315c9c5658d7f20fda560af44))
* use custom elements wrapper for foreign column and description in query ([973b0fc](https://github.com/antares-sql/antares/commit/973b0fc4be1dac25757e430e4520d6fc2212f93b))
### Improvements
* **translation:** update Spanish translations, closes [#962](https://github.com/antares-sql/antares/issues/962) ([acea18e](https://github.com/antares-sql/antares/commit/acea18e6f061adab7e79d1249e0e68555a620db5))
### [0.7.34](https://github.com/antares-sql/antares/compare/v0.7.33...v0.7.34) (2025-02-14)
### Bug Fixes
* issue with some SSH connections, definitely ([eb706c3](https://github.com/antares-sql/antares/commit/eb706c3e51e9cb7577febd291a33594c0650a34a))
### [0.7.33](https://github.com/antares-sql/antares/compare/v0.7.32...v0.7.33) (2025-02-14)
### Bug Fixes
* issue with some SSH connections, fixes [#947](https://github.com/antares-sql/antares/issues/947) ([3129bf4](https://github.com/antares-sql/antares/commit/3129bf4baa5e72b1d79df986605fd5fad1dce291))
### [0.7.32](https://github.com/antares-sql/antares/compare/v0.7.31...v0.7.32) (2025-02-14)
### Bug Fixes
* black background with light theme, fixes [#945](https://github.com/antares-sql/antares/issues/945) ([1d7053c](https://github.com/antares-sql/antares/commit/1d7053ce032efec8377d9500f2e24618f6381ab4))
* enhance SVG support in connection customization, fixes [#939](https://github.com/antares-sql/antares/issues/939) ([49a3589](https://github.com/antares-sql/antares/commit/49a3589536d2e75a14125be7b874e29b60fb56c4))
* improve error handling in SSH connection ([704f708](https://github.com/antares-sql/antares/commit/704f70819b21a42194d8f68cf9b58ba337f1ada7))
* **PostgreSQL:** error with materialized view tabs ([41e797f](https://github.com/antares-sql/antares/commit/41e797f9e27db66370d3ae7750c057f708af76f9))
### [0.7.31](https://github.com/antares-sql/antares/compare/v0.7.31-beta.5...v0.7.31) (2025-02-11)
### [0.7.31-beta.5](https://github.com/antares-sql/antares/compare/v0.7.31-beta.4...v0.7.31-beta.5) (2025-02-09)
### Bug Fixes
* **devtoolsInstaller:** improve file path handling and increase chromium version ([7595e89](https://github.com/antares-sql/antares/commit/7595e892238d2a93c454e9c1f236915fb458eed1))
* improve BLOB primary fields management, fixes [#938](https://github.com/antares-sql/antares/issues/938) ([72f8d42](https://github.com/antares-sql/antares/commit/72f8d4249f7f587d3e92b46cf7709ddab42107d4))
* **Linux:** restored AppImage auto updates ([0479e53](https://github.com/antares-sql/antares/commit/0479e5307c9a9d5f791e1c61fa772d331f6f7f1f))
* replace 'this.addNotification' with 'addNotification' in useResultTables.ts ([580bef7](https://github.com/antares-sql/antares/commit/580bef76ba390fc85df0892265f31392b80301bd))
* unable to delete rows from context menu ([0f93d70](https://github.com/antares-sql/antares/commit/0f93d70417871f02f9f64f203f6654fa1bf2004b))
### Improvements
* improve button styles of notes ([48cfa67](https://github.com/antares-sql/antares/commit/48cfa67889bd83228c109b7966c4acea4e542fc6))
* **MySQL:** long loading in table settings when no checks present ([9cda38e](https://github.com/antares-sql/antares/commit/9cda38e9d10e3000473863560d8be8f426a5ed17))
### [0.7.31-beta.4](https://github.com/antares-sql/antares/compare/v0.7.31-beta.3...v0.7.31-beta.4) (2025-01-31)
### Bug Fixes
* **Linux:** missing window management icons ([d34e56a](https://github.com/antares-sql/antares/commit/d34e56a517784dea16a7a53bc2249072a3b96596))
### [0.7.31-beta.3](https://github.com/antares-sql/antares/compare/v0.7.31-beta.2...v0.7.31-beta.3) (2025-01-31)
### Features
* implement a better query splitter for SQL queries, fixes [#926](https://github.com/antares-sql/antares/issues/926) ([96ae09f](https://github.com/antares-sql/antares/commit/96ae09fecad0c1fc8926d5dcf64cc779abe5ed49))
* **Linux:** update title bar for better Linux experience ([8e54f7b](https://github.com/antares-sql/antares/commit/8e54f7b80135768a33934bc9336239dee38401a5))
### Bug Fixes
* **MySQL:** adjust utf8mb3 encoding to resolve compatibility issue, fixes [#646](https://github.com/antares-sql/antares/issues/646) ([27387f1](https://github.com/antares-sql/antares/commit/27387f18a107fc6c09afec5f85134496ce764355))
### [0.7.31-beta.2](https://github.com/antares-sql/antares/compare/v0.7.31-beta.1...v0.7.31-beta.2) (2025-01-30)
### Features
* add developer tools and refresh buttons to console in development mode ([592d7b3](https://github.com/antares-sql/antares/commit/592d7b35170f8437ebc15221c97985e889fccb40))
### Bug Fixes
* fail to fill cell to datetime column(Postgre) fixes [#924](https://github.com/antares-sql/antares/issues/924) ([d3d7ab3](https://github.com/antares-sql/antares/commit/d3d7ab38c029fc5ec23767c6c86c49a96e4e329c))
* reorder condition when format the update data ([e493db5](https://github.com/antares-sql/antares/commit/e493db5112478ff121e4e77f69c21747c5d2e032))
### [0.7.31-beta.1](https://github.com/antares-sql/antares/compare/v0.7.31-beta.0...v0.7.31-beta.1) (2025-01-22)
### Features
* zoom in/out and fullscreen shortcuts ([47ac729](https://github.com/antares-sql/antares/commit/47ac729d2f5cced2c503358f7d45a1795f232a20))
### Bug Fixes
* cannot update column value with composite primary key and JSON column, fixes [#916](https://github.com/antares-sql/antares/issues/916) ([0029967](https://github.com/antares-sql/antares/commit/002996761997444ff689bf2384dae64ccb9ef8f7))
* fail to duplicate JSON row ([507dc7d](https://github.com/antares-sql/antares/commit/507dc7d55b342240bf18fd58e6bc71709e8e33a0))
* saved connections lost opening a second window after first app run ([4a2b592](https://github.com/antares-sql/antares/commit/4a2b5926f4783d0b9b1e28485e9293a25ddd31f3))
### Improvements
* **translation:** update spanish translation ([d3ae45e](https://github.com/antares-sql/antares/commit/d3ae45ec94b3538e84ac3013b285034caea695cf))
### [0.7.31-beta.0](https://github.com/antares-sql/antares/compare/v0.7.30...v0.7.31-beta.0) (2025-01-06)
### Features
* **language:** add uzbek language support ([fb9c258](https://github.com/antares-sql/antares/commit/fb9c258cc10e4d85242ca533a66a95f4101d472c))
### Bug Fixes
* prevent delete confirmation modal from triggering on non-delete key presses, fixes [#906](https://github.com/antares-sql/antares/issues/906) ([8de99da](https://github.com/antares-sql/antares/commit/8de99dae7b6eb72bd6833c607d3c3a5db9508ebb))
### [0.7.30](https://github.com/antares-sql/antares/compare/v0.7.30-beta.1...v0.7.30) (2024-12-04)
### Bug Fixes
* issue saving queries as file ([a54b8d7](https://github.com/antares-sql/antares/commit/a54b8d719c6454500b885050c9ce6feaf7cfae1f))
### [0.7.30-beta.1](https://github.com/antares-sql/antares/compare/v0.7.30-beta.0...v0.7.30-beta.1) (2024-11-15)
### Bug Fixes
* missing support check for table check features ([2ee9cfc](https://github.com/antares-sql/antares/commit/2ee9cfcf0bbcf86e8a194d2eff78801300ce7cb3))
### Improvements
* **PostgreSQL:** improved support of connection strings, closes [#893](https://github.com/antares-sql/antares/issues/893) ([f0d312f](https://github.com/antares-sql/antares/commit/f0d312fb59fd98d6e4501bc407959b91eb0650f2))
### [0.7.30-beta.0](https://github.com/antares-sql/antares/compare/v0.7.29...v0.7.30-beta.0) (2024-10-25) ### [0.7.30-beta.0](https://github.com/antares-sql/antares/compare/v0.7.29...v0.7.30-beta.0) (2024-10-25)

View File

@@ -1,13 +1,13 @@
<!-- markdownlint-disable --> <!-- markdownlint-disable -->
<p align="center"> <p align="center">
<img width="800" src="https://raw.githubusercontent.com/Fabio286/antares/master/docs/gh-logo.png"> <img width="800" src="https://raw.githubusercontent.com/antares-sql/antares/master/docs/gh-logo.png">
</p> </p>
<!-- markdownlint-restore --> <!-- markdownlint-restore -->
# Antares SQL Client # Antares SQL Client
![GitHub package.json version](https://img.shields.io/github/package-json/v/fabio286/antares) ![GitHub](https://img.shields.io/github/license/fabio286/antares) ![Test e2e](https://github.com/antares-sql/antares/actions/workflows/test-e2e-win.yml/badge.svg?branch=develop) ![Mastodon Follow](https://img.shields.io/mastodon/follow/%20110860460902482117?domain=https%3A%2F%2Ffosstodon.org&style=social) [![Plant a Tree](https://raw.githubusercontent.com/Fabio286/treedom-badge/master/svg/plant-a-tree.svg)](https://www.treedom.net/en/user/fabio-di-stasio/event/antares-for-the-planet) ![GitHub package.json version](https://img.shields.io/github/package-json/v/antares-sql/antares) ![GitHub](https://img.shields.io/github/license/antares-sql/antares) ![Test e2e](https://github.com/antares-sql/antares/actions/workflows/test-e2e-win.yml/badge.svg?branch=develop) ![Mastodon Follow](https://img.shields.io/mastodon/follow/%20110860460902482117?domain=https%3A%2F%2Ffosstodon.org&style=social) [![Plant a Tree](https://raw.githubusercontent.com/Fabio286/treedom-badge/master/svg/plant-a-tree.svg)](https://www.treedom.net/en/user/fabio-di-stasio/event/antares-for-the-planet)
Antares is an SQL client based on [Electron.js](https://github.com/electron/electron) and [Vue.js](https://github.com/vuejs/vue) that aims to become a useful tool, especially for developers. Antares is an SQL client based on [Electron.js](https://github.com/electron/electron) and [Vue.js](https://github.com/vuejs/vue) that aims to become a useful tool, especially for developers.
Our target is to support as many databases as possible, and all major operating systems, including the ARM versions. Our target is to support as many databases as possible, and all major operating systems, including the ARM versions.
@@ -16,7 +16,7 @@ Our target is to support as many databases as possible, and all major operating
However, there are all the features necessary to have a pleasant database management experience, so give it a chance and send us your feedback, we would really appreciate it. However, there are all the features necessary to have a pleasant database management experience, so give it a chance and send us your feedback, we would really appreciate it.
We are actively working on it, hoping to provide new cool features, improvements and fixes as soon as possible. We are actively working on it, hoping to provide new cool features, improvements and fixes as soon as possible.
🔗 If you are curious to try Antares you can download and install the [latest release](https://github.com/Fabio286/antares/releases/latest). 🔗 If you are curious to try Antares you can download and install the [latest release](https://github.com/antares-sql/antares/releases/latest).
👁 To stay tuned for new releases follow Antares SQL on [Mastodon](https://fosstodon.org/@AntaresSQL). 👁 To stay tuned for new releases follow Antares SQL on [Mastodon](https://fosstodon.org/@AntaresSQL).
🌟 Don't forget to **leave a star** if you appreciate this project. 🌟 Don't forget to **leave a star** if you appreciate this project.
@@ -60,7 +60,7 @@ On Linux you can simply download and run the `.AppImage` distribution, install f
### Windows ### Windows
On Windows you can choose between downloading the app from Microsoft Store or downloading the `.exe` from our [website](https://antares-sql.app/downloads) or [this github repo](https://github.com/Fabio286/antares/releases/latest). Distributions that are not from Microsoft Store are not signed with a certificate, so to install you need to click on "More info" and then "Run anyway" on SmartScreen prompt. On Windows you can choose between downloading the app from Microsoft Store or downloading the `.exe` from our [website](https://antares-sql.app/downloads) or [this github repo](https://github.com/antares-sql/antares/releases/latest). Distributions that are not from Microsoft Store are not signed with a certificate, so to install you need to click on "More info" and then "Run anyway" on SmartScreen prompt.
### MacOS ### MacOS
@@ -99,8 +99,8 @@ On macOS you can run `.dmg` distribution following [this guide](https://support.
## How to contribute ## How to contribute
- 🌍 [Translate Antares](https://github.com/Fabio286/antares/wiki/Translate-Antares) - 🌍 [Translate Antares](https://github.com/antares-sql/antares/wiki/Translate-Antares)
- 📖 [Contributors Guide](https://github.com/Fabio286/antares/wiki/Contributors-Guide) - 📖 [Contributors Guide](https://github.com/antares-sql/antares/wiki/Contributors-Guide)
- 🚧 [Project Board](https://github.com/orgs/antares-sql/projects/3/views/2) - 🚧 [Project Board](https://github.com/orgs/antares-sql/projects/3/views/2)
## Contributors ✨ ## Contributors ✨
@@ -155,6 +155,13 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zwei-c"><img src="https://avatars.githubusercontent.com/u/55912811?v=4?s=100" width="100px;" alt="CHANG, CHIH WEI"/><br /><sub><b>CHANG, CHIH WEI</b></sub></a><br /><a href="#translation-zwei-c" title="Translation">🌍</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/zwei-c"><img src="https://avatars.githubusercontent.com/u/55912811?v=4?s=100" width="100px;" alt="CHANG, CHIH WEI"/><br /><sub><b>CHANG, CHIH WEI</b></sub></a><br /><a href="#translation-zwei-c" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mirrorb"><img src="https://avatars.githubusercontent.com/u/34116207?v=4?s=100" width="100px;" alt="GaoChun"/><br /><sub><b>GaoChun</b></sub></a><br /><a href="https://github.com/antares-sql/antares/commits?author=mirrorb" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/mirrorb"><img src="https://avatars.githubusercontent.com/u/34116207?v=4?s=100" width="100px;" alt="GaoChun"/><br /><sub><b>GaoChun</b></sub></a><br /><a href="https://github.com/antares-sql/antares/commits?author=mirrorb" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LeviEyal"><img src="https://avatars.githubusercontent.com/u/48846533?v=4?s=100" width="100px;" alt="Eyal Levi"/><br /><sub><b>Eyal Levi</b></sub></a><br /><a href="#translation-LeviEyal" title="Translation">🌍</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/LeviEyal"><img src="https://avatars.githubusercontent.com/u/48846533?v=4?s=100" width="100px;" alt="Eyal Levi"/><br /><sub><b>Eyal Levi</b></sub></a><br /><a href="#translation-LeviEyal" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://telegram.dog/SawGoD"><img src="https://avatars.githubusercontent.com/u/67802757?v=4?s=100" width="100px;" alt="Nikita Karelikov"/><br /><sub><b>Nikita Karelikov</b></sub></a><br /><a href="#translation-SawGoD" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/carvalhods"><img src="https://avatars.githubusercontent.com/u/6569255?v=4?s=100" width="100px;" alt="David Carvalho"/><br /><sub><b>David Carvalho</b></sub></a><br /><a href="#platform-carvalhods" title="Packaging/porting to new platform">📦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/r4f4dev"><img src="https://avatars.githubusercontent.com/u/65920592?v=4?s=100" width="100px;" alt="r4f4dev"/><br /><sub><b>r4f4dev</b></sub></a><br /><a href="#translation-r4f4dev" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/salvymc"><img src="https://avatars.githubusercontent.com/u/10051897?v=4?s=100" width="100px;" alt="Salvatore Forino"/><br /><sub><b>Salvatore Forino</b></sub></a><br /><a href="https://github.com/antares-sql/antares/commits?author=salvymc" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://gadev.com.es/"><img src="https://avatars.githubusercontent.com/u/16820141?v=4?s=100" width="100px;" alt="José González"/><br /><sub><b>José González</b></sub></a><br /><a href="#translation-JoseGonzalez84" title="Translation">🌍</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

24
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "antares", "name": "antares",
"version": "0.7.30-beta.0", "version": "0.7.35-beta.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "antares", "name": "antares",
"version": "0.7.30-beta.0", "version": "0.7.35-beta.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -22,6 +22,7 @@
"babel-loader": "~8.2.3", "babel-loader": "~8.2.3",
"better-sqlite3": "~10.0.0", "better-sqlite3": "~10.0.0",
"chalk": "~4.1.2", "chalk": "~4.1.2",
"ciaplu": "^2.2.0",
"cpu-features": "^0.0.10", "cpu-features": "^0.0.10",
"cross-env": "~7.0.2", "cross-env": "~7.0.2",
"css-loader": "~6.5.0", "css-loader": "~6.5.0",
@@ -42,7 +43,6 @@
"node-firebird": "~1.1.8", "node-firebird": "~1.1.8",
"node-loader": "~2.0.0", "node-loader": "~2.0.0",
"pg": "~8.11.5", "pg": "~8.11.5",
"pg-connection-string": "~2.5.0",
"pg-query-stream": "~4.2.3", "pg-query-stream": "~4.2.3",
"pgsql-ast-parser": "~7.2.1", "pgsql-ast-parser": "~7.2.1",
"pinia": "~2.1.7", "pinia": "~2.1.7",
@@ -5609,6 +5609,12 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/ciaplu": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/ciaplu/-/ciaplu-2.2.0.tgz",
"integrity": "sha512-7y8s0GMFpIKqX2kwiOEYbaX3P9tPIbX4x41uw8GZjkZ+y0QZrpY3PFjE2Ed6BOeFxcCWi7b85MYHeiRrVrlSOQ==",
"license": "MIT"
},
"node_modules/clean-css": { "node_modules/clean-css": {
"version": "5.3.2", "version": "5.3.2",
"license": "MIT", "license": "MIT",
@@ -12142,10 +12148,6 @@
"license": "MIT", "license": "MIT",
"optional": true "optional": true
}, },
"node_modules/pg-connection-string": {
"version": "2.5.0",
"license": "MIT"
},
"node_modules/pg-cursor": { "node_modules/pg-cursor": {
"version": "2.10.3", "version": "2.10.3",
"license": "MIT", "license": "MIT",
@@ -20350,6 +20352,11 @@
"version": "3.9.0", "version": "3.9.0",
"dev": true "dev": true
}, },
"ciaplu": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/ciaplu/-/ciaplu-2.2.0.tgz",
"integrity": "sha512-7y8s0GMFpIKqX2kwiOEYbaX3P9tPIbX4x41uw8GZjkZ+y0QZrpY3PFjE2Ed6BOeFxcCWi7b85MYHeiRrVrlSOQ=="
},
"clean-css": { "clean-css": {
"version": "5.3.2", "version": "5.3.2",
"requires": { "requires": {
@@ -24507,9 +24514,6 @@
"version": "1.1.1", "version": "1.1.1",
"optional": true "optional": true
}, },
"pg-connection-string": {
"version": "2.5.0"
},
"pg-cursor": { "pg-cursor": {
"version": "2.10.3", "version": "2.10.3",
"requires": {} "requires": {}

View File

@@ -1,7 +1,7 @@
{ {
"name": "antares", "name": "antares",
"productName": "Antares", "productName": "Antares",
"version": "0.7.30-beta.0", "version": "0.7.35-beta.0",
"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",
@@ -131,6 +131,7 @@
"babel-loader": "~8.2.3", "babel-loader": "~8.2.3",
"better-sqlite3": "~10.0.0", "better-sqlite3": "~10.0.0",
"chalk": "~4.1.2", "chalk": "~4.1.2",
"ciaplu": "^2.2.0",
"cpu-features": "^0.0.10", "cpu-features": "^0.0.10",
"cross-env": "~7.0.2", "cross-env": "~7.0.2",
"css-loader": "~6.5.0", "css-loader": "~6.5.0",
@@ -151,7 +152,6 @@
"node-firebird": "~1.1.8", "node-firebird": "~1.1.8",
"node-loader": "~2.0.0", "node-loader": "~2.0.0",
"pg": "~8.11.5", "pg": "~8.11.5",
"pg-connection-string": "~2.5.0",
"pg-query-stream": "~4.2.3", "pg-query-stream": "~4.2.3",
"pgsql-ast-parser": "~7.2.1", "pgsql-ast-parser": "~7.2.1",
"pinia": "~2.1.7", "pinia": "~2.1.7",

View File

@@ -1,5 +1,5 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck // @ts-check
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const https = require('https'); const https = require('https');
@@ -7,13 +7,18 @@ const unzip = require('unzip-crx-3');
const { antares } = require('../package.json'); const { antares } = require('../package.json');
const extensionID = antares.devtoolsId; const extensionID = antares.devtoolsId;
const chromiumVersion = '124';
const destFolder = path.resolve(__dirname, `../misc/${extensionID}`); const destFolder = path.resolve(__dirname, `../misc/${extensionID}`);
const filePath = path.resolve(__dirname, `${destFolder}${extensionID}.crx`); const filePath = path.resolve(__dirname, `${destFolder}/${extensionID}.crx`);
const fileUrl = `https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&x=id%3D${extensionID}%26uc&prodversion=32`; const fileUrl = `https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&x=id%3D${extensionID}%26uc&prodversion=${chromiumVersion}`;
if (!fs.existsSync(destFolder))
fs.mkdirSync(destFolder, { recursive: true });
const fileStream = fs.createWriteStream(filePath); const fileStream = fs.createWriteStream(filePath);
const downloadFile = url => { const downloadFile = url => {
return new Promise((resolve, reject) => { return /** @type {Promise<void>} */(new Promise((resolve, reject) => {
const request = https.get(url); const request = https.get(url);
request.on('response', response => { request.on('response', response => {
@@ -33,7 +38,7 @@ const downloadFile = url => {
}); });
request.on('error', reject); request.on('error', reject);
request.end(); request.end();
}); }));
}; };
(async () => { (async () => {

View File

@@ -34,6 +34,7 @@ export interface ClientParams {
| { databasePath: string; readonly: boolean }; | { databasePath: string; readonly: boolean };
poolSize?: number; poolSize?: number;
logger?: () => void; logger?: () => void;
querySplitter?: (sql: string, clieng?: string) => string[];
} }
/** /**
@@ -57,6 +58,7 @@ export interface ConnectionParams {
cert?: string; cert?: string;
key?: string; key?: string;
ca?: string; ca?: string;
connString?: string;
untrustedConnection: boolean; untrustedConnection: boolean;
ciphers?: string; ciphers?: string;
ssh: boolean; ssh: boolean;

View File

@@ -1,3 +1,5 @@
import { match } from 'ciaplu';
function isJSON (str: string) { function isJSON (str: string) {
try { try {
if (!['{', '['].includes(str.trim()[0])) if (!['{', '['].includes(str.trim()[0]))
@@ -176,17 +178,13 @@ function isMD (str: string) {
} }
export function langDetector (str: string) { export function langDetector (str: string) {
if (!str || !str.trim().length) return match(str)
return 'text'; .when(() => !str || !str.trim().length, () => 'text')
if (isJSON(str)) .when(isJSON, () => 'json')
return 'json'; .when(isHTML, () => 'html')
if (isHTML(str)) .when(isSVG, () => 'svg')
return 'html'; .when(isXML, () => 'xml')
if (isSVG(str)) .when(isMD, () => 'markdown')
return 'svg'; .otherwise(() => 'text')
if (isXML(str)) .return();
return 'xml';
if (isMD(str))
return 'markdown';
return 'text';
} }

View File

@@ -1,45 +1,25 @@
import { match } from 'ciaplu';
export function mimeFromHex (hex: string) { export function mimeFromHex (hex: string) {
switch (hex.substring(0, 4)) { // 2 bytes return match(hex.substring(0, 4)) // 2 bytes
case '424D': .with('424D', () => ({ ext: 'bmp', mime: 'image/bmp' }))
return { ext: 'bmp', mime: 'image/bmp' }; .with('1F8B', () => ({ ext: 'tar.gz', mime: 'application/gzip' }))
case '1F8B': .with('0B77', () => ({ ext: 'ac3', mime: 'audio/vnd.dolby.dd-raw' }))
return { ext: 'tar.gz', mime: 'application/gzip' }; .with('7801', () => ({ ext: 'dmg', mime: 'application/x-apple-diskimage' }))
case '0B77': .with('4D5A', () => ({ ext: 'exe', mime: 'application/x-msdownload' }))
return { ext: 'ac3', mime: 'audio/vnd.dolby.dd-raw' }; .when((val) => ['1FA0', '1F9D'].includes(val), () => ({ ext: 'Z', mime: 'application/x-compress' }))
case '7801': .extracting(() => hex.substring(0, 6)) // 3 bytes
return { ext: 'dmg', mime: 'application/x-apple-diskimage' }; .with('FFD8FF', () => ({ ext: 'jpg', mime: 'image/jpeg' }))
case '4D5A': .with('4949BC', () => ({ ext: 'jxr', mime: 'image/vnd.ms-photo' }))
return { ext: 'exe', mime: 'application/x-msdownload' }; .with('425A68', () => ({ ext: 'bz2', mime: 'application/x-bzip2' }))
case '1FA0': .extracting(() => hex) // 4 bytes
case '1F9D': .with('89504E47', () => ({ ext: 'png', mime: 'image/png' }))
return { ext: 'Z', mime: 'application/x-compress' }; .with('47494638', () => ({ ext: 'gif', mime: 'image/gif' }))
default: .with('25504446', () => ({ ext: 'pdf', mime: 'application/pdf' }))
switch (hex.substring(0, 6)) { // 3 bytes .with('504B0304', () => ({ ext: 'zip', mime: 'application/zip' }))
case 'FFD8FF': .with('425047FB', () => ({ ext: 'bpg', mime: 'image/bpg' }))
return { ext: 'jpg', mime: 'image/jpeg' }; .with('4D4D002A', () => ({ ext: 'tif', mime: 'image/tiff' }))
case '4949BC': .with('00000100', () => ({ ext: 'ico', mime: 'image/x-icon' }))
return { ext: 'jxr', mime: 'image/vnd.ms-photo' }; .otherwise(() => ({ ext: '', mime: 'unknown ' + hex }))
case '425A68': .return();
return { ext: 'bz2', mime: 'application/x-bzip2' };
default:
switch (hex) { // 4 bites
case '89504E47':
return { ext: 'png', mime: 'image/png' };
case '47494638':
return { ext: 'gif', mime: 'image/gif' };
case '25504446':
return { ext: 'pdf', mime: 'application/pdf' };
case '504B0304':
return { ext: 'zip', mime: 'application/zip' };
case '425047FB':
return { ext: 'bpg', mime: 'image/bpg' };
case '4D4D002A':
return { ext: 'tif', mime: 'image/tiff' };
case '00000100':
return { ext: 'ico', mime: 'image/x-icon' };
default:
return { ext: '', mime: 'unknown ' + hex };
}
}
}
} }

View File

@@ -2,18 +2,162 @@
/* eslint-disable no-useless-escape */ /* eslint-disable no-useless-escape */
import { lineString, point, polygon } from '@turf/helpers'; import { lineString, point, polygon } from '@turf/helpers';
import { BIT, BLOB, DATE, DATETIME, FLOAT, IS_MULTI_SPATIAL, NUMBER, SPATIAL, TEXT_SEARCH } from 'common/fieldTypes'; import { BIT, BLOB, DATE, DATETIME, FLOAT, IS_MULTI_SPATIAL, NUMBER, SPATIAL, TEXT_SEARCH } from 'common/fieldTypes';
import * as antares from 'common/interfaces/antares';
import { ClientCode } from 'common/interfaces/antares';
import * as moment from 'moment'; import * as moment from 'moment';
import customizations from '../customizations'; import customizations from '../customizations';
import { ClientCode } from '../interfaces/antares';
import { getArrayDepth } from './getArrayDepth'; import { getArrayDepth } from './getArrayDepth';
import hexToBinary, { HexChar } from './hexToBinary'; import hexToBinary, { HexChar } from './hexToBinary';
/** /**
* Escapes a string fo SQL use * Splits a SQL string into multiple queries based on semicolons (;).
* Handles BEGIN-END blocks, strings, comments, and PostgreSQL dollar-quoted tags.
* *
* @param { String } string * @param {string} sql - The SQL string to split.
* @returns { String } Escaped string * @param {ClientCode} dbType - The database type (e.g., 'pg', 'mysql').
* @returns {string[]} - An array of separated SQL queries.
*/
export const querySplitter =(sql: string, dbType: ClientCode): string[] => {
const queries: string[] = [];
let currentQuery = '';
let insideBlock = false;
let insideString = false;
let stringDelimiter: string | null = null;
let insideDollarTag = false;
let dollarTagDelimiter: string | null = null;
// Regex patterns for BEGIN-END blocks, dollar tags in PostgreSQL, and semicolons
const beginRegex = /\bBEGIN\b/i;
const endRegex = /\bEND\b;/i;
const dollarTagRegex = /\$(\w+)?\$/; // Matches $tag$ or $$
// Split on semicolons, keeping semicolons attached to the lines
const lines = sql.split(/(?<=;)/);
for (let line of lines) {
line = line.trim();
if (!line) continue;
for (let i = 0; i < line.length; i++) {
const char = line[i];
// Handle string boundaries
if ((char === '\'' || char === '"') && (!insideString || char === stringDelimiter)) {
if (!insideString) {
insideString = true;
stringDelimiter = char;
}
else {
insideString = false;
stringDelimiter = null;
}
}
currentQuery += char;
if (dbType === 'pg') {
// Handle dollar-quoted blocks in PostgreSQL
if (!insideString && line.slice(i).match(dollarTagRegex)) {
const match = line.slice(i).match(dollarTagRegex);
if (match) {
const tag = match[0];
if (!insideDollarTag) {
insideDollarTag = true;
dollarTagDelimiter = tag;
currentQuery += tag;
i += tag.length - 1;
}
else if (dollarTagDelimiter === tag) {
insideDollarTag = false;
dollarTagDelimiter = null;
currentQuery += tag;
i += tag.length - 1;
}
}
}
}
// Check BEGIN-END blocks
if (!insideString && !insideDollarTag) {
if (beginRegex.test(line))
insideBlock = true;
if (insideBlock && endRegex.test(line))
insideBlock = false;
}
}
// Append the query if we encounter a semicolon outside a BEGIN-END block, outside a string, and outside dollar tags
if (!insideBlock && !insideString && !insideDollarTag && /;\s*$/.test(line)) {
queries.push(currentQuery.trim());
currentQuery = '';
}
}
// Add any remaining query
if (currentQuery.trim())
queries.push(currentQuery.trim());
return queries;
};
/**
* Removes all comments (both single-line and multi-line) from a SQL string.
*
* @param {string} sql - The SQL string to process.
* @returns {string} - The SQL string without comments.
*/
export const removeComments = (sql: string): string => {
let result = '';
let insideSingleLineComment = false;
let insideMultiLineComment = false;
for (let i = 0; i < sql.length; i++) {
const char = sql[i];
const nextChar = sql[i + 1] || '';
// Handle single-line comments (--)
if (!insideMultiLineComment && char === '-' && nextChar === '-')
insideSingleLineComment = true;
// Handle multi-line comments (/* */)
if (!insideSingleLineComment && char === '/' && nextChar === '*') {
insideMultiLineComment = true;
i++; // Skip the '*' character
continue;
}
if (insideMultiLineComment && char === '*' && nextChar === '/') {
insideMultiLineComment = false;
i++; // Skip the '/' character
continue;
}
// Skip characters inside comments
if (insideSingleLineComment) {
if (char === '\n')
insideSingleLineComment = false;
continue;
}
if (insideMultiLineComment)
continue;
// Append non-comment characters to the result
result += char;
}
return result;
};
/**
* Escapes a string for safe use in SQL queries.
*
* @param {string} string - The string to escape.
* @returns {string} - The escaped string.
*/ */
export const sqlEscaper = (string: string): string => { export const sqlEscaper = (string: string): string => {
// eslint-disable-next-line no-control-regex // eslint-disable-next-line no-control-regex
@@ -26,6 +170,12 @@ export const sqlEscaper = (string: string): string => {
}); });
}; };
/**
* Converts a value into a GeoJSON object based on its type.
*
* @param {any} val - The value to convert.
* @returns {object} - The generated GeoJSON object.
*/
export const objectToGeoJSON = (val: any) => { export const objectToGeoJSON = (val: any) => {
if (Array.isArray(val)) { if (Array.isArray(val)) {
if (getArrayDepth(val) === 1) if (getArrayDepth(val) === 1)
@@ -37,6 +187,13 @@ export const objectToGeoJSON = (val: any) => {
return point([val.x, val.y]); return point([val.x, val.y]);
}; };
/**
* Escapes and wraps a string in quotes for safe use in SQL queries.
*
* @param {string} val - The string to process.
* @param {ClientCode} client - The database type (e.g., 'pg', 'mysql').
* @returns {string} - The escaped and quoted string.
*/
export const escapeAndQuote = (val: string, client: ClientCode) => { export const escapeAndQuote = (val: string, client: ClientCode) => {
const { stringsWrapper: sw } = customizations[client]; const { stringsWrapper: sw } = customizations[client];
// eslint-disable-next-line no-control-regex // eslint-disable-next-line no-control-regex
@@ -73,11 +230,17 @@ export const escapeAndQuote = (val: string, client: ClientCode) => {
return `${sw}${escapedVal}${sw}`; return `${sw}${escapedVal}${sw}`;
}; };
/**
* Converts a value into a SQL string based on the field type and database type.
*
* @param {object} args - Arguments containing the value, database type, and field type.
* @returns {string} - The generated SQL string.
*/
export const valueToSqlString = (args: { export const valueToSqlString = (args: {
val: any; val: any;
client: ClientCode; client: ClientCode;
field: {type: string; datePrecision?: number; precision?: number | false; isArray?: boolean}; field: { type: string; datePrecision?: number; precision?: number | false; isArray?: boolean };
}): string => { }): string => {
let parsedValue; let parsedValue;
const { val, client, field } = args; const { val, client, field } = args;
const { stringsWrapper: sw } = customizations[client]; const { stringsWrapper: sw } = customizations[client];
@@ -164,13 +327,19 @@ export const valueToSqlString = (args: {
return parsedValue; return parsedValue;
}; };
/**
* Converts a JSON array into an SQL INSERT query.
*
* @param {object} args - Arguments containing the JSON data, database type, fields, and options.
* @returns {string} - The generated SQL INSERT query.
*/
export const jsonToSqlInsert = (args: { export const jsonToSqlInsert = (args: {
json: Record<string, any>[]; json: Record<string, any>[];
client: ClientCode; client: ClientCode;
fields: Record<string, {type: string; datePrecision: number}>; fields: Record<string, { type: string; datePrecision: number }>;
table: string; table: string;
options?: {sqlInsertAfter: number; sqlInsertDivider: 'bytes' | 'rows'}; options?: { sqlInsertAfter: number; sqlInsertDivider: 'bytes' | 'rows' };
}) => { }) => {
const { client, json, fields, table, options } = args; const { client, json, fields, table, options } = args;
const sqlInsertAfter = options && options.sqlInsertAfter ? options.sqlInsertAfter : 1; const sqlInsertAfter = options && options.sqlInsertAfter ? options.sqlInsertAfter : 1;
const sqlInsertDivider = options && options.sqlInsertDivider ? options.sqlInsertDivider : 'rows'; const sqlInsertDivider = options && options.sqlInsertDivider ? options.sqlInsertDivider : 'rows';
@@ -192,7 +361,7 @@ export const jsonToSqlInsert = (args: {
(sqlInsertDivider === 'bytes' && queryLength >= sqlInsertAfter * 1024) || (sqlInsertDivider === 'bytes' && queryLength >= sqlInsertAfter * 1024) ||
(sqlInsertDivider === 'rows' && rowsWritten === sqlInsertAfter) (sqlInsertDivider === 'rows' && rowsWritten === sqlInsertAfter)
) { ) {
insertsString += insertStmt+';'; insertsString += insertStmt + ';';
insertStmt = `\nINSERT INTO ${ew}${table}${ew} (${fieldNames.join(', ')}) VALUES `; insertStmt = `\nINSERT INTO ${ew}${table}${ew} (${fieldNames.join(', ')}) VALUES `;
rowsWritten = 0; rowsWritten = 0;
} }
@@ -205,7 +374,31 @@ export const jsonToSqlInsert = (args: {
} }
if (rowsWritten > 0) if (rowsWritten > 0)
insertsString += insertStmt+';'; insertsString += insertStmt + ';';
return insertsString; return insertsString;
}; };
/**
* Formats a JSON value for use in an SQL WHERE clause.
*
* @param {object} jsonValue - The JSON value to format.
* @param {ClientCode} clientType - The database type (e.g., 'pg', 'mysql').
* @returns {string} - The formatted SQL WHERE clause.
*/
export const formatJsonForSqlWhere = (jsonValue: object, clientType: antares.ClientCode) => {
const formattedValue = JSON.stringify(jsonValue);
switch (clientType) {
case 'mysql':
return ` = CAST('${formattedValue}' AS JSON)`;
case 'maria':
return ` = '${formattedValue}'`;
case 'pg':
return `::text = '${formattedValue}'`;
case 'firebird':
case 'sqlite':
default:
return ` = '${formattedValue}'`;
}
};

View File

@@ -1,29 +1,34 @@
export const shortcutEvents: Record<string, { l18n: string; l18nParam?: string | number; context?: 'tab' }> = { export const shortcutEvents: Record<string, { i18n: string; i18nParam?: string | number; context?: 'tab' | 'main' }> = {
'run-or-reload': { l18n: 'application.runOrReload', context: 'tab' }, 'run-or-reload': { i18n: 'application.runOrReload', context: 'tab' },
'open-new-tab': { l18n: 'application.openNewTab', context: 'tab' }, 'open-new-tab': { i18n: 'application.openNewTab', context: 'tab' },
'close-tab': { l18n: 'application.closeTab', context: 'tab' }, 'close-tab': { i18n: 'application.closeTab', context: 'tab' },
'format-query': { l18n: 'database.formatQuery', context: 'tab' }, 'format-query': { i18n: 'database.formatQuery', context: 'tab' },
'kill-query': { l18n: 'database.killQuery', context: 'tab' }, 'kill-query': { i18n: 'database.killQuery', context: 'tab' },
'query-history': { l18n: 'database.queryHistory', context: 'tab' }, 'query-history': { i18n: 'database.queryHistory', context: 'tab' },
'clear-query': { l18n: 'database.clearQuery', context: 'tab' }, 'clear-query': { i18n: 'database.clearQuery', context: 'tab' },
// 'save-file': { l18n: 'application.saveFile', context: 'tab' }, // 'save-file': { i18n: 'application.saveFile', context: 'tab' },
'open-file': { l18n: 'application.openFile', context: 'tab' }, 'open-file': { i18n: 'application.openFile', context: 'tab' },
'save-file-as': { l18n: 'application.saveFileAs', context: 'tab' }, 'save-file-as': { i18n: 'application.saveFileAs', context: 'tab' },
'next-tab': { l18n: 'application.nextTab' }, 'next-tab': { i18n: 'application.nextTab' },
'prev-tab': { l18n: 'application.previousTab' }, 'prev-tab': { i18n: 'application.previousTab' },
'open-all-connections': { l18n: 'application.openAllConnections' }, 'open-all-connections': { i18n: 'application.openAllConnections' },
'open-filter': { l18n: 'application.openFilter' }, 'open-filter': { i18n: 'application.openFilter' },
'next-page': { l18n: 'application.nextResultsPage' }, 'next-page': { i18n: 'application.nextResultsPage' },
'prev-page': { l18n: 'application.previousResultsPage' }, 'prev-page': { i18n: 'application.previousResultsPage' },
'toggle-console': { l18n: 'application.toggleConsole' }, 'toggle-console': { i18n: 'application.toggleConsole' },
'save-content': { l18n: 'application.saveContent' }, 'save-content': { i18n: 'application.saveContent' },
'create-connection': { l18n: 'connection.createNewConnection' }, 'create-connection': { i18n: 'connection.createNewConnection' },
'open-settings': { l18n: 'application.openSettings' }, 'open-settings': { i18n: 'application.openSettings' },
'open-scratchpad': { l18n: 'application.openNotes' } 'open-scratchpad': { i18n: 'application.openNotes' },
setFullScreen: { i18n: 'application.fullScreen', context: 'main' },
setZoomIn: { i18n: 'application.zoomIn', context: 'main' },
setZoomOut: { i18n: 'application.zoomOut', context: 'main' },
setZoomReset: { i18n: 'application.zoomReset', context: 'main' }
}; };
interface ShortcutRecord { interface ShortcutRecord {
event: string; event: string;
isFunction?: boolean;
keys: Electron.Accelerator[] | string[]; keys: Electron.Accelerator[] | string[];
/** Needed for default shortcuts */ /** Needed for default shortcuts */
os: NodeJS.Platform[]; os: NodeJS.Platform[];
@@ -38,6 +43,30 @@ const shortcuts: ShortcutRecord[] = [
keys: ['F5'], keys: ['F5'],
os: ['darwin', 'linux', 'win32'] os: ['darwin', 'linux', 'win32']
}, },
{
event: 'setFullScreen',
isFunction: true,
keys: ['F11'],
os: ['darwin', 'linux', 'win32']
},
{
event: 'setZoomIn',
isFunction: true,
keys: ['CommandOrControl+='],
os: ['darwin', 'linux', 'win32']
},
{
event: 'setZoomOut',
isFunction: true,
keys: ['CommandOrControl+-'],
os: ['darwin', 'linux', 'win32']
},
{
event: 'setZoomReset',
isFunction: true,
keys: ['CommandOrControl+0'],
os: ['darwin', 'linux', 'win32']
},
{ {
event: 'save-content', event: 'save-content',
keys: ['CommandOrControl+S'], keys: ['CommandOrControl+S'],
@@ -142,8 +171,8 @@ const shortcuts: ShortcutRecord[] = [
for (let i = 1; i <= 9; i++) { for (let i = 1; i <= 9; i++) {
shortcutEvents[`select-tab-${i}`] = { shortcutEvents[`select-tab-${i}`] = {
l18n: 'application.selectTabNumber', i18n: 'application.selectTabNumber',
l18nParam: i i18nParam: i
}; };
shortcuts.push({ shortcuts.push({

View File

@@ -26,6 +26,7 @@ export default (connections: Record<string, antares.Client>) => {
user: conn.user, user: conn.user,
password: conn.password, password: conn.password,
readonly: conn.readonly, readonly: conn.readonly,
connectionString: conn.connString,
database: '', database: '',
schema: '', schema: '',
databasePath: '', databasePath: '',
@@ -63,9 +64,9 @@ export default (connections: Record<string, antares.Client>) => {
username: conn.sshUser, username: conn.sshUser,
password: conn.sshPass, password: conn.sshPass,
port: conn.sshPort ? conn.sshPort : 22, port: conn.sshPort ? conn.sshPort : 22,
privateKey: conn.sshKey ? fs.readFileSync(conn.sshKey).toString() : null, privateKey: conn.sshKey ? fs.readFileSync(conn.sshKey).toString() : undefined,
passphrase: conn.sshPassphrase, passphrase: conn.sshPassphrase,
keepaliveInterval: conn.sshKeepAliveInterval ? conn.sshKeepAliveInterval*1000 : null keepaliveInterval: conn.sshKeepAliveInterval ? conn.sshKeepAliveInterval*1000 : undefined
}; };
} }
@@ -89,11 +90,12 @@ export default (connections: Record<string, antares.Client>) => {
return { status: 'success' }; return { status: 'success' };
} }
catch (err) { catch (error) {
clearInterval(abortChecker); clearInterval(abortChecker);
if (error instanceof AggregateError)
if (!isLocalAborted) throw new Error(error.errors.reduce((acc, curr) => acc +' | '+ curr.message, ''));
return { status: 'error', response: err.toString() }; else if (!isLocalAborted)
return { status: 'error', response: error.toString() };
else else
return { status: 'abort', response: 'Connection aborted' }; return { status: 'abort', response: 'Connection aborted' };
} }
@@ -122,6 +124,7 @@ export default (connections: Record<string, antares.Client>) => {
password: conn.password, password: conn.password,
application_name: 'Antares SQL', application_name: 'Antares SQL',
readonly: conn.readonly, readonly: conn.readonly,
connectionString: conn.connString,
database: '', database: '',
schema: '', schema: '',
databasePath: '', databasePath: '',

View File

@@ -183,6 +183,7 @@ export default (connections: Record<string, antares.Client>) => {
const result = await connections[uid].raw(query, { const result = await connections[uid].raw(query, {
nest: true, nest: true,
details: true, details: true,
comments: false,
schema, schema,
tabUid, tabUid,
autocommit autocommit

View File

@@ -3,7 +3,7 @@ import { ARRAY, BIT, BLOB, BOOLEAN, DATE, DATETIME, FLOAT, LONG_TEXT, NUMBER, TE
import * as antares from 'common/interfaces/antares'; import * as antares from 'common/interfaces/antares';
import { InsertRowsParams } from 'common/interfaces/tableApis'; import { InsertRowsParams } from 'common/interfaces/tableApis';
import { fakerCustom } from 'common/libs/fakerCustom'; import { fakerCustom } from 'common/libs/fakerCustom';
import { sqlEscaper } from 'common/libs/sqlUtils'; import { formatJsonForSqlWhere, sqlEscaper } from 'common/libs/sqlUtils';
import { ipcMain } from 'electron'; import { ipcMain } from 'electron';
import * as fs from 'fs'; import * as fs from 'fs';
import * as moment from 'moment'; import * as moment from 'moment';
@@ -221,7 +221,7 @@ export default (connections: Record<string, antares.Client>) => {
.update({ [params.field]: `= ${escapedParam}` }) .update({ [params.field]: `= ${escapedParam}` })
.schema(params.schema) .schema(params.schema)
.from(params.table) .from(params.table)
.where({ [params.primary]: `= ${id}` }) .where({ [params.primary]: `= ${typeof id === 'string' ? sqlEscaper(id) : id}` })
.limit(1) .limit(1)
.run(); .run();
} }
@@ -233,9 +233,10 @@ export default (connections: Record<string, antares.Client>) => {
for (const key in orgRow) { for (const key in orgRow) {
if (typeof orgRow[key] === 'string') if (typeof orgRow[key] === 'string')
orgRow[key] = `'${orgRow[key]}'`; orgRow[key] = ` = '${sqlEscaper(orgRow[key])}'`;
else if (typeof orgRow[key] === 'object' && orgRow[key] !== null)
if (orgRow[key] === null) orgRow[key] = formatJsonForSqlWhere(orgRow[key], connections[params.uid]._client);
else if (orgRow[key] === null)
orgRow[key] = `IS ${orgRow[key]}`; orgRow[key] = `IS ${orgRow[key]}`;
else else
orgRow[key] = `= ${orgRow[key]}`; orgRow[key] = `= ${orgRow[key]}`;
@@ -289,7 +290,7 @@ export default (connections: Record<string, antares.Client>) => {
for (const row of params.rows) { for (const row of params.rows) {
for (const key in row) { for (const key in row) {
if (typeof row[key] === 'string') if (typeof row[key] === 'string')
row[key] = `'${row[key]}'`; row[key] = `'${sqlEscaper(row[key])}'`;
if (row[key] === null) if (row[key] === null)
row[key] = 'IS NULL'; row[key] = 'IS NULL';
@@ -439,16 +440,17 @@ export default (connections: Record<string, antares.Client>) => {
ipcMain.handle('get-foreign-list', async (event, { uid, schema, table, column, description }) => { ipcMain.handle('get-foreign-list', async (event, { uid, schema, table, column, description }) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' }; if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
const { elementsWrapper: ew } = customizations[connections[uid]._client];
try { try {
const query = connections[uid] const query = connections[uid]
.select(`${column} AS foreign_column`) .select(`${ew}${column}${ew} AS foreign_column`)
.schema(schema) .schema(schema)
.from(table) .from(table)
.orderBy('foreign_column ASC'); .orderBy('foreign_column ASC');
if (description) if (description)
query.select(`LEFT(${description}, 20) AS foreign_description`); query.select(`LEFT(${ew}${description}${ew}, 20) AS foreign_description`);
const results = await query.run<Record<string, string>>(); const results = await query.run<Record<string, string>>();

View File

@@ -81,7 +81,15 @@ export class ShortcutRegister {
accelerator: key, accelerator: key,
visible: isMenuVisible, visible: isMenuVisible,
click: () => { click: () => {
this._mainWindow.webContents.send(shortcut.event); if (shortcut.isFunction) {
if (shortcut.event in this) {
type exporterMethods = 'setFullScreen' | 'setZoomIn' | 'setZoomOut' | 'setZoomReset';
this[shortcut.event as exporterMethods]();
}
}
else
this._mainWindow.webContents.send(shortcut.event);
if (isDevelopment) console.log('LOCAL EVENT:', shortcut); if (isDevelopment) console.log('LOCAL EVENT:', shortcut);
} }
}); });
@@ -121,6 +129,24 @@ export class ShortcutRegister {
} }
} }
setFullScreen () {
this._mainWindow.setFullScreen(!this._mainWindow.isFullScreen());
}
setZoomIn () {
const currentZoom = this._mainWindow.webContents.getZoomLevel();
this._mainWindow.webContents.setZoomLevel(currentZoom + 1);
}
setZoomOut () {
const currentZoom = this._mainWindow.webContents.getZoomLevel();
this._mainWindow.webContents.setZoomLevel(currentZoom - 1);
}
setZoomReset () {
this._mainWindow.webContents.setZoomLevel(0);
}
reload () { reload () {
this.unregister(); this.unregister();
this.init(); this.init();

View File

@@ -1,28 +1,10 @@
import * as antares from 'common/interfaces/antares'; import * as antares from 'common/interfaces/antares';
import { querySplitter } from 'common/libs/sqlUtils';
import mysql from 'mysql2/promise'; import mysql from 'mysql2/promise';
import * as pg from 'pg'; import * as pg from 'pg';
import SSH2Promise = require('@fabio286/ssh2-promise'); import SSH2Promise = require('@fabio286/ssh2-promise');
export type LoggerLevel = 'query' | 'error' import { ipcLogger, LoggerLevel } from '../misc/ipcLogger';
const ipcLogger = ({ content, cUid, level }: {content: string; cUid: string; level: LoggerLevel}) => {
if (level === 'error') {
if (process.type !== undefined) {
const mainWindow = require('electron').webContents.fromId(1);
mainWindow.send('non-blocking-exception', { cUid, message: content, date: new Date() });
}
if (process.env.NODE_ENV === 'development' && process.type === 'browser') console.log(content);
}
else if (level === 'query') {
// Remove comments, newlines and multiple spaces
const escapedSql = content.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '').replace(/\s\s+/g, ' ');
if (process.type !== undefined) {
const mainWindow = require('electron').webContents.fromId(1);
mainWindow.send('query-log', { cUid, sql: escapedSql, date: new Date() });
}
if (process.env.NODE_ENV === 'development' && process.type === 'browser') console.log(escapedSql);
}
};
/** /**
* As Simple As Possible Query Builder Core * As Simple As Possible Query Builder Core
@@ -34,6 +16,7 @@ export abstract class BaseClient {
protected _poolSize: number; protected _poolSize: number;
protected _ssh?: SSH2Promise; protected _ssh?: SSH2Promise;
protected _logger: (args: {content: string; cUid: string; level: LoggerLevel}) => void; protected _logger: (args: {content: string; cUid: string; level: LoggerLevel}) => void;
protected _querySplitter: (sql: string, client: antares.ClientCode) => string[];
protected _queryDefaults: antares.QueryBuilderObject; protected _queryDefaults: antares.QueryBuilderObject;
protected _query: antares.QueryBuilderObject; protected _query: antares.QueryBuilderObject;
@@ -43,6 +26,7 @@ export abstract class BaseClient {
this._params = args.params; this._params = args.params;
this._poolSize = args.poolSize || undefined; this._poolSize = args.poolSize || undefined;
this._logger = args.logger || ipcLogger; this._logger = args.logger || ipcLogger;
this._querySplitter = args.querySplitter || querySplitter;
this._queryDefaults = { this._queryDefaults = {
schema: '', schema: '',

View File

@@ -1,6 +1,7 @@
import dataTypes from 'common/data-types/firebird'; import dataTypes from 'common/data-types/firebird';
import { FLOAT, NUMBER } from 'common/fieldTypes'; import { FLOAT, NUMBER } from 'common/fieldTypes';
import * as antares from 'common/interfaces/antares'; import * as antares from 'common/interfaces/antares';
import { removeComments } from 'common/libs/sqlUtils';
import * as firebird from 'node-firebird'; import * as firebird from 'node-firebird';
import * as path from 'path'; import * as path from 'path';
@@ -245,10 +246,10 @@ export class FirebirdSQLClient extends BaseClient {
name: db.name, name: db.name,
size: schemaSize, size: schemaSize,
tables: remappedTables, tables: remappedTables,
functions: [], functions: [] as null[],
procedures: remappedProcedures, procedures: remappedProcedures,
triggers: remappedTriggers, triggers: remappedTriggers,
schedulers: [] schedulers: [] as null[]
}; };
}); });
} }
@@ -337,7 +338,7 @@ export class FirebirdSQLClient extends BaseClient {
return { return {
name: field.FIELD_NAME.trim(), name: field.FIELD_NAME.trim(),
key: null, key: null as null,
type: fieldType, type: fieldType,
schema: schema, schema: schema,
table: table, table: table,
@@ -346,14 +347,14 @@ export class FirebirdSQLClient extends BaseClient {
datePrecision: field.FIELD_NAME.trim() === 'TIMESTAMP' ? 4 : null, datePrecision: field.FIELD_NAME.trim() === 'TIMESTAMP' ? 4 : null,
charLength: ![...NUMBER, ...FLOAT].includes(fieldType) ? field.FIELD_LENGTH : null, charLength: ![...NUMBER, ...FLOAT].includes(fieldType) ? field.FIELD_LENGTH : null,
nullable: !field.NOT_NULL, nullable: !field.NOT_NULL,
unsigned: null, unsigned: null as null,
zerofill: null, zerofill: null as null,
order: field.FIELD_POSITION+1, order: field.FIELD_POSITION+1,
default: defaultValue, default: defaultValue,
charset: field.CHARSET, charset: field.CHARSET,
collation: null, collation: null as null,
autoIncrement: false, autoIncrement: false,
onUpdate: null, onUpdate: null as null,
comment: field.DESCRIPTION?.trim() comment: field.DESCRIPTION?.trim()
}; };
}); });
@@ -457,7 +458,7 @@ export class FirebirdSQLClient extends BaseClient {
table: table, table: table,
field: field.FKCOLUMN_NAME.trim(), field: field.FKCOLUMN_NAME.trim(),
position: field.KEY_SEQ, position: field.KEY_SEQ,
constraintPosition: null, constraintPosition: null as null,
constraintName: field.FK_NAME.trim(), constraintName: field.FK_NAME.trim(),
refSchema: schema, refSchema: schema,
refTable: field.PKTABLE_NAME.trim(), refTable: field.PKTABLE_NAME.trim(),
@@ -1036,14 +1037,12 @@ export class FirebirdSQLClient extends BaseClient {
}; };
if (!args.comments) if (!args.comments)
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments sql = removeComments(sql);
const resultsArr = []; const resultsArr = [];
let paramsArr = []; let paramsArr = [];
const queries = args.split const queries = args.split
? sql.split(/((?:[^;'"]*(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*')[^;'"]*)+)|;/gm) ? this._querySplitter(sql, 'firebird')
.filter(Boolean)
.map(q => q.trim())
: [sql]; : [sql];
let connection: firebird.Database | firebird.Transaction; let connection: firebird.Database | firebird.Transaction;

View File

@@ -2,9 +2,12 @@ import SSH2Promise = require('@fabio286/ssh2-promise');
import SSHConfig from '@fabio286/ssh2-promise/lib/sshConfig'; import SSHConfig from '@fabio286/ssh2-promise/lib/sshConfig';
import dataTypes from 'common/data-types/mysql'; import dataTypes from 'common/data-types/mysql';
import * as antares from 'common/interfaces/antares'; import * as antares from 'common/interfaces/antares';
import { removeComments } from 'common/libs/sqlUtils';
import * as mysql from 'mysql2/promise'; import * as mysql from 'mysql2/promise';
import * as EncodingToCharset from '../../../../node_modules/mysql2/lib/constants/encoding_charset.js';
import { BaseClient } from './BaseClient'; import { BaseClient } from './BaseClient';
EncodingToCharset.utf8mb3 = 192; // To fix https://github.com/sidorares/node-mysql2/issues/1398 until not included in mysql2
export class MySQLClient extends BaseClient { export class MySQLClient extends BaseClient {
private _schema?: string; private _schema?: string;
@@ -171,13 +174,13 @@ export class MySQLClient extends BaseClient {
remotePort: this._params.port remotePort: this._params.port
}); });
dbConfig.host = (this._ssh.config as SSHConfig[] & { host: string }).host; dbConfig.host = undefined;
dbConfig.port = tunnel.localPort; dbConfig.port = tunnel.localPort;
} }
catch (err) { catch (err) {
if (this._ssh) { if (this._ssh) {
this._ssh.close();
this._ssh.closeTunnel(); this._ssh.closeTunnel();
this._ssh.close();
} }
throw err; throw err;
} }
@@ -225,8 +228,8 @@ export class MySQLClient extends BaseClient {
clearInterval(this._keepaliveTimer); clearInterval(this._keepaliveTimer);
this._keepaliveTimer = undefined; this._keepaliveTimer = undefined;
if (this._ssh) { if (this._ssh) {
this._ssh.close();
this._ssh.closeTunnel(); this._ssh.closeTunnel();
this._ssh.close();
} }
} }
@@ -300,6 +303,8 @@ export class MySQLClient extends BaseClient {
await this.connect(); await this.connect();
return this.getConnection(args, true); return this.getConnection(args, true);
} }
else if (error instanceof AggregateError)
throw new Error(error.errors.reduce((acc, curr) => acc +' | '+ curr.message, ''));
else else
throw new Error(error.message); throw new Error(error.message);
} }
@@ -1747,15 +1752,13 @@ export class MySQLClient extends BaseClient {
}; };
if (!args.comments) if (!args.comments)
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments sql = removeComments(sql);
const nestTables = args.nest ? '.' : false; const nestTables = args.nest ? '.' : false;
const resultsArr: antares.QueryResult[] = []; const resultsArr: antares.QueryResult[] = [];
let paramsArr = []; let paramsArr = [];
const queries = args.split const queries = args.split
? sql.split(/((?:[^;'"]*(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*')[^;'"]*)+)|;/gm) ? this._querySplitter(sql, 'mysql')
.filter(Boolean)
.map(q => q.trim())
: [sql]; : [sql];
const connection = await this.getConnection(args); const connection = await this.getConnection(args);

View File

@@ -2,6 +2,7 @@ import SSH2Promise = require('@fabio286/ssh2-promise');
import SSHConfig from '@fabio286/ssh2-promise/lib/sshConfig'; import SSHConfig from '@fabio286/ssh2-promise/lib/sshConfig';
import dataTypes from 'common/data-types/postgresql'; import dataTypes from 'common/data-types/postgresql';
import * as antares from 'common/interfaces/antares'; import * as antares from 'common/interfaces/antares';
import { removeComments } from 'common/libs/sqlUtils';
import * as pg from 'pg'; import * as pg from 'pg';
import * as pgAst from 'pgsql-ast-parser'; import * as pgAst from 'pgsql-ast-parser';
import { ConnectionOptions } from 'tls'; import { ConnectionOptions } from 'tls';
@@ -155,6 +156,7 @@ export class PostgreSQLClient extends BaseClient {
host: this._params.host, host: this._params.host,
port: this._params.port, port: this._params.port,
user: this._params.user, user: this._params.user,
connectionString: this._params.connectionString,
database: 'postgres' as string, database: 'postgres' as string,
password: this._params.password, password: this._params.password,
ssl: null as ConnectionOptions ssl: null as ConnectionOptions
@@ -178,7 +180,7 @@ export class PostgreSQLClient extends BaseClient {
remotePort: this._params.port remotePort: this._params.port
}); });
dbConfig.host = (this._ssh.config as SSHConfig[] & { host: string }).host; dbConfig.host = undefined;
dbConfig.port = tunnel.localPort; dbConfig.port = tunnel.localPort;
} }
catch (err) { catch (err) {
@@ -347,7 +349,7 @@ export class PostgreSQLClient extends BaseClient {
matviewowner AS owner, matviewowner AS owner,
ispopulated AS is_populated, ispopulated AS is_populated,
definition, definition,
'materializedview' AS table_type 'materializedView' AS table_type
FROM pg_matviews FROM pg_matviews
WHERE schemaname = '${db.database}' WHERE schemaname = '${db.database}'
ORDER BY schema_name, ORDER BY schema_name,
@@ -407,8 +409,8 @@ export class PostgreSQLClient extends BaseClient {
name: table.table_name, name: table.table_name,
type: table.table_type === 'VIEW' type: table.table_type === 'VIEW'
? 'view' ? 'view'
: table.table_type === 'materializedview' : table.table_type === 'materializedView'
? 'materializedview' ? 'materializedView'
: 'table', : 'table',
rows: table.reltuples, rows: table.reltuples,
size: tableSize, size: tableSize,
@@ -465,7 +467,7 @@ export class PostgreSQLClient extends BaseClient {
procedures: remappedProcedures, procedures: remappedProcedures,
triggers: remappedTriggers, triggers: remappedTriggers,
triggerFunctions: remappedTriggerFunctions, triggerFunctions: remappedTriggerFunctions,
schedulers: [] schedulers: [] as null[]
}; };
} }
else { else {
@@ -531,7 +533,7 @@ export class PostgreSQLClient extends BaseClient {
return { return {
name: field.column_name, name: field.column_name,
key: null, key: null as null,
type: type.toUpperCase(), type: type.toUpperCase(),
isArray, isArray,
schema: field.table_schema, schema: field.table_schema,
@@ -541,14 +543,14 @@ export class PostgreSQLClient extends BaseClient {
datePrecision: field.datetime_precision, datePrecision: field.datetime_precision,
charLength: field.character_maximum_length, charLength: field.character_maximum_length,
nullable: field.is_nullable.includes('YES'), nullable: field.is_nullable.includes('YES'),
unsigned: null, unsigned: null as null,
zerofill: null, zerofill: null as null,
order: field.ordinal_position, order: field.ordinal_position,
default: field.column_default, default: field.column_default,
charset: field.character_set_name, charset: field.character_set_name,
collation: field.collation_name, collation: field.collation_name,
autoIncrement: false, autoIncrement: false,
onUpdate: null, onUpdate: null as null,
comment: field.column_comment comment: field.column_comment
}; };
}); });
@@ -1251,9 +1253,9 @@ export class PostgreSQLClient extends BaseClient {
return results.rows.map(async row => { return results.rows.map(async row => {
if (!row.pg_get_functiondef) { if (!row.pg_get_functiondef) {
return { return {
definer: null, definer: null as null,
sql: '', sql: '',
parameters: [], parameters: [] as null[],
name: routine, name: routine,
comment: '', comment: '',
security: 'DEFINER', security: 'DEFINER',
@@ -1302,8 +1304,8 @@ export class PostgreSQLClient extends BaseClient {
name: routine, name: routine,
comment: '', comment: '',
security: row.pg_get_functiondef.includes('SECURITY DEFINER') ? 'DEFINER' : 'INVOKER', security: row.pg_get_functiondef.includes('SECURITY DEFINER') ? 'DEFINER' : 'INVOKER',
deterministic: null, deterministic: null as null,
dataAccess: null, dataAccess: null as null,
language: row.pg_get_functiondef.match(/(?<=LANGUAGE )(.*)(?<=[\S+\n\r\s])/gm)[0] language: row.pg_get_functiondef.match(/(?<=LANGUAGE )(.*)(?<=[\S+\n\r\s])/gm)[0]
}; };
})[0]; })[0];
@@ -1367,9 +1369,9 @@ export class PostgreSQLClient extends BaseClient {
return results.rows.map(async row => { return results.rows.map(async row => {
if (!row.pg_get_functiondef) { if (!row.pg_get_functiondef) {
return { return {
definer: null, definer: null as null,
sql: '', sql: '',
parameters: [], parameters: [] as null[],
name: func, name: func,
comment: '', comment: '',
security: 'DEFINER', security: 'DEFINER',
@@ -1417,8 +1419,8 @@ export class PostgreSQLClient extends BaseClient {
name: func, name: func,
comment: '', comment: '',
security: row.pg_get_functiondef.includes('SECURITY DEFINER') ? 'DEFINER' : 'INVOKER', security: row.pg_get_functiondef.includes('SECURITY DEFINER') ? 'DEFINER' : 'INVOKER',
deterministic: null, deterministic: null as null,
dataAccess: null, dataAccess: null as null,
language: row.pg_get_functiondef.match(/(?<=LANGUAGE )(.*)(?<=[\S+\n\r\s])/gm)[0], language: row.pg_get_functiondef.match(/(?<=LANGUAGE )(.*)(?<=[\S+\n\r\s])/gm)[0],
returns: row.pg_get_functiondef.match(/(?<=RETURNS )(.*)(?<=[\S+\n\r\s])/gm)[0].replace('SETOF ', '').toUpperCase() returns: row.pg_get_functiondef.match(/(?<=RETURNS )(.*)(?<=[\S+\n\r\s])/gm)[0].replace('SETOF ', '').toUpperCase()
}; };
@@ -1659,14 +1661,12 @@ export class PostgreSQLClient extends BaseClient {
}; };
if (!args.comments) if (!args.comments)
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments sql = removeComments(sql);
const resultsArr: antares.QueryResult[] = []; const resultsArr: antares.QueryResult[] = [];
let paramsArr = []; let paramsArr = [];
const queries = args.split const queries = args.split
? sql.split(/(?!\B'[^']*);(?![^']*'\B)/gm) ? this._querySplitter(sql, 'pg')
.filter(Boolean)
.map(q => q.trim())
: [sql]; : [sql];
let connection: pg.Client | pg.PoolClient; let connection: pg.Client | pg.PoolClient;

View File

@@ -2,6 +2,7 @@ import * as sqlite from 'better-sqlite3';
import dataTypes from 'common/data-types/sqlite'; import dataTypes from 'common/data-types/sqlite';
import { DATETIME, FLOAT, NUMBER, TIME } from 'common/fieldTypes'; import { DATETIME, FLOAT, NUMBER, TIME } from 'common/fieldTypes';
import * as antares from 'common/interfaces/antares'; import * as antares from 'common/interfaces/antares';
import { removeComments } from 'common/libs/sqlUtils';
import { BaseClient } from './BaseClient'; import { BaseClient } from './BaseClient';
@@ -124,10 +125,10 @@ export class SQLiteClient extends BaseClient {
name: db.name, name: db.name,
size: schemaSize, size: schemaSize,
tables: remappedTables, tables: remappedTables,
functions: [], functions: [] as null[],
procedures: [], procedures: [] as null[],
triggers: remappedTriggers, triggers: remappedTriggers,
schedulers: [] schedulers: [] as null[]
}; };
} }
else { else {
@@ -166,22 +167,22 @@ export class SQLiteClient extends BaseClient {
return { return {
name: field.name, name: field.name,
key: null, key: null as null,
type: type.trim(), type: type.trim(),
schema: schema, schema: schema,
table: table, table: table,
numLength: [...NUMBER, ...FLOAT].includes(type) ? length : null, numLength: [...NUMBER, ...FLOAT].includes(type) ? length : null,
datePrecision: null, datePrecision: null as null,
charLength: ![...NUMBER, ...FLOAT].includes(type) ? length : null, charLength: ![...NUMBER, ...FLOAT].includes(type) ? length : null,
nullable: !field.notnull, nullable: !field.notnull,
unsigned: null, unsigned: null as null,
zerofill: null, zerofill: null as null,
order: typeof field.cid === 'string' ? +field.cid + 1 : field.cid + 1, order: typeof field.cid === 'string' ? +field.cid + 1 : field.cid + 1,
default: field.dflt_value, default: field.dflt_value,
charset: null, charset: null as null,
collation: null, collation: null as null,
autoIncrement: false, autoIncrement: false,
onUpdate: null, onUpdate: null as null,
comment: '' comment: ''
}; };
}); });
@@ -267,7 +268,7 @@ export class SQLiteClient extends BaseClient {
table: table, table: table,
field: field.from, field: field.from,
position: field.id + 1, position: field.id + 1,
constraintPosition: null, constraintPosition: null as null,
constraintName: field.id, constraintName: field.id,
refSchema: schema, refSchema: schema,
refTable: field.table, refTable: field.table,
@@ -624,14 +625,12 @@ export class SQLiteClient extends BaseClient {
}; };
if (!args.comments) if (!args.comments)
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments sql = removeComments(sql);
const resultsArr = []; const resultsArr = [];
let paramsArr = []; let paramsArr = [];
const queries = args.split const queries = args.split
? sql.split(/((?:[^;'"]*(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*')[^;'"]*)+)|;/gm) ? this._querySplitter(sql, 'sqlite')
.filter(Boolean)
.map(q => q.trim())
: [sql]; : [sql];
let connection: sqlite.Database; let connection: sqlite.Database;

View File

@@ -0,0 +1,20 @@
export type LoggerLevel = 'query' | 'error'
export const ipcLogger = ({ content, cUid, level }: {content: string; cUid: string; level: LoggerLevel}) => {
if (level === 'error') {
if (process.type !== undefined) {
const mainWindow = require('electron').webContents.fromId(1);
mainWindow.send('non-blocking-exception', { cUid, message: content, date: new Date() });
}
if (process.env.NODE_ENV === 'development' && process.type === 'browser') console.log(content);
}
else if (level === 'query') {
// Remove comments, newlines and multiple spaces
const escapedSql = content.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '').replace(/\s\s+/g, ' ');
if (process.type !== undefined) {
const mainWindow = require('electron').webContents.fromId(1);
mainWindow.send('query-log', { cUid, sql: escapedSql, date: new Date() });
}
if (process.env.NODE_ENV === 'development' && process.type === 'browser') console.log(escapedSql);
}
};

View File

@@ -43,7 +43,8 @@ async function createMainWindow () {
spellcheck: false spellcheck: false
}, },
autoHideMenuBar: true, autoHideMenuBar: true,
titleBarStyle: isLinux ? 'default' :'hidden', frame: !isLinux,
titleBarStyle: 'hidden',
titleBarOverlay: isWindows titleBarOverlay: isWindows
? { ? {
color: appTheme === 'dark' ? '#3f3f3f' : '#fff', color: appTheme === 'dark' ? '#3f3f3f' : '#fff',
@@ -127,15 +128,25 @@ app.on('ready', async () => {
if (isWindows) if (isWindows)
mainWindow.show(); mainWindow.show();
if (isDevelopment && !isWindows)// Because on Windows you can open devtools from title-bar // if (isDevelopment && !isWindows)
mainWindow.webContents.openDevTools(); // mainWindow.webContents.openDevTools();
process.on('uncaughtException', error => { process.on('uncaughtException', error => {
mainWindow.webContents.send('unhandled-exception', error); if (error instanceof AggregateError) {
for (const e of error.errors)
mainWindow.webContents.send('unhandled-exception', e);
}
else
mainWindow.webContents.send('unhandled-exception', error);
}); });
process.on('unhandledRejection', error => { process.on('unhandledRejection', error => {
mainWindow.webContents.send('unhandled-exception', error); if (error instanceof AggregateError) {
for (const e of error.errors)
mainWindow.webContents.send('unhandled-exception', e);
}
else
mainWindow.webContents.send('unhandled-exception', error);
}); });
}); });

View File

@@ -39,11 +39,11 @@ const props = defineProps({
default: () => 'mdi' default: () => 'mdi'
}, },
flip: { flip: {
type: String as PropType<'horizontal' | 'vertical' | 'both'>, type: String as PropType<'horizontal' | 'vertical' | 'both' | null>,
default: () => null default: () => null
}, },
rotate: { rotate: {
type: Number, type: Number as PropType<number | null>,
default: () => null default: () => null
} }
}); });
@@ -55,8 +55,7 @@ const iconPath = computed(() => {
const base64 = getIconByUid(props.iconName)?.base64; const base64 = getIconByUid(props.iconName)?.base64;
const svgString = Buffer const svgString = Buffer
.from(base64, 'base64') .from(base64, 'base64')
.toString('utf-8') .toString('utf-8');
.replaceAll(/width="[^"]*"|height="[^"]*"/g, '');
return svgString; return svgString;
} }

View File

@@ -21,7 +21,23 @@
<a class="tab-link" @click="selectedTab = 'debug'">{{ t('application.debugConsole') }}</a> <a class="tab-link" @click="selectedTab = 'debug'">{{ t('application.debugConsole') }}</a>
</li> </li>
</ul> </ul>
<button class="btn btn-clear mr-1" @click="resizeConsole(0)" /> <div class="d-flex">
<div
v-if="isDevelopment"
class="c-hand mr-2"
@click="openDevTools()"
>
<BaseIcon icon-name="mdiBugPlayOutline" :size="22" />
</div>
<div
v-if="isDevelopment"
class="c-hand mr-2"
@click="reload()"
>
<BaseIcon icon-name="mdiRefresh" :size="22" />
</div>
<button class="btn btn-clear mr-1" @click="resizeConsole(0)" />
</div>
</div> </div>
<div <div
v-show="selectedTab === 'query'" v-show="selectedTab === 'query'"
@@ -71,6 +87,7 @@
</BaseContextMenu> </BaseContextMenu>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { getCurrentWindow } from '@electron/remote';
import * as moment from 'moment'; import * as moment from 'moment';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { highlight } from 'sql-highlight'; import { highlight } from 'sql-highlight';
@@ -112,6 +129,8 @@ const isHover = ref(false);
const isContext = ref(false); const isContext = ref(false);
const contextContent: Ref<string> = ref(null); const contextContent: Ref<string> = ref(null);
const contextEvent: Ref<MouseEvent> = ref(null); const contextEvent: Ref<MouseEvent> = ref(null);
const w = ref(getCurrentWindow());
const isDevelopment = ref(process.env.NODE_ENV === 'development');
const resize = (e: MouseEvent) => { const resize = (e: MouseEvent) => {
const el = queryConsole.value; const el = queryConsole.value;
@@ -142,6 +161,14 @@ const copyLog = () => {
isContext.value = false; isContext.value = false;
}; };
const openDevTools = () => {
w.value.webContents.openDevTools();
};
const reload = () => {
w.value.reload();
};
watch(workspaceQueryLogs, async () => { watch(workspaceQueryLogs, async () => {
if (!isHover.value) { if (!isHover.value) {
await nextTick(); await nextTick();

View File

@@ -127,7 +127,7 @@ const fakerGroups = computed(() => {
localType.value = 'datetime'; localType.value = 'datetime';
else if (TIME.includes(props.type)) else if (TIME.includes(props.type))
localType.value = 'time'; localType.value = 'time';
else if (UUID.includes(props.type)) else if (UUID.includes(props.type) || (BLOB.includes(props.type) && props.field.key === 'pri'))
localType.value = 'uuid'; localType.value = 'uuid';
else else
localType.value = 'none'; localType.value = 'none';
@@ -177,7 +177,7 @@ const inputProps = () => {
return { type: 'text', mask: datetimeMask }; return { type: 'text', mask: datetimeMask };
} }
if (BLOB.includes(props.type)) if (BLOB.includes(props.type) && props.field.key !== 'pri')
return { type: 'file', mask: false }; return { type: 'file', mask: false };
if (BIT.includes(props.type)) if (BIT.includes(props.type))

View File

@@ -131,8 +131,10 @@ import Application from '@/ipc-api/Application';
import { camelize } from '@/libs/camelize'; import { camelize } from '@/libs/camelize';
import { unproxify } from '@/libs/unproxify'; import { unproxify } from '@/libs/unproxify';
import { SidebarElement, useConnectionsStore } from '@/stores/connections'; import { SidebarElement, useConnectionsStore } from '@/stores/connections';
import { useNotificationsStore } from '@/stores/notifications';
const connectionsStore = useConnectionsStore(); const connectionsStore = useConnectionsStore();
const { addNotification } = useNotificationsStore();
const { addIcon, removeIcon, updateConnectionOrder, getConnectionName } = connectionsStore; const { addIcon, removeIcon, updateConnectionOrder, getConnectionName } = connectionsStore;
const { customIcons } = storeToRefs(connectionsStore); const { customIcons } = storeToRefs(connectionsStore);
@@ -225,12 +227,56 @@ const removeIconHandler = () => {
isContext.value = false; isContext.value = false;
}; };
const adjustSVGContent = (svgContent: string) => {
try {
const parser = new DOMParser();
const doc = parser.parseFromString(svgContent, 'image/svg+xml');
const parseError = doc.querySelector('parsererror');
if (parseError) {
addNotification({ status: 'error', message: parseError.textContent });
return null;
}
const svg = doc.documentElement;
if (svg.tagName.toLowerCase() !== 'svg') {
addNotification({ status: 'error', message: t('application.invalidFIle') });
return null;
}
if (!svg.hasAttribute('viewBox')) {
const width = svg.getAttribute('width') || '36';
const height = svg.getAttribute('height') || '36';
svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
}
svg.removeAttribute('width');
svg.removeAttribute('height');
const serializer = new XMLSerializer();
return serializer.serializeToString(svg);
}
catch (error) {
addNotification({ status: 'error', message: error.stack });
return null;
}
};
const openFile = async () => { const openFile = async () => {
const result = await Application.showOpenDialog({ properties: ['openFile'], filters: [{ name: '"SVG"', extensions: ['svg'] }] }); const result = await Application.showOpenDialog({
properties: ['openFile'],
filters: [{ name: '"SVG"', extensions: ['svg'] }]
});
if (result && !result.canceled) { if (result && !result.canceled) {
const file = result.filePaths[0]; const file = result.filePaths[0];
const content = await Application.readFile({ filePath: file, encoding: 'base64url' }); let content = await Application.readFile({ filePath: file, encoding: 'utf-8' });
addIcon(content);
content = adjustSVGContent(content);
const base64Content = Buffer.from(content).toString('base64');
addIcon(base64Content);
} }
}; };

View File

@@ -339,6 +339,8 @@ onMounted(() => {
for (const field of props.fields) { for (const field of props.fields) {
if (typeof props.rowToDuplicate[field.name] !== 'object') if (typeof props.rowToDuplicate[field.name] !== 'object')
rowObj[field.name] = { value: props.rowToDuplicate[field.name] }; rowObj[field.name] = { value: props.rowToDuplicate[field.name] };
else if (field.type === 'JSON')
rowObj[field.name] = { value: JSON.stringify(props.rowToDuplicate[field.name]) };
if (field.autoIncrement || !!field.onUpdate)// Disable by default auto increment or "on update" fields if (field.autoIncrement || !!field.onUpdate)// Disable by default auto increment or "on update" fields
fieldsToExclude.value = [...fieldsToExclude.value, field.name]; fieldsToExclude.value = [...fieldsToExclude.value, field.name];

View File

@@ -42,7 +42,7 @@
tabindex="0" tabindex="0"
> >
<div class="td py-1"> <div class="td py-1">
{{ t(shortcutEvents[shortcut.event].l18n, {param: shortcutEvents[shortcut.event].l18nParam}) }} {{ t(shortcutEvents[shortcut.event].i18n, {param: shortcutEvents[shortcut.event].i18nParam}) }}
</div> </div>
<div <div
class="td py-1" class="td py-1"
@@ -167,7 +167,7 @@
</template> </template>
<template #body> <template #body>
<div class="mb-2"> <div class="mb-2">
{{ t('general.deleteConfirm') }} <b>{{ t(shortcutEvents[shortcutToDelete.event].l18n, {param: shortcutEvents[shortcutToDelete.event].l18nParam}) }} (<span v-html="parseKeys(shortcutToDelete.keys)" />)</b>? {{ t('general.deleteConfirm') }} <b>{{ t(shortcutEvents[shortcutToDelete.event].i18n, {param: shortcutEvents[shortcutToDelete.event].i18nParam}) }} (<span v-html="parseKeys(shortcutToDelete.keys)" />)</b>?
</div> </div>
</template> </template>
</ConfirmModal> </ConfirmModal>
@@ -233,7 +233,7 @@ const { shortcuts } = storeToRefs(settingsStore);
const eventOptions = computed(() => { const eventOptions = computed(() => {
return Object.keys(shortcutEvents) return Object.keys(shortcutEvents)
.map(key => { .map(key => {
return { value: key, label: t(shortcutEvents[key].l18n, { param: shortcutEvents[key].l18nParam }) }; return { value: key, label: t(shortcutEvents[key].i18n, { param: shortcutEvents[key].i18nParam }) };
}) })
.sort((a, b) => { .sort((a, b) => {
if (a.label < b.label) return -1; if (a.label < b.label) return -1;

View File

@@ -47,65 +47,50 @@
<div class="tile-history-buttons"> <div class="tile-history-buttons">
<button <button
v-if="note.type === 'todo' && !note.isArchived" v-if="note.type === 'todo' && !note.isArchived"
class="btn btn-link pl-1" class="btn btn-dark tooltip tooltip-left"
:data-tooltip="t('general.archive')"
@click.stop="$emit('archive-note', note.uid)" @click.stop="$emit('archive-note', note.uid)"
> >
<BaseIcon <BaseIcon icon-name="mdiCheck" :size="22" />
icon-name="mdiCheck"
class="pr-1"
:size="22"
/> {{ t('general.archive') }}
</button> </button>
<button <button
v-if="note.type === 'todo' && note.isArchived" v-if="note.type === 'todo' && note.isArchived"
class="btn btn-link pl-1" class="btn btn-dark tooltip tooltip-left"
:data-tooltip="t('general.undo')"
@click.stop="$emit('restore-note', note.uid)" @click.stop="$emit('restore-note', note.uid)"
> >
<BaseIcon <BaseIcon icon-name="mdiRestore" :size="22" />
icon-name="mdiRestore"
class="pr-1"
:size="22"
/> {{ t('general.undo') }}
</button> </button>
<button <button
v-if="note.type === 'query'" v-if="note.type === 'query'"
class="btn btn-link pl-1" class="btn btn-dark tooltip tooltip-left"
:data-tooltip="t('general.select')"
@click.stop="$emit('select-query', note.note)" @click.stop="$emit('select-query', note.note)"
> >
<BaseIcon <BaseIcon icon-name="mdiOpenInApp" :size="22" />
icon-name="mdiOpenInApp"
class="pr-1"
:size="22"
/> {{ t('general.select') }}
</button> </button>
<button <button
v-if="note.type === 'query'" v-if="note.type === 'query'"
class="btn btn-link pl-1" class="btn btn-dark tooltip tooltip-left"
:data-tooltip="t('general.copy')"
@click.stop="copyText(note.note)" @click.stop="copyText(note.note)"
> >
<BaseIcon <BaseIcon icon-name="mdiContentCopy" :size="18" />
icon-name="mdiContentCopy"
class="pr-1"
:size="22"
/> {{ t('general.copy') }}
</button> </button>
<button <button
v-if=" !note.isArchived" v-if=" !note.isArchived"
class="btn btn-link pl-1" class="btn btn-dark tooltip tooltip-left"
:data-tooltip="t('general.edit')"
@click.stop="$emit('edit-note')" @click.stop="$emit('edit-note')"
> >
<BaseIcon <BaseIcon icon-name="mdiPencil" :size="22" />
icon-name="mdiPencil"
class="pr-1"
:size="22"
/> {{ t('general.edit') }}
</button> </button>
<button class="btn btn-link pl-1" @click.stop="$emit('delete-note', note.uid)"> <button
<BaseIcon class="btn btn-dark tooltip tooltip-left"
icon-name="mdiDeleteForever" :data-tooltip="t('general.delete')"
class="pr-1" @click.stop="$emit('delete-note', note.uid)"
:size="22" >
/> {{ t('general.delete') }} <BaseIcon icon-name="mdiDeleteForever" :size="22" />
</button> </button>
</div> </div>
</div> </div>
@@ -278,11 +263,14 @@ const highlightWord = (string: string) => {
button { button {
font-size: 0.7rem; font-size: 0.7rem;
height: 1rem;
line-height: 1rem; line-height: 1rem;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin: 0 5px;
padding: 0;
height: 24px;
width: 24px;
} }
} }
} }

View File

@@ -1,6 +1,5 @@
<template> <template>
<div <div
v-if="!isLinux"
id="titlebar" id="titlebar"
@dblclick="toggleFullScreen" @dblclick="toggleFullScreen"
> >
@@ -21,16 +20,27 @@
class="titlebar-element" class="titlebar-element"
@click="openDevTools" @click="openDevTools"
> >
<BaseIcon icon-name="mdiBugPlayOutline" :size="24" /> <BaseIcon icon-name="mdiBugPlayOutline" :size="18" />
</div> </div>
<div <div
v-if="isDevelopment" v-if="isDevelopment"
class="titlebar-element" class="titlebar-element"
@click="reload" @click="reload"
> >
<BaseIcon icon-name="mdiRefresh" :size="24" /> <BaseIcon icon-name="mdiRefresh" :size="18" />
</div> </div>
<div v-if="isWindows" :style="'width: 140px;'" /> <div v-if="isWindows" :style="'width: 140px;'" />
<div v-if="isLinux" class="d-flex">
<div class="titlebar-element" @click="minimize">
<BaseIcon icon-name="mdiWindowMinimize" :size="18" />
</div>
<div class="titlebar-element" @click="toggleFullScreen">
<BaseIcon :icon-name="isMaximized ? 'mdiWindowRestore' : 'mdiWindowMaximize'" :size="18" />
</div>
<div class="titlebar-element" @click="closeApp">
<BaseIcon icon-name="mdiClose" :size="18" />
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
@@ -74,6 +84,18 @@ const windowTitle = computed(() => {
return [connectionName, ...breadcrumbs].join(' • '); return [connectionName, ...breadcrumbs].join(' • ');
}); });
const openDevTools = () => {
w.value.webContents.openDevTools();
};
const reload = () => {
w.value.reload();
};
const minimize = () => {
w.value.minimize();
};
const toggleFullScreen = () => { const toggleFullScreen = () => {
if (isMaximized.value) if (isMaximized.value)
w.value.unmaximize(); w.value.unmaximize();
@@ -81,12 +103,8 @@ const toggleFullScreen = () => {
w.value.maximize(); w.value.maximize();
}; };
const openDevTools = () => { const closeApp = () => {
w.value.webContents.openDevTools(); ipcRenderer.send('close-app');
};
const reload = () => {
w.value.reload();
}; };
const onResize = () => { const onResize = () => {

View File

@@ -64,7 +64,7 @@
> >
<BaseIcon <BaseIcon
class="mt-1 mr-1" class="mt-1 mr-1"
:icon-name="['view', 'materializedview'].includes(element.elementType) ? 'mdiTableEye' : 'mdiTable'" :icon-name="['view', 'materializedView'].includes(element.elementType) ? 'mdiTableEye' : 'mdiTable'"
:size="18" :size="18"
/> />
<span :title="`${t('general.data').toUpperCase()}: ${t(`database.${element.elementType}`)}`"> <span :title="`${t('general.data').toUpperCase()}: ${t(`database.${element.elementType}`)}`">
@@ -81,7 +81,7 @@
<a v-else-if="element.type === 'data'" class="tab-link"> <a v-else-if="element.type === 'data'" class="tab-link">
<BaseIcon <BaseIcon
class="mt-1 mr-1" class="mt-1 mr-1"
:icon-name="['view', 'materializedview'].includes(element.elementType) ? 'mdiTableEye' : 'mdiTable'" :icon-name="['view', 'materializedView'].includes(element.elementType) ? 'mdiTableEye' : 'mdiTable'"
:size="18" :size="18"
/> />
<span :title="`${t('general.data').toUpperCase()}: ${t(`database.${element.elementType}`)}`"> <span :title="`${t('general.data').toUpperCase()}: ${t(`database.${element.elementType}`)}`">

View File

@@ -67,7 +67,7 @@
<div class="column col-7 col-sm-12"> <div class="column col-7 col-sm-12">
<input <input
ref="pgString" ref="pgString"
v-model="connection.pgConnString" v-model="connection.connString"
class="form-input" class="form-input"
type="text" type="text"
> >
@@ -502,8 +502,8 @@ const connection = ref({
sshKey: '', sshKey: '',
sshPort: 22, sshPort: 22,
sshKeepAliveInterval: 1800, sshKeepAliveInterval: 1800,
pgConnString: '' connString: ''
}) as Ref<ConnectionParams & { pgConnString: string }>; }) as Ref<ConnectionParams & { connString: string }>;
const firstInput: Ref<HTMLInputElement> = ref(null); const firstInput: Ref<HTMLInputElement> = ref(null);
const isConnecting = ref(false); const isConnecting = ref(false);

View File

@@ -68,7 +68,7 @@
<div class="column col-7 col-sm-12"> <div class="column col-7 col-sm-12">
<input <input
ref="pgString" ref="pgString"
v-model="localConnection.pgConnString" v-model="localConnection.connString"
class="form-input" class="form-input"
type="text" type="text"
> >
@@ -502,7 +502,7 @@ const clients = [
]; ];
const firstInput: Ref<HTMLInputElement> = ref(null); const firstInput: Ref<HTMLInputElement> = ref(null);
const localConnection: Ref<ConnectionParams & { pgConnString: string }> = ref(null); const localConnection: Ref<ConnectionParams & { connString: string }> = ref(null);
const isConnecting = ref(false); const isConnecting = ref(false);
const isTesting = ref(false); const isTesting = ref(false);
const isAsking = ref(false); const isAsking = ref(false);
@@ -527,14 +527,14 @@ watch(() => props.connection, () => {
localConnection.value = JSON.parse(JSON.stringify(props.connection)); localConnection.value = JSON.parse(JSON.stringify(props.connection));
}); });
const startConnection = async () => { const startConnection = async (): Promise<void> => {
await saveConnection(); await saveConnection();
isConnecting.value = true; isConnecting.value = true;
if (localConnection.value.ask) if (localConnection.value.ask)
isAsking.value = true; isAsking.value = true;
else { else {
await connectWorkspace(localConnection.value, { signal: abortController.value.signal }).catch(() => undefined); await connectWorkspace(localConnection.value, { signal: abortController.value.signal }).catch((): void => undefined);
isConnecting.value = false; isConnecting.value = false;
} }
}; };
@@ -582,7 +582,7 @@ const continueTest = async (credentials: {user: string; password: string }) => {
try { try {
if (isConnecting.value) { if (isConnecting.value) {
const params = Object.assign({}, props.connection, credentials); const params = Object.assign({}, props.connection, credentials);
await connectWorkspace(params, { signal: abortController.value.signal }).catch(() => undefined); await connectWorkspace(params, { signal: abortController.value.signal }).catch((): void => undefined);
isConnecting.value = false; isConnecting.value = false;
} }
else { else {

View File

@@ -143,7 +143,7 @@
:selected-schema="selectedSchema" :selected-schema="selectedSchema"
:context-event="miscContextEvent" :context-event="miscContextEvent"
@open-create-view-tab="openCreateElementTab('view')" @open-create-view-tab="openCreateElementTab('view')"
@open-create-materializedview-tab="openCreateElementTab('materialized-view')" @open-create-materializedView-tab="openCreateElementTab('materialized-view')"
@open-create-trigger-tab="openCreateElementTab('trigger')" @open-create-trigger-tab="openCreateElementTab('trigger')"
@open-create-trigger-function-tab="openCreateElementTab('trigger-function')" @open-create-trigger-function-tab="openCreateElementTab('trigger-function')"
@open-create-routine-tab="openCreateElementTab('routine')" @open-create-routine-tab="openCreateElementTab('routine')"

View File

@@ -16,9 +16,9 @@
/> {{ t('database.createNewView') }}</span> /> {{ t('database.createNewView') }}</span>
</div> </div>
<div <div
v-if="props.selectedMisc === 'materializedview'" v-if="props.selectedMisc === 'materializedView'"
class="context-element" class="context-element"
@click="emit('open-create-materializedview-tab')" @click="emit('open-create-materializedView-tab')"
> >
<span class="d-flex"> <span class="d-flex">
<BaseIcon <BaseIcon
@@ -106,7 +106,7 @@ const props = defineProps({
const emit = defineEmits([ const emit = defineEmits([
'open-create-view-tab', 'open-create-view-tab',
'open-create-materializedview-tab', 'open-create-materializedView-tab',
'open-create-trigger-tab', 'open-create-trigger-tab',
'open-create-routine-tab', 'open-create-routine-tab',
'open-create-function-tab', 'open-create-function-tab',

View File

@@ -121,7 +121,7 @@
<summary <summary
class="accordion-header misc-name" class="accordion-header misc-name"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger}" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger}"
@contextmenu.prevent="showMiscFolderContext($event, 'materializedview')" @contextmenu.prevent="showMiscFolderContext($event, 'materializedView')"
> >
<BaseIcon <BaseIcon
class="misc-icon mr-1" class="misc-icon mr-1"
@@ -133,7 +133,7 @@
icon-name="mdiFolderOpen" icon-name="mdiFolderOpen"
:size="18" :size="18"
/> />
{{ t('database.materializedview', 2) }} {{ t('database.materializedView', 2) }}
</summary> </summary>
<div class="accordion-body"> <div class="accordion-body">
<div> <div>
@@ -477,8 +477,12 @@ const searchTerm = computed(() => {
}); });
const filteredTables = computed(() => { const filteredTables = computed(() => {
if (props.searchMethod === 'elements') if (props.searchMethod === 'elements') {
return props.database.tables.filter(table => table.name.search(searchTerm.value) >= 0 && table.type === 'table'); const searchTermLower = searchTerm.value.toLowerCase();
return props.database.tables.filter(table =>
table.name.toLowerCase().includes(searchTermLower) && table.type === 'table'
);
}
else else
return props.database.tables; return props.database.tables;
}); });
@@ -492,9 +496,9 @@ const filteredViews = computed(() => {
const filteredMatViews = computed(() => { const filteredMatViews = computed(() => {
if (props.searchMethod === 'elements') if (props.searchMethod === 'elements')
return props.database.tables.filter(table => table.name.search(searchTerm.value) >= 0 && table.type === 'materializedview'); return props.database.tables.filter(table => table.name.search(searchTerm.value) >= 0 && table.type === 'materializedView');
else else
return props.database.tables.filter(table => table.type === 'materializedview'); return props.database.tables.filter(table => table.type === 'materializedView');
}); });
const filteredTriggers = computed(() => { const filteredTriggers = computed(() => {

View File

@@ -50,7 +50,7 @@
class="text-light mt-1 mr-1" class="text-light mt-1 mr-1"
icon-name="mdiTableEye" icon-name="mdiTableEye"
:size="18" :size="18"
/> {{ t('database.materializedview') }}</span> /> {{ t('database.materializedView') }}</span>
</div> </div>
<div <div
v-if="workspace.customizations.triggerAdd" v-if="workspace.customizations.triggerAdd"

View File

@@ -48,7 +48,7 @@
/> {{ t('application.settings') }}</span> /> {{ t('application.settings') }}</span>
</div> </div>
<div <div
v-if="selectedTable && selectedTable.type === 'materializedview' && customizations.materializedViewSettings" v-if="selectedTable && selectedTable.type === 'materializedView' && customizations.materializedViewSettings"
class="context-element" class="context-element"
@click="openMaterializedViewSettingTab" @click="openMaterializedViewSettingTab"
> >

View File

@@ -195,7 +195,7 @@ const saveChanges = async () => {
uid: props.connection.uid, uid: props.connection.uid,
schema: props.schema, schema: props.schema,
elementName: localView.value.name, elementName: localView.value.name,
elementType: 'materializedview', elementType: 'materializedView',
type: 'materialized-view-props' type: 'materialized-view-props'
}); });

View File

@@ -73,6 +73,7 @@
<span>{{ t('database.foreignKeys') }}</span> <span>{{ t('database.foreignKeys') }}</span>
</button> </button>
<button <button
v-if="workspace.customizations.tableCheck"
class="btn btn-dark btn-sm ml-2 mr-0" class="btn btn-dark btn-sm ml-2 mr-0"
:disabled="isSaving || !localFields.length" :disabled="isSaving || !localFields.length"
:title="t('database.manageTableChecks')" :title="t('database.manageTableChecks')"

View File

@@ -227,7 +227,7 @@ const saveChanges = async () => {
schema: props.schema, schema: props.schema,
elementName: oldName, elementName: oldName,
elementNewName: localView.value.name, elementNewName: localView.value.name,
elementType: 'materializedview' elementType: 'materializedView'
}); });
changeBreadcrumbs({ schema: props.schema, view: localView.value.name }); changeBreadcrumbs({ schema: props.schema, view: localView.value.name });

View File

@@ -73,6 +73,7 @@
<span>{{ t('database.foreignKeys') }}</span> <span>{{ t('database.foreignKeys') }}</span>
</button> </button>
<button <button
v-if="workspace.customizations.tableCheck"
class="btn btn-dark btn-sm ml-2 mr-0" class="btn btn-dark btn-sm ml-2 mr-0"
:disabled="isSaving" :disabled="isSaving"
:title="t('database.manageTableChecks')" :title="t('database.manageTableChecks')"
@@ -457,6 +458,8 @@ const getFieldsData = async () => {
addNotification({ status: 'error', message: err.stack }); addNotification({ status: 'error', message: err.stack });
} }
isLoading.value = false;
if (workspace.value.customizations.tableCheck) { if (workspace.value.customizations.tableCheck) {
try { // Table checks try { // Table checks
const { status, response } = await Tables.getTableChecks(params); const { status, response } = await Tables.getTableChecks(params);
@@ -477,8 +480,6 @@ const getFieldsData = async () => {
addNotification({ status: 'error', message: err.stack }); addNotification({ status: 'error', message: err.stack });
} }
} }
isLoading.value = false;
}; };
const saveChanges = async () => { const saveChanges = async () => {

View File

@@ -745,7 +745,7 @@ const saveFileAs = async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const result: any = await Application.showSaveDialog({ const result: any = await Application.showSaveDialog({
filters: [{ name: 'SQL', extensions: ['sql'] }], filters: [{ name: 'SQL', extensions: ['sql'] }],
defaultPath: (!queryName.value.includes('.sql') ? `${queryName.value}.sql` :queryName.value) || 'query.sql' defaultPath: (queryName.value !== undefined && !queryName.value.includes('.sql') ? `${queryName.value}.sql` : queryName.value) || 'query.sql'
}); });
if (result && !result.canceled) { if (result && !result.canceled) {

View File

@@ -7,7 +7,7 @@
:key="i" :key="i"
class="mb-4" class="mb-4"
> >
{{ t(shortcutEvents[shortcut.event].l18n, {param: shortcutEvents[shortcut.event].l18nParam}) }} {{ t(shortcutEvents[shortcut.event].i18n, {param: shortcutEvents[shortcut.event].i18nParam}) }}
</div> </div>
</div> </div>
<div class="column col-16"> <div class="column col-16">

View File

@@ -284,7 +284,7 @@ const settingsStore = useSettingsStore();
const consoleStore = useConsoleStore(); const consoleStore = useConsoleStore();
const { getWorkspace } = useWorkspacesStore(); const { getWorkspace } = useWorkspacesStore();
const { dataTabLimit: pageSize, defaultCopyType } = storeToRefs(settingsStore); const { /* dataTabLimit: pageSize, */ defaultCopyType } = storeToRefs(settingsStore);
const { consoleHeight } = storeToRefs(consoleStore); const { consoleHeight } = storeToRefs(consoleStore);
@@ -357,37 +357,37 @@ const isSortable = computed(() => {
return fields.value.every(field => field.name); return fields.value.every(field => field.name);
}); });
const isHardSort = computed(() => { // const isHardSort = computed(() => {
return props.mode === 'table' && localResults.value.length === pageSize.value; // return props.mode === 'table' && localResults.value.length === pageSize.value;
}); // });
const sortedResults = computed(() => { const sortedResults = computed(() => {
if (currentSort.value[resultsetIndex.value] && !isHardSort.value) { // if (currentSort.value[resultsetIndex.value] && !isHardSort.value) {
const sortObj = currentSort.value[resultsetIndex.value]; // const sortObj = currentSort.value[resultsetIndex.value];
return [...localResults.value].sort((a: any, b: any) => { // return [...localResults.value].sort((a: any, b: any) => {
const modifier = sortObj.dir === 'desc' ? -1 : 1; // const modifier = sortObj.dir === 'desc' ? -1 : 1;
let valA = a[sortObj.field]; // let valA = a[sortObj.field];
let valB = b[sortObj.field]; // let valB = b[sortObj.field];
// Handle null values // // Handle null values
if (valA === null && valB !== null) return sortObj.dir === 'asc' ? -1 : 1; // if (valA === null && valB !== null) return sortObj.dir === 'asc' ? -1 : 1;
if (valA !== null && valB === null) return sortObj.dir === 'asc' ? 1 : -1; // if (valA !== null && valB === null) return sortObj.dir === 'asc' ? 1 : -1;
if (valA === null && valB === null) return 0; // if (valA === null && valB === null) return 0;
valA = typeof valA === 'string' ? valA.toLowerCase() : valA; // valA = typeof valA === 'string' ? valA.toLowerCase() : valA;
valB = typeof valB === 'string' ? valB.toLowerCase() : valB; // valB = typeof valB === 'string' ? valB.toLowerCase() : valB;
if (typeof valA !== 'number' && !isNaN(valA)) valA = String(Number(valA)); // if (typeof valA !== 'number' && !isNaN(valA)) valA = String(Number(valA));
if (typeof valB !== 'number' && !isNaN(valB)) valB = String(Number(valB)); // if (typeof valB !== 'number' && !isNaN(valB)) valB = String(Number(valB));
if (valA < valB) return -1 * modifier; // if (valA < valB) return -1 * modifier;
if (valA > valB) return 1 * modifier; // if (valA > valB) return 1 * modifier;
return 0; // return 0;
}); // });
} // }
else // else
return localResults.value; return localResults.value;
}); });
const resultsWithRows = computed(() => props.results.filter(result => result.rows.length)); const resultsWithRows = computed(() => props.results.filter(result => result.rows.length));
@@ -538,6 +538,7 @@ const closeContext = () => {
}; };
const showDeleteConfirmModal = (e: any) => { const showDeleteConfirmModal = (e: any) => {
if (e && e.code !== 'Delete') return;
if (e && e.path && ['INPUT', 'TEXTAREA', 'SELECT'].includes(e.path[0].tagName)) if (e && e.path && ['INPUT', 'TEXTAREA', 'SELECT'].includes(e.path[0].tagName))
return; return;
if (selectedRows.value.length === 0) return; if (selectedRows.value.length === 0) return;
@@ -562,6 +563,7 @@ const deleteSelected = () => {
table: getTable(resultsetIndex.value), table: getTable(resultsetIndex.value),
rows rows
}; };
console.log(params);
emit('delete-selected', params); emit('delete-selected', params);
}; };
@@ -695,15 +697,15 @@ const fillCell = (event: { name: string; group: string; type: string }) => {
} }
fakeValue = (fakerCustom as any)[event.group][event.name](); fakeValue = (fakerCustom as any)[event.group][event.name]();
if (['string', 'number'].includes(typeof fakeValue)) { const isDateType = [...DATE, ...DATETIME].includes(selectedCell.value.type);
if (isDateType)
fakeValue = moment(fakeValue).format(`YYYY-MM-DD HH:mm:ss${datePrecision}`);
else if (['string', 'number'].includes(typeof fakeValue)) {
if (typeof fakeValue === 'number') if (typeof fakeValue === 'number')
fakeValue = String(fakeValue); fakeValue = String(fakeValue);
if (selectedCell.value.length) if (selectedCell.value.length)
fakeValue = fakeValue.substring(0, selectedCell.value.length < 1024 ? Number(selectedCell.value.length) : 1024); fakeValue = fakeValue.substring(0, selectedCell.value.length < 1024 ? Number(selectedCell.value.length) : 1024);
} }
else if ([...DATE, ...DATETIME].includes(selectedCell.value.type))
fakeValue = moment(fakeValue).format(`YYYY-MM-DD HH:mm:ss${datePrecision}`);
else if (TIME.includes(selectedCell.value.type)) else if (TIME.includes(selectedCell.value.type))
fakeValue = moment(fakeValue).format(`HH:mm:ss${datePrecision}`); fakeValue = moment(fakeValue).format(`HH:mm:ss${datePrecision}`);
@@ -827,12 +829,12 @@ const sort = (field: TableField) => {
}; };
} }
if (isHardSort.value) { // if (isHardSort.value) {
emit('hard-sort', { emit('hard-sort', {
field: currentSort.value[resultsetIndex.value].field, field: currentSort.value[resultsetIndex.value].field,
dir: currentSort.value[resultsetIndex.value].dir dir: currentSort.value[resultsetIndex.value].dir
}); });
} // }
}; };
const resetSort = () => { const resetSort = () => {

View File

@@ -440,7 +440,7 @@ const editON = async (field: string) => {
return; return;
} }
if (BLOB.includes(type)) { if (BLOB.includes(type) && props.fields[field].key !== 'pri') {
isBlobEditor.value = true; isBlobEditor.value = true;
editingContent.value = content || ''; editingContent.value = content || '';
fileToUpload.value = null; fileToUpload.value = null;
@@ -458,9 +458,12 @@ const editON = async (field: string) => {
}; };
} }
} }
emit('start-editing', field); emit('start-editing', field);
return; return;
} }
else if (BLOB.includes(type) && props.fields[field].key === 'pri')// Disable edit on BLOB primary until we are sure it's not problematic
return;
// Inline editable fields // Inline editable fields
editingContent.value = originalContent.value; editingContent.value = originalContent.value;

View File

@@ -56,10 +56,10 @@ export function useResultTables (uid: string, reloadTable: () => void) {
if (status === 'success') if (status === 'success')
reloadTable(); reloadTable();
else else
this.addNotification({ status: 'error', message: response }); addNotification({ status: 'error', message: response });
} }
catch (err) { catch (err) {
this.addNotification({ status: 'error', message: err.stack }); addNotification({ status: 'error', message: err.stack });
isQuering.value = false; isQuering.value = false;
} }
} }

View File

@@ -141,7 +141,7 @@ export const csCZ = {
total: 'Celkem', total: 'Celkem',
table: 'Tabulka | Tabulky', table: 'Tabulka | Tabulky',
view: 'Pohled | Pohledy', view: 'Pohled | Pohledy',
materializedview: 'Materializovaný pohled', materializedView: 'Materializovaný pohled',
definer: 'Definér', definer: 'Definér',
algorithm: 'Algoritmus', algorithm: 'Algoritmus',
trigger: 'Trigger | Triggery', trigger: 'Trigger | Triggery',

View File

@@ -141,7 +141,7 @@ export const enUS = {
total: 'Total', total: 'Total',
table: 'Table | Tables', table: 'Table | Tables',
view: 'View | Views', view: 'View | Views',
materializedview: 'Materialized view | Materialized views', materializedView: 'Materialized view | Materialized views',
definer: 'Definer', definer: 'Definer',
algorithm: 'Algorithm', algorithm: 'Algorithm',
trigger: 'Trigger | Triggers', trigger: 'Trigger | Triggers',
@@ -401,6 +401,7 @@ export const enUS = {
ignoreDuplicates: 'Ignore duplicates', ignoreDuplicates: 'Ignore duplicates',
wrongImportPassword: 'Wrong import password', wrongImportPassword: 'Wrong import password',
wrongFileFormat: 'Wrong file format', wrongFileFormat: 'Wrong file format',
invalidFile: 'Invalid file',
dataImportSuccess: 'Data successfully imported', dataImportSuccess: 'Data successfully imported',
note: 'Note | Notes', note: 'Note | Notes',
thereAreNoNotesYet: 'There are no notes yet', thereAreNoNotesYet: 'There are no notes yet',
@@ -416,7 +417,11 @@ export const enUS = {
openNotes: 'Open notes', openNotes: 'Open notes',
debugConsole: 'Debug console', // <- console tab name debugConsole: 'Debug console', // <- console tab name
executedQueries: 'Executed queries', // <- console tab name executedQueries: 'Executed queries', // <- console tab name
sizeLimitError: 'Maximum size of {size} exceeded' sizeLimitError: 'Maximum size of {size} exceeded',
fullScreen: 'Full screen',
zoomIn: 'Zoom in',
zoomOut: 'Zoom out',
zoomReset: 'Reset zoom'
}, },
faker: { // Faker.js methods, used in random generated content faker: { // Faker.js methods, used in random generated content
address: 'Address', address: 'Address',

View File

@@ -1,5 +1,14 @@
/**
* [TRANSLATION UPDATE HELPER]
* - Open a terminal in antares folder and run `npm run translation:check short-code` replacing short-code with the one you are updating.
* - The command will output which terms are missing or not translated from english.
* - Open antares folder with your editor of choice.
* - Go to antares/src/renderer/i18n/ and open the locale file you want to translate.
* - Add and translate missing terms and consider whether to translate untranslated terms.
*/
export const esES = { export const esES = {
general: { general: { // General purpose terms
edit: 'Editar', edit: 'Editar',
save: 'Guardar', save: 'Guardar',
close: 'Cerrar', close: 'Cerrar',
@@ -8,6 +17,7 @@ export const esES = {
cancel: 'Cancelar', cancel: 'Cancelar',
send: 'Enviar', send: 'Enviar',
refresh: 'Refrescar', refresh: 'Refrescar',
autoRefresh: 'Auto refresco',
version: 'Versión', version: 'Versión',
donate: 'Donar', donate: 'Donar',
run: 'Ejecutar', run: 'Ejecutar',
@@ -18,12 +28,62 @@ export const esES = {
add: 'Añadir', add: 'Añadir',
data: 'Datos', data: 'Datos',
properties: 'Propiedades', properties: 'Propiedades',
name: 'Nombre',
clear: 'Limpiar',
options: 'Opciones',
insert: 'Insertar', insert: 'Insertar',
discard: 'Descartar',
stay: 'Mantener',
author: 'Autor',
upload: 'Subir',
browse: 'Explorar',
content: 'Contenido',
cut: 'Cortar',
copy: 'Copiar',
paste: 'Pegar',
duplicate: 'Duplicar',
tools: 'Herramientas',
seconds: 'Segundos', seconds: 'Segundos',
deleteConfirm: 'Confirmas la cancelación de', all: 'Todos',
uploadFile: 'Cargar fichero' new: 'Nuevo',
select: 'Seleccionar',
change: 'Cambiar',
include: 'Agregar',
includes: 'Agregados',
completed: 'Completado',
aborted: 'Cancelado',
disabled: 'Deshabilitado',
enable: 'Habilitar',
disable: 'Desabilitar',
contributors: 'Colaboradores',
pin: 'Fijar',
unpin: 'Desfijar',
folder: 'Carpeta | Carpetas',
none: 'Ninguno',
singleQuote: 'Comillas simples',
doubleQuote: 'Comillas dobles',
deleteConfirm: 'Confirmar la cancelación de',
uploadFile: 'Cargar fichero',
format: 'Formato', // Format code
history: 'Histórico',
filter: 'Filtro',
manualValue: 'Valor manual',
selectAll: 'Seleccionar todo',
pageNumber: 'Número de página',
directoryPath: 'Ruta de directorio',
actionSuccessful: '{action} exitoso',
outputFormat: 'Formato de salida',
singleFile: 'Fichero {ext} único',
zipCompressedFile: 'Fichero ZIP {ext} comprimido',
copyName: 'Copiar nombre',
search: 'Búsqueda',
title: 'Título',
archive: 'Archivo', // verb
undo: 'Deshacer',
moveTo: 'Mover a'
}, },
connection: { connection: { // Database connection
connection: 'Conexión',
connectionName: 'Nombre de la conexión', connectionName: 'Nombre de la conexión',
client: 'Cliente', client: 'Cliente',
hostName: 'Servidor', hostName: 'Servidor',
@@ -31,10 +91,21 @@ export const esES = {
user: 'Usuario', user: 'Usuario',
password: 'Contraseña', password: 'Contraseña',
credentials: 'Credenciales', credentials: 'Credenciales',
connect: 'Connectar', connect: 'Conectar',
connected: 'Conectado', connected: 'Conectado',
disconnect: 'Desconectar', disconnect: 'Desconectar',
disconnected: 'Desconectado', disconnected: 'Desconectado',
ssl: 'SSL',
enableSsl: 'Habilitar SSL',
privateKey: 'Clave privada',
certificate: 'Certificado',
caCertificate: 'Certificado CA',
ciphers: 'Cifrado',
untrustedConnection: 'Conexión no confiable',
passphrase: 'Frase de paso',
sshTunnel: 'Túnel SSH',
enableSsh: 'Habilitar SSH',
connectionString: 'Cadena de conexión',
addConnection: 'Añadir conexión', addConnection: 'Añadir conexión',
createConnection: 'Crear conexión', createConnection: 'Crear conexión',
createNewConnection: 'Crear nueva conexión', createNewConnection: 'Crear nueva conexión',
@@ -42,26 +113,216 @@ export const esES = {
testConnection: 'Comprobar conexión', testConnection: 'Comprobar conexión',
editConnection: 'Editar conexión', editConnection: 'Editar conexión',
deleteConnection: 'Eliminar conexión', deleteConnection: 'Eliminar conexión',
connectionSuccessfullyMade: 'Conexión realizada correctamente!' connectionSuccessfullyMade: 'Conexión realizada correctamente!',
readOnlyMode: 'Solo lectura',
allConnections: 'Todas las conexiones',
searchForConnections: 'Buscar por conexiones',
keepAliveInterval: 'Mantenimiento de conexión',
singleConnection: 'Conexión única'
}, },
database: { database: { // Database related terms
schema: 'Esquema', schema: 'Esquema',
type: 'Tipo', type: 'Tipo',
unableEditFieldWithoutPrimary: 'No se puede editar una campo sin Llave Primaria en el registro', insert: 'Insertar',
editCell: 'Editar celda', indexes: 'Índices',
foreignKeys: 'Claves Foráneas',
tablaChecks: 'Validación de tabla',
length: 'Longitud',
unsigned: 'Sin signo',
default: 'Por defecto',
comment: 'Comentario',
key: 'Clave | Claves',
order: 'Orden',
expression: 'Expresión',
autoIncrement: 'Autoincremental',
engine: 'Motor',
field: 'Campo | Campos',
approximately: 'Aproximadamente',
total: 'Total',
table: 'Tabla | Tablas',
view: 'Vista | Vistas',
materializedView: 'Vista Materializada | Vistas Materializadas',
definer: 'Definidor',
algorithm: 'Algoritmo',
trigger: 'Disparador | Disparadores',
storedRoutine: 'Procedimiento almacenado | Procedimientos almacenados',
scheduler: 'Planificador | Planificadores',
event: 'Evento',
parameters: 'Parámetros',
function: 'Función | Funciones',
deterministic: 'Determinístico',
context: 'Contexto',
export: 'Exportar',
import: 'Importar',
returns: 'Retorno',
timing: 'Timing',
state: 'Estado',
execution: 'Ejecución',
starts: 'Inicio',
ends: 'Final',
variables: 'Variables',
processes: 'Procesos',
database: 'Base de Datos',
array: 'Tupla',
structure: 'Estructura',
row: 'Fila | Filas',
cell: 'Celda | Celdas',
triggerFunction: 'Función disparadora | Funciones disparadoras',
routine: 'Rutina | Rutinas',
drop: 'Abandonar',
commit: 'Commit',
rollback: 'Marcha atrás',
ddl: 'DDL',
collation: 'Colación',
resultsTable: 'Tabla de resultados',
unableEditFieldWithoutPrimary: 'No es posible modificar un campo sin una clave primaria en el set de resultados',
editCell: 'Modificar celda',
deleteRows: 'Eliminar fila | Eliminar {count} filas', deleteRows: 'Eliminar fila | Eliminar {count} filas',
confirmToDeleteRows: '¿Quiere realmente eliminar una fila? | ¿Quiere realmente eliminar {count} filas?', confirmToDeleteRows: '¿Quiere realmente eliminar una fila? | ¿Quiere realmente eliminar {count} filas?',
addNewRow: 'Añadir nueva fila', addNewRow: 'Añadir nueva fila',
numberOfInserts: 'Numero de inserciones', numberOfInserts: 'Número de inserciones',
affectedRows: 'Filas afectadas' affectedRows: 'Líneas afectadas',
createNewDatabase: 'Crear nueva Base de Datos',
databaseName: 'Nombre de Base de Datos',
serverDefault: 'Servidor por defecto',
deleteDatabase: 'Eliminar Base de Datos',
editDatabase: 'Modificar Base de Datos',
clearChanges: 'Deshacer cambios',
addNewField: 'Añadir nuevo campo',
manageIndexes: 'Administrar índices',
manageForeignKeys: 'Administrar claves foráneas',
manageTableChecks: 'Administrar validaciones de tabla',
allowNull: 'Permitir NULL',
zeroFill: 'Rellenar con ceros',
customValue: 'Valor predeterminado',
onUpdate: 'On UPDATE',
deleteField: 'Eliminar campo',
createNewIndex: 'Crear nuevo índice',
createNewCheck: 'Crear nueva validación',
checkClause: 'Comprobar cláusula',
addToIndex: 'Añadir al índice',
createNewTable: 'Crear nueva tabla',
emptyTable: 'Vaciar tabla',
duplicateTable: 'Duplicar tabla',
deleteTable: 'Eliminar tabla',
exportTable: 'Exportar tabla',
emptyConfirm: 'Confirmar vaciado',
thereAreNoIndexes: 'No hay índices',
thereAreNoForeign: 'No hay claves foráneas',
thereAreNoTableChecks: 'No hay validaciones de tabla',
createNewForeign: 'Crear nueva clave foránea',
referenceTable: 'Ref. tabla',
referenceField: 'Ref. campo',
foreignFields: 'Campos foráneos',
invalidDefault: 'Valor por defecto no válido',
onDelete: 'On DELETE',
selectStatement: 'Declaración SELECT',
triggerStatement: 'Declaración TRIGGER',
sqlSecurity: 'Seguridad SQL',
updateOption: 'Opción UPDATE',
deleteView: 'Eliminar vista',
createNewView: 'Crear nueva vista',
createNewMaterializedView: 'Crear nueva vista materializada',
deleteTrigger: 'Eliminar disparador',
createNewTrigger: 'Crear nuevo disparador',
currentUser: 'Usuario actual',
routineBody: 'Cuerpo de rutina',
dataAccess: 'Acceso a datos',
thereAreNoParameters: 'No hay parámetros',
createNewParameter: 'Crear nuevo parámetro',
createNewRoutine: 'Crear nuevo procedimiento almacenado',
deleteRoutine: 'Eliminar procedimiento almacenado',
functionBody: 'Cuerpo de función',
createNewFunction: 'Crear nueva función',
deleteFunction: 'Eliminar función',
schedulerBody: 'Cuerpo de planificador',
createNewScheduler: 'Crear nuevo planificador',
deleteScheduler: 'Eliminar planificador',
preserveOnCompletion: 'Mantener al finalizar',
tableFiller: 'Rellenador de tabla',
fakeDataLanguage: 'Lenguaje de datos dummy',
queryDuration: 'Duración de la consulta',
setNull: 'Establecer a NULL',
processesList: 'Lista de procesos',
processInfo: 'Información de proceso',
manageUsers: 'Administrar usuarios',
createNewSchema: 'Crear nuevo esquema',
schemaName: 'Nombre de esquema',
editSchema: 'Modificar esquema',
deleteSchema: 'Eliminar esquema',
noSchema: 'No hay esquemas',
runQuery: 'Ejecutar consulta',
thereAreNoTableFields: 'No hay campos en la tabla',
newTable: 'Nueva tabla',
newView: 'Nueva vista',
newMaterializedView: 'Nueva vista materializada',
newTrigger: 'Nuevo disparador',
newRoutine: 'Nueva rutina',
newFunction: 'Nueva función',
newScheduler: 'Nuevo planificador',
newTriggerFunction: 'Nueva función de disparador',
thereAreNoQueriesYet: 'No quedan mas consultas',
searchForQueries: 'Buscar consultas',
killProcess: 'Matar proceso',
exportSchema: 'Exportar esquema',
importSchema: 'Importar esquema',
newInsertStmtEvery: 'Nueva declaración INSERT',
processingTableExport: 'Procesando {table}',
fetchingTableExport: 'Obteniendo datos de {table}',
writingTableExport: 'Escribiendo datos de {table}',
checkAllTables: 'Verificar todas las tablas',
uncheckAllTables: 'Desmarcar todas las tablas',
killQuery: 'Matar consulta',
insertRow: 'Añadir fila | Añadir filas',
commitMode: 'Modo de Commit',
autoCommit: 'Automático',
manualCommit: 'Manual',
importQueryErrors: 'Atención: {n} de error encontrado | Atención: {n} errores encontrados',
executedQueries: '{n} consulta ejecutada | {n} consultas ejecutadas',
disableFKChecks: 'Deshabilitar comprobación de claves foráneas',
formatQuery: 'Formato de consulta',
queryHistory: 'Histórico de consultas',
clearQuery: 'Limpiar consulta',
fillCell: 'Rellenar celda',
executeSelectedQuery: 'Ejecutar consulta seleccionada',
noResultsPresent: 'No se obtuvieron resultados',
sqlExportOptions: 'Opciones de exportación de SQL',
targetTable: 'Tabla objetivo',
switchDatabase: 'Cambiar de Base de datos',
searchForElements: 'Buscar por elementos',
searchForSchemas: 'Buscar por esquemas',
savedQueries: 'Consultas almacenadas'
}, },
application: { application: {
settings: 'Configuración', settings: 'Configuración',
console: 'Consola',
general: 'General', general: 'General',
themes: 'Temas', themes: 'Temas',
update: 'Actualizar', update: 'Actualizar',
about: 'Sobre', about: 'Sobre',
language: 'Idioma', language: 'Idioma',
shortcuts: 'Atajos',
key: 'Tecla | Teclas', // Keyboard key
event: 'Evento',
light: 'Claro',
dark: 'Oscuro',
autoCompletion: 'Autocompletado',
application: 'Aplicación',
editor: 'Editor',
changelog: 'Histórico de cambios',
small: 'Pequeño',
medium: 'Mediano',
large: 'Grande',
appearance: 'Apariencia',
color: 'Color',
label: 'Etiqueta',
icon: 'Icono',
customIcon: 'Icono personalizado',
fileName: 'Nombre de fichero',
choseFile: 'Elegir fichero',
data: 'Datos',
password: 'Contraseña',
required: 'Requerido',
madeWithJS: 'Hecho con 💛 y JavaScript!', madeWithJS: 'Hecho con 💛 y JavaScript!',
checkForUpdates: 'Comprobar actualizaciones', checkForUpdates: 'Comprobar actualizaciones',
noUpdatesAvailable: 'No hay actualizaciones', noUpdatesAvailable: 'No hay actualizaciones',
@@ -71,7 +332,260 @@ export const esES = {
downloadingUpdate: 'Descargando actualización', downloadingUpdate: 'Descargando actualización',
updateDownloaded: 'Descargada actualización', updateDownloaded: 'Descargada actualización',
restartToInstall: 'Reiniciar Antares para instalar', restartToInstall: 'Reiniciar Antares para instalar',
includeBetaUpdates: 'Incluir actualizaciones en fase beta',
notificationsTimeout: 'Tiempo de espera', notificationsTimeout: 'Tiempo de espera',
openNewTab: 'Abrir nueva pestaña' openNewTab: 'Abrir nueva pestaña',
unsavedChanges: 'Cambios sin guardar',
discardUnsavedChanges: 'Tiene algunos cambios sin guardar. Al cerrar esta pestaña, serán descartados.',
applicationTheme: 'Tema de la Aplicación',
editorTheme: 'Editor de Tema',
wrapLongLines: 'Mantener lineas largas',
markdownSupported: 'Markdown soportado',
plantATree: 'Planta un arbol',
dataTabPageSize: 'Resultados por página',
noOpenTabs: 'No hay pestañas abiertas. Navega por la barra de la izquierda o:',
restorePreviousSession: 'Restablecer la sesión anterior',
closeTab: 'Cerrar pestaña',
goToDownloadPage: 'Ir a la página de descargas',
disableBlur: 'Deshabilitar opacidad',
missingOrIncompleteTranslation: '¿No hay traducción o hay algo incorrecto?',
findOutHowToContribute: 'Mira como puedes contribuir',
reportABug: 'Informar de un problema',
nextTab: 'Siguiente pestaña',
previousTab: 'Anterior pestaña',
selectTabNumber: 'Selecciona numero de pestaña {param}',
toggleConsole: 'Alternar vista de consola',
addShortcut: 'Añadir atajo',
editShortcut: 'Modificar atajo',
deleteShortcut: 'Eliminar atajo',
restoreDefaults: 'Restablecer valores por defecto',
restoreDefaultsQuestion: '¿Está seguro que quiere establecer los valores por defecto?',
registerAShortcut: 'Registrar un atajo',
invalidShortcutMessage: 'Combinación no válida, por favor intente otra',
shortcutAlreadyExists: 'El atajo ya existe',
saveContent: 'Guardar contenido',
openAllConnections: 'Abrir todas las conexiones',
openSettings: 'Abrir configuración',
runOrReload: 'Ejecutar o recargar',
openFilter: 'Abrir filtro',
nextResultsPage: 'Siguiente pagina de resultados',
previousResultsPage: 'Anterior página de resultados',
editFolder: 'Modificar carpeta',
folderName: 'Nombre de carpeta',
deleteFolder: 'Eliminar carpeta',
newFolder: 'Crear nueva carpeta',
outOfFolder: 'Fuera de la carpeta',
editConnectionAppearance: 'Modificar apariencia de conexión',
defaultCopyType: 'Copiar fichero por defecto',
showTableSize: 'Mostrar tamaño de tabla en la barra lateral',
showTableSizeDescription: 'Solo para MySQL/MariaDB. Habilitar esta opción puede afectar al rendimiento en esquemas con muchas tablas.',
switchSearchMethod: 'Cambiar método de búsqueda',
phpArray: 'Array de PHP',
closeAllTabs: 'Cerrar todas las pestañas',
closeOtherTabs: 'Cerrar las otras pestañas',
closeTabsToLeft: 'Cerrar las pestañas a la izquierda',
closeTabsToRight: 'Cerrar las pestañas a la derecha',
csvFieldDelimiter: 'Delimitador de campos',
csvLinesTerminator: 'Terminador de líneas',
csvStringDelimiter: 'Delimitador de cadenas',
csvIncludeHeader: 'Incluir cabecera',
csvExportOptions: 'Opciones de exportación de CSV',
exportData: 'Exportar datos',
exportDataExplanation: 'Exportar conexiones guardadas en Antares. Se le preguntará por una contraseña para encriptar el fichero exportado.',
importData: 'Importar datos',
importDataExplanation: 'Importará un fichero con extensión .antares que contiene conexiones. Necesitará la contraseña con la que se encriptó el mismo.',
includeConnectionPasswords: 'Incluir contraseñas de conexión',
includeFolders: 'Incluir carpetas',
encryptionPassword: 'Contraseña de encriptado',
encryptionPasswordError: 'La contraseña de encriptado debe tener al menos 8 caracteres',
ignoreDuplicates: 'Ignorar duplicados',
wrongImportPassword: 'Contraseña de importación errónea',
wrongFileFormat: 'Formato de fichero erróneo',
invalidFile: 'Fichero no válido',
dataImportSuccess: 'Datos importados correctamente',
note: 'Nota | Notas',
thereAreNoNotesYet: 'No hay notas',
addNote: 'Nueva nota',
editNote: 'Modificar nota',
saveAsNote: 'Guardar como nota',
showArchivedNotes: 'Mostrar notas archivadas',
hideArchivedNotes: 'Ocultar notas archivadas',
tag: 'Etiqueta', // Note tag,
saveFile: 'Guardar fichero',
saveFileAs: 'Guardar fichero como...',
openFile: 'Abrir fichero',
openNotes: 'Abrir notas',
debugConsole: 'Consola de Depuración', // <- console tab name
executedQueries: 'Consultas realizadas', // <- console tab name
sizeLimitError: 'Tamaño maximo de {size} excedido',
fullScreen: 'Pantalla completa',
zoomIn: 'Mas zoom',
zoomOut: 'Menos zoom',
zoomReset: 'Restablecer zoom'
},
faker: { // Faker.js methods, used in random generated content
address: 'Dirección',
commerce: 'Comercio',
company: 'Compañía',
database: 'Base de datos',
date: 'Fecha',
finance: 'Finanzas',
git: 'Git',
hacker: 'Hacker',
internet: 'Internet',
lorem: 'Lorem',
name: 'Nombre',
music: 'Música',
phone: 'Teléfono',
random: 'Aleatorio',
system: 'Sistema',
time: 'Hora',
vehicle: 'Vehículo',
zipCode: 'Código Postal',
zipCodeByState: 'Código Postal por Estado',
city: 'Ciudad',
cityPrefix: 'Prefijo de ciudad',
citySuffix: 'Sufijo de ciudad',
streetName: 'Nombre de calle',
streetAddress: 'Dirección',
streetSuffix: 'Sufijo de calle',
streetPrefix: 'Prefijo de calle',
secondaryAddress: 'Dirección secundaria',
county: 'Condado',
country: 'País',
countryCode: 'Código de país',
state: 'Estado',
stateAbbr: 'Abreviatura de Estado',
latitude: 'Latitud',
longitude: 'Longitud',
direction: 'Dirección',
cardinalDirection: 'Dirección cardinal',
ordinalDirection: 'Dirección ordinal',
nearbyGPSCoordinate: 'Coordenadas GPS',
timeZone: 'Zona horaria',
color: 'Color',
department: 'Departmento',
productName: 'Nombre de producto',
price: 'Precio',
productAdjective: 'Adjetivo de producto',
productMaterial: 'Material de producto',
product: 'Producto',
productDescription: 'Descripción de producto',
suffixes: 'Sufijos',
companyName: 'Nombre de compañía',
companySuffix: 'Sufijo de compañía',
catchPhrase: 'Catch phrase',
bs: 'BS',
catchPhraseAdjective: 'Catch phrase adjective',
catchPhraseDescriptor: 'Catch phrase descriptor',
catchPhraseNoun: 'Catch phrase noun',
bsAdjective: 'BS adjective',
bsBuzz: 'BS buzz',
bsNoun: 'BS noun',
column: 'Columna',
type: 'Tipo',
collation: 'Colación',
engine: 'Motor',
past: 'Pasado',
now: 'Ahora',
future: 'Futuro',
between: 'Entre',
recent: 'Reciente',
soon: 'Pronto',
month: 'Mes',
weekday: 'Día de la semana',
account: 'Cuenta',
accountName: 'Nombre de cuenta',
routingNumber: 'Número de enrutamiento',
mask: 'Máscara',
amount: 'Cantidad',
transactionType: 'Tipo de transacción',
currencyCode: 'Código de Moneda',
currencyName: 'Nombre de Moneda',
currencySymbol: 'Símbolo de Moneda',
bitcoinAddress: 'Dirección Bitcoin',
litecoinAddress: 'Dirección Litecoin',
creditCardNumber: 'Número de tarjeta',
creditCardCVV: 'CVV',
ethereumAddress: 'Dirección Ethereum',
iban: 'IBAN',
bic: 'BIC',
transactionDescription: 'Descripción de transacción',
branch: 'Rama',
commitEntry: 'Entrada de Commit',
commitMessage: 'Mensaje de Commit',
commitSha: 'SHA de Commit',
shortSha: 'SHA corto',
abbreviation: 'Abreviatura',
adjective: 'Adjetivo',
noun: 'Sustantivo',
verb: 'Verbo',
ingverb: 'Adverbio',
phrase: 'Frase',
avatar: 'Avatar',
email: 'Email',
exampleEmail: 'Email de ejemplo',
userName: 'Nombre de usuario',
protocol: 'Protocolo',
url: 'URL',
domainName: 'Dominio',
domainSuffix: 'Prefijo de dominio',
domainWord: 'Palabra de dominio',
ip: 'IP',
ipv6: 'IPv6',
userAgent: 'Agente de Usuario',
mac: 'MAC',
password: 'Contraseña',
word: 'Palabra',
words: 'Palabras',
sentence: 'Sentencia',
slug: 'Slug',
sentences: 'Sentencias',
paragraph: 'Frase',
paragraphs: 'Frases',
text: 'Texto',
lines: 'Lineas',
genre: 'Género',
firstName: 'Nombre',
lastName: 'Apellido',
middleName: 'Apellido',
findName: 'Nombre completo',
jobTitle: 'Ocupación',
gender: 'Género',
prefix: 'Prefijo',
suffix: 'Sufijo',
title: 'Título',
jobDescriptor: 'Descripción de trabajo',
jobArea: 'Area de trabajo',
jobType: 'Tipo de trabajo',
phoneNumber: 'Número de teléfono',
phoneNumberFormat: 'Formato de número de teléfono',
phoneFormats: 'Formatos de teléfono',
number: 'Número',
float: 'Decimal',
arrayElement: 'Elemento Array',
arrayElements: 'Elementos de Array',
objectElement: 'Elemento Objeto',
uuid: 'UUID',
boolean: 'Booleano',
image: 'Imagen',
locale: 'Conf. regional',
alpha: 'Alpha',
alphaNumeric: 'Alfanumérico',
hexaDecimal: 'Hexadecimal',
fileName: 'Nombre de fichero',
commonFileName: 'Nombre de fichero común',
mimeType: 'Mime-Type',
commonFileType: 'Tipo de fichero común',
commonFileExt: 'Extensión de fichero común',
fileType: 'Tipo de fichero',
fileExt: 'Extensión de fichero',
directoryPath: 'Ruta de directorio',
filePath: 'Ruta de fichero',
semver: 'SemVer',
manufacturer: 'Fabricante',
model: 'Modelo',
fuel: 'Combustible',
vin: 'VIN'
} }
}; };

View File

@@ -140,7 +140,7 @@ export const heIL = {
total: 'סך הכל', total: 'סך הכל',
table: 'טבלה | טבלאות', table: 'טבלה | טבלאות',
view: 'תצוגה | תצוגות', view: 'תצוגה | תצוגות',
materializedview: 'תצוגה ממומשת | תצוגות ממומשות', materializedView: 'תצוגה ממומשת | תצוגות ממומשות',
definer: 'מגדיר', definer: 'מגדיר',
algorithm: 'אלגוריתם', algorithm: 'אלגוריתם',
trigger: 'טריגר | טריגרים', trigger: 'טריגר | טריגרים',

View File

@@ -16,6 +16,7 @@ import { nlNL } from './nl-NL';
import { ptBR } from './pt-BR'; import { ptBR } from './pt-BR';
import { ruRU } from './ru-RU'; import { ruRU } from './ru-RU';
import { ukUA } from './uk-UA'; import { ukUA } from './uk-UA';
import { uzUZ } from './uz-UZ';
import { viVN } from './vi-VN'; import { viVN } from './vi-VN';
import { zhCN } from './zh-CN'; import { zhCN } from './zh-CN';
import { zhTW } from './zh-TW'; import { zhTW } from './zh-TW';
@@ -39,7 +40,8 @@ const messages = {
'cs-CZ': csCZ, 'cs-CZ': csCZ,
'uk-UA': ukUA, 'uk-UA': ukUA,
'zh-TW': zhTW, 'zh-TW': zhTW,
'he-IL': heIL 'he-IL': heIL,
'uz-UZ': uzUZ
}; };
type NestedPartial<T> = { type NestedPartial<T> = {

View File

@@ -275,7 +275,7 @@ export const nlNL = {
savedQueries: 'Opgeslagen queries', savedQueries: 'Opgeslagen queries',
searchForElements: 'Zoek naar elementen', searchForElements: 'Zoek naar elementen',
searchForSchemas: 'Zoek naar schema\'s', searchForSchemas: 'Zoek naar schema\'s',
materializedview: 'Materialized view | Materialized views', materializedView: 'Materialized view | Materialized views',
createNewMaterializedView: 'Materialized view maken', createNewMaterializedView: 'Materialized view maken',
newMaterializedView: 'Nieuwe materialized view' newMaterializedView: 'Nieuwe materialized view'
}, },

View File

@@ -270,7 +270,7 @@ export const ruRU = {
importQueryErrors: 'Внимание: {n} ошибка возникла | Внимание: {n} ошибок произошло', importQueryErrors: 'Внимание: {n} ошибка возникла | Внимание: {n} ошибок произошло',
executedQueries: '{n} запрос выполнен | {n} запросов выполнено', executedQueries: '{n} запрос выполнен | {n} запросов выполнено',
insert: 'Вставить', insert: 'Вставить',
materializedview: 'Материализованное представление | Материализованные представления', materializedView: 'Материализованное представление | Материализованные представления',
exportTable: 'Экспорт таблицы', exportTable: 'Экспорт таблицы',
createNewMaterializedView: 'Создать новое материализованное представление', createNewMaterializedView: 'Создать новое материализованное представление',
newMaterializedView: 'Новое материализованное представление', newMaterializedView: 'Новое материализованное представление',

View File

@@ -17,5 +17,6 @@ export const localesNames: Record<string, string> = {
'ca-ES': 'Català', 'ca-ES': 'Català',
'cs-CZ': 'Čeština', 'cs-CZ': 'Čeština',
'uk-UA': 'Українська', 'uk-UA': 'Українська',
'uz-UZ': 'O`zbek',
'he-IL': 'עברית' 'he-IL': 'עברית'
}; };

576
src/renderer/i18n/uz-UZ.ts Normal file
View File

@@ -0,0 +1,576 @@
export const uzUZ = {
general: {
edit: 'Tahrirlash',
save: 'Saqlash',
close: 'Yopish',
delete: 'Oʻchirish',
confirm: 'Tasdiqlash',
cancel: 'Bekor qilish',
send: 'Yuborish',
refresh: 'Yangilash',
autoRefresh: 'Avto yangilash',
version: 'Versiya',
donate: 'Donat',
run: 'Bajarish',
results: 'Korsatildi',
size: 'Hajmi',
mimeType: 'Mime-Turi',
download: 'Yuklab olish',
add: 'Qoʻshish',
data: 'Maʼlumotlar',
properties: 'Xususiyatlar',
insert: 'Kiritish',
name: 'Nomi',
clear: 'Tozalash',
seconds: 'Soniyalar',
options: 'Parametrlar',
discard: 'Bekor qilish',
stay: 'Qolish',
author: 'Muallif',
upload: 'Yuklash',
browse: 'Koʻrish',
content: 'Mazmun',
cut: 'Kesish',
copy: 'Nusxalash',
paste: 'Qoʻyish',
tools: 'Asboblar',
format: 'Formatlash',
all: 'Hammasi',
duplicate: 'Nusxa kochirish',
new: 'Yangi',
history: 'Tarix',
select: 'Tanlash',
filter: 'Filtr',
change: 'Oʻzgartirish',
includes: 'Oʻz ichiga oladi',
completed: 'Tugallandi',
aborted: 'Bekor qilindi',
disabled: 'Oʻchirib qoʻyilgan',
enable: 'Yoqish',
disable: 'Oʻchirish',
contributors: 'Hissa qoʻshuvchilar',
pin: 'Biriktirish',
unpin: 'Ajratish',
folder: 'Papka | Papkalar',
deleteConfirm: 'Oʻchirib tashlashni tasdiqlaysizmi',
uploadFile: 'Fayl yuklash',
manualValue: 'Qiymatni qoʻlda kiritish',
selectAll: 'Hammasini tanlash',
pageNumber: 'Sahifa raqami',
directoryPath: 'Papka yoʻli',
actionSuccessful: '{action} muvaffaqiyatli bajarildi',
outputFormat: 'Chiqarish formati',
singleFile: 'Bitta {ext} fayl',
zipCompressedFile: 'ZIP siqilgan {ext} fayl',
include: 'Qoʻshish',
none: 'Yoʻq',
singleQuote: 'Yagona qoʻshtirnoq',
doubleQuote: 'Ikkilik qoʻshtirnoq',
copyName: 'Nomi nusxalash',
search: 'Qidirish',
title: 'Sarlavha',
archive: 'Arxiv',
undo: 'Bekor qilish',
moveTo: 'Koʻchirish'
},
connection: {
connectionName: 'Ulanish nomi',
client: 'Mijoz',
hostName: 'Xost nomi',
port: 'Port',
user: 'Foydalanuvchi',
password: 'Parol',
credentials: 'Vakolatlar',
connect: 'Ulanish',
connected: 'Ulangan',
disconnect: 'Uzish',
disconnected: 'Uzilgan',
ssl: 'SSL',
privateKey: 'Yopiq kalit',
certificate: 'Sertifikat',
caCertificate: 'CA sertifikat',
ciphers: 'Shifrlar',
sshTunnel: 'SSH tunnel',
passphrase: 'Parol frazasi',
connectionString: 'Ulanish qatori',
addConnection: 'Ulanish qoʻshish',
createConnection: 'Ulanish yaratish',
createNewConnection: 'Yangi ulanish yaratish',
askCredentials: 'Hisob maʼlumotlarini soʻrash',
testConnection: 'Ulanishni sinash',
editConnection: 'Ulanishni tahrirlash',
deleteConnection: 'Ulanishni oʻchirish',
connectionSuccessfullyMade: 'Ulanish muvaffaqiyatli amalga oshirildi!',
enableSsl: 'SSL ni yoqish',
enableSsh: 'SSH ni yoqish',
readOnlyMode: 'Faqat oʻqish rejimi',
untrustedConnection: 'Ishonchsiz ulanish',
allConnections: 'Barcha ulanishlar',
searchForConnections: 'Ulanishlarni qidirish',
keepAliveInterval: 'Ulanishni saqlash oraliq vaqti',
singleConnection: 'Bitta ulanish',
connection: 'Ulanish'
},
database: {
schema: 'Shema',
type: 'Tur',
foreignKeys: 'Tashqi kalitlar',
length: 'Uzunlik',
unsigned: 'Imzosiz',
default: 'Standart',
comment: 'Izoh',
collation: 'Moslash',
key: 'Kalit | Kalitlar',
order: 'Tartib',
expression: 'Ifoda',
autoIncrement: 'Avtomatik oshirish',
engine: 'Dvigatel',
field: 'Maydon | Maydonlar',
approximately: 'Taxminan',
total: 'Jami',
table: 'Jadval',
view: 'Korinish',
indexes: 'Indekslar',
definer: 'Belgilar',
algorithm: 'Algoritm',
trigger: 'Trigge | Triggeler',
storedRoutine: 'Saqlangan protsedura | Saqlangan protseduralar',
scheduler: 'Rejalashtiruvchi | Rejalashtiruvchilar',
event: 'Voqea',
parameters: 'Parametrlar',
function: 'Funktsiya | Funktsiyalar',
deterministic: 'Deterministik',
context: 'Kontekst',
export: 'Eksport',
import: 'Import',
returns: 'Qaytaradi',
timing: 'Vaqt',
state: 'Holat',
execution: 'Bajarish',
starts: 'Boshlanishi',
ends: 'Tugashi',
variables: 'Ozgaruvchilar',
processes: 'Jarayonlar',
database: 'Maʼlumotlar bazasi',
array: 'Massiv',
structure: 'Tuzilish',
row: 'Qator | Qatorlar',
cell: 'Yacheyka | Yacheykalar',
triggerFunction: 'Trigger funktsiyasi | Trigger funktsiyalari',
routine: 'Protsedura',
commit: 'Tasdiqlash',
rollback: 'Bekor qilish',
resultsTable: 'Natija jadvali',
ddl: 'DDL',
drop: 'Oʻchirish',
unableEditFieldWithoutPrimary: 'Birlamchi kalit bolmasa, maydonni tahrirlash mumkin emas',
editCell: 'Yacheykani tahrirlash',
deleteRows: 'Qatorni oʻchirish | {count} qatorni oʻchirish',
confirmToDeleteRows: 'Qatorni oʻchirishni tasdiqlaysizmi? | {count} qatorni oʻchirishni tasdiqlaysizmi?',
addNewRow: 'Yangi qator qoʻshish',
numberOfInserts: 'Qoʻshilganlar soni',
affectedRows: 'Taqsir qilingan qatorlar',
createNewDatabase: 'Yangi maʼlumotlar bazasi yaratish',
databaseName: 'Maʼlumotlar bazasi nomi',
serverDefault: 'Server standartlari boyicha',
deleteDatabase: 'Maʼlumotlar bazasini oʻchirish',
editDatabase: 'Maʼlumotlar bazasini tahrirlash',
clearChanges: 'Oʻzgarishlarni oʻchirish',
addNewField: 'Yangi maydon qoshish',
manageIndexes: 'Indekslarni boshqarish',
manageForeignKeys: 'Tashqi kalitlarni boshqarish',
allowNull: 'NULL ga ruxsat berish',
zeroFill: 'Nol bilan toʻldirish',
customValue: 'Foydalanuvchi qiymati',
onUpdate: 'Yangilanishda',
deleteField: 'Maydonni oʻchirish',
createNewIndex: 'Yangi indeks yaratish',
addToIndex: 'Indeksga qoʻshish',
createNewTable: 'Yangi jadval yaratish',
emptyTable: 'Jadvalni tozalash',
deleteTable: 'Jadvalni oʻchirish',
emptyConfirm: 'Tozalashni tasdiqlaysizmi?',
thereAreNoIndexes: 'Indekslar mavjud emas',
thereAreNoForeign: 'Tashqi kalitlar mavjud emas',
createNewForeign: 'Yangi tashqi kalit yaratish',
referenceTable: 'Jadvalga murojaat',
referenceField: 'Maydonga murojaat',
foreignFields: 'Tashqi maydonlar',
invalidDefault: 'Notogri qiymat',
onDelete: 'Oʻchirishda',
selectStatement: 'Tanlash operatori',
triggerStatement: 'Trigger operatori',
sqlSecurity: 'SQL xavfsizligi',
updateOption: 'Yangilash parametrlari',
deleteView: 'Korinishni oʻchirish',
createNewView: 'Yangi korinish yaratish',
deleteTrigger: 'Trigerni oʻchirish',
createNewTrigger: 'Yangi trigger yaratish',
currentUser: 'Hozirgi foydalanuvchi',
routineBody: 'Protsedura matni',
dataAccess: 'Maʼlumotlarga kirish',
thereAreNoParameters: 'Parametrlar yoʻq',
createNewParameter: 'Yangi parametr yaratish',
createNewRoutine: 'Yangi protsedura yaratish',
deleteRoutine: 'Protsedurani oʻchirish',
functionBody: 'Funktsiya matni',
createNewFunction: 'Yangi funktsiya yaratish',
deleteFunction: 'Funktsiyani oʻchirish',
schedulerBody: 'Rejalashtiruvchi matni',
createNewScheduler: 'Yangi rejalashtiruvchi yaratish',
deleteScheduler: 'Rejalashtiruvchini oʻchirish',
preserveOnCompletion: 'Yakunlangandan soʻng saqlash',
tableFiller: 'Jadval toʻldiruvchisi',
fakeDataLanguage: 'Soxta maʼlumotlar tili',
queryDuration: 'Soʻrov davomiyligi',
setNull: 'NULL qiymatni oʻrnatish',
processesList: 'Jarayonlar roʻyxati',
processInfo: 'Jarayon maʼlumotlari',
manageUsers: 'Foydalanuvchilarni boshqarish',
createNewSchema: 'Yangi shema yaratish',
schemaName: 'Shema nomi',
editSchema: 'Shemaga ozgartirish kiritish',
deleteSchema: 'Shemadan oʻchirish',
duplicateTable: 'Jadvalni dublikat qilish',
noSchema: 'Shemalar mavjud emas',
runQuery: 'Soʻrovni bajarish',
thereAreNoTableFields: 'Jadvalda maydonlar yoʻq',
newTable: 'Yangi jadval',
newView: 'Yangi korinish',
newTrigger: 'Yangi trigger',
newRoutine: 'Yangi protsedura',
newFunction: 'Yangi funktsiya',
newScheduler: 'Yangi rejalashtiruvchi',
newTriggerFunction: 'Yangi trigger funktsiyasi',
thereAreNoQueriesYet: 'Soʻrovlar hali mavjud emas',
searchForQueries: 'Soʻrovlarni qidirish',
killProcess: 'Jarayonni toʻxtatish',
exportSchema: 'Shemadan eksport qilish',
importSchema: 'Shemaga import qilish',
newInsertStmtEvery: 'Har bir uchun yangi INSERT operator',
processingTableExport: '{table} ni qayta ishlash',
fetchingTableExport: '{table} maʼlumotlarini olish',
writingTableExport: '{table} ga yozish',
checkAllTables: 'Barcha jadvallarni belgilash',
uncheckAllTables: 'Barcha jadvallardan belgilashni olib tashlash',
killQuery: 'Soʻrovni toʻxtatish',
insertRow: 'Qator kiritish | Qatorlarni kiritish',
commitMode: 'Tranzaksiya tasdiqlash rejimi',
autoCommit: 'Avtomatik tasdiqlash',
manualCommit: 'Qoʻlda tasdiqlash',
disableFKChecks: 'Tashqi kalitlarni tekshirishni oʻchirish',
formatQuery: 'Soʻrovni formatlash',
queryHistory: 'Soʻrovlar tarixi',
clearQuery: 'Soʻrovni tozalash',
fillCell: 'Yacheykani toʻldirish',
executeSelectedQuery: 'Tanlangan soʻrovni bajarish',
noResultsPresent: 'Natijalar yoʻq',
sqlExportOptions: 'SQL eksport parametrlari',
targetTable: 'Maqsad jadvali',
importQueryErrors: 'Diqqat: {n} xato yuz berdi | Diqqat: {n} xatolar yuz berdi',
executedQueries: '{n} soʻrov bajarildi | {n} soʻrovlar bajarildi',
insert: 'Kiritish',
materializedView: 'Materializatsiya qilingan korinish | Materializatsiya qilingan korinishlar',
exportTable: 'Jadvalni eksport qilish',
createNewMaterializedView: 'Yangi materializatsiya qilingan korinish yaratish',
newMaterializedView: 'Yangi materializatsiya qilingan korinish',
switchDatabase: 'Maʼlumotlar bazasini almashtirish',
searchForElements: 'Elementlarni qidirish',
searchForSchemas: 'Shemalarni qidirish',
savedQueries: 'Saqlangan soʻrovlar'
},
application: {
settings: 'Sozlamalar',
general: 'Umumiy',
themes: 'Mavzular',
update: 'Yangilash',
about: 'Dastur haqida',
language: 'Til',
light: 'Yorugʻ',
dark: 'Qorongʻi',
autoCompletion: 'Avto-tugallash',
application: 'Ilova',
editor: 'Tahrirchi',
scratchpad: 'Eslatmalar',
changelog: 'Oʻzgarishlar jurnali',
small: 'Kichik',
medium: 'Oʻrta',
large: 'Katta',
console: 'Konsol',
shortcuts: 'Qisqa tugmalar',
appearance: 'Tashqi koʻrinish',
color: 'Rang',
label: 'Yorliq',
icon: 'Belgi',
madeWithJS: '💛 va JavaScript bilan yaratilgan!',
checkForUpdates: 'Yangilanishlarni tekshirish',
noUpdatesAvailable: 'Yangilanishlar topilmadi',
checkingForUpdate: 'Yangilanishlarni qidirish',
checkFailure: 'Yangilanishlarni tekshirib boʻlmadi, iltimos keyinroq urinib koʻring',
updateAvailable: 'Yangilanish mavjud',
downloadingUpdate: 'Yangilanishni yuklab olish',
updateDownloaded: 'Yangilanish yuklab olindi',
restartToInstall: 'Oʻrnatish uchun Antares-ni qayta ishga tushiring',
notificationsTimeout: 'Bildirishnoma vaqti',
openNewTab: 'Yangi tab ochish',
unsavedChanges: 'Saqlanmagan oʻzgarishlar',
discardUnsavedChanges: 'Saqlanmagan maʼlumotlaringiz mavjud. Ushbu oynani yopish ularni bekor qiladi.',
applicationTheme: 'Ilova mavzusi',
editorTheme: 'Tahrirchi mavzusi',
wrapLongLines: 'Uzun satrlarni oʻrash',
includeBetaUpdates: 'Beta yangilanishlarni olish',
markdownSupported: 'Markdown qoʻllab-quvvatlanadi',
plantATree: 'Daraxt ekish',
dataTabPageSize: 'MAʼLUMOTLAR yorligʻi sahifa oʻlchami',
noOpenTabs: 'Ochiq yorliqlar yoʻq, chap paneldan foydalaning yoki:',
restorePreviousSession: 'Avvalgi sessiyani tiklash',
closeTab: 'Yorliqni yopish',
goToDownloadPage: 'Yuklash sahifasiga oʻtish',
disableBlur: 'Bulutni oʻchirish',
missingOrIncompleteTranslation: 'Yetishmayotgan yoki toʻliq boʻlmagan tarjima?',
findOutHowToContribute: 'Qanday hissa qoʻshishni bilib oling',
disableScratchpad: 'Eslatmalarni oʻchirish',
reportABug: 'Xato haqida xabar berish',
nextTab: 'Keyingi yorliq',
previousTab: 'Oldingi yorliq',
selectTabNumber: '{param} raqamli yorliqni tanlash',
toggleConsole: 'Konsolni almashtirish',
addShortcut: 'Qisqa tugma qoʻshish',
editShortcut: 'Qisqa tugmani tahrirlash',
deleteShortcut: 'Qisqa tugmani oʻchirish',
restoreDefaults: 'Standartlarga qaytarish',
restoreDefaultsQuestion: 'Standart qiymatlarni tiklashga rozimisiz?',
registerAShortcut: 'Qisqa tugma roʻyxatdan oʻtkazish',
invalidShortcutMessage: 'Bu kombinatsiyani ishlatib boʻlmaydi, boshqa birini sinab koʻring',
shortcutAlreadyExists: 'Bunday kombinatsiya allaqachon mavjud',
saveContent: 'Mazmunni saqlash',
openAllConnections: 'Barcha ulanishlarni ochish',
openSettings: 'Sozlamalarni ochish',
openScratchpad: 'Eslatmalarni ochish',
runOrReload: 'Bajaring yoki qayta yuklang',
openFilter: 'Filtrni ochish',
nextResultsPage: 'Keyingi sahifa',
previousResultsPage: 'Oldingi sahifa',
editFolder: 'Papkani tahrirlash',
folderName: 'Papkalar nomi',
deleteFolder: 'Papkani oʻchirish',
editConnectionAppearance: 'Ulanish tashqi koʻrinishini oʻzgartirish',
defaultCopyType: 'Odatiy nusxa koʻchirish turi',
showTableSize: 'Yon panelda jadval oʻlchamini koʻrsatish',
showTableSizeDescription: 'Faqat MySQL/MariaDB. Ushbu parametrni yoqish koʻp jadvalli sxemalarda ishlash tezligiga taʼsir qilishi mumkin.',
searchForSchemas: 'Sxemalarni qidirish',
searchForElements: 'Elementlarni qidirish',
switchSearchMethod: 'Qidiruv usulini almashtirish',
closeAllTabs: 'Barcha yorliqlarni yopish',
closeOtherTabs: 'Boshqa yorliqlarni yopish',
closeTabsToLeft: 'Chapdagi yorliqlarni yopish',
closeTabsToRight: 'Oʻngdagi yorliqlarni yopish',
phpArray: 'PHP massiv',
event: 'Hodisa',
customIcon: 'Maxsus belgi',
fileName: 'Fayl nomi',
choseFile: 'Faylni tanlash',
data: 'Maʼlumotlar',
password: 'Parol',
required: 'Majburiy',
newFolder: 'Yangi papka',
outOfFolder: 'Papkadan tashqari',
csvFieldDelimiter: 'CSV maydon ajratgichi',
csvLinesTerminator: 'CSV satr terminatori',
csvStringDelimiter: 'CSV satr ajratgichi',
csvIncludeHeader: 'Sarlavhani kiritish',
csvExportOptions: 'CSV eksport imkoniyatlari',
exportData: 'Maʼlumotlarni eksport qilish',
exportDataExplanation: 'Antares-dagi saqlangan ulanishlarni eksport qilish. Shifrlangan fayl uchun parol kiritishingiz soʻraladi.',
importData: 'Maʼlumotlarni import qilish',
importDataExplanation: '.antares faylini import qiladi, unda ulanishlar mavjud. Eksport paytida kiritilgan parolni kiritishingiz kerak.',
includeConnectionPasswords: 'Ulanish parollarini kiritish',
includeFolders: 'Papkalarni kiritish',
encryptionPassword: 'Shifrlash paroli',
encryptionPasswordError: 'Shifrlash paroli kamida 8 ta belgi boʻlishi kerak.',
ignoreDuplicates: 'Nusxalarni inkor qilish',
wrongImportPassword: 'Import paroli notoʻgʻri',
wrongFileFormat: 'Fayl formati notoʻgʻri',
dataImportSuccess: 'Maʼlumotlar muvaffaqiyatli import qilindi',
note: 'Eslatma | Eslatmalar',
thereAreNoNotesYet: 'Hali eslatmalar yoʻq',
addNote: 'Eslatma qoʻshish',
editNote: 'Eslatmani tahrirlash',
saveAsNote: 'Eslatma sifatida saqlash',
showArchivedNotes: 'Arxivlangan eslatmalarni koʻrsatish',
hideArchivedNotes: 'Arxivlangan eslatmalarni yashirish',
tag: 'Teg',
saveFile: 'Faylni saqlash',
saveFileAs: 'Faylni sifatida saqlash',
openFile: 'Faylni ochish',
openNotes: 'Eslatmalarni ochish',
debugConsole: 'Nosozliklarni tuzatish konsoli',
executedQueries: 'Bajarilgan soʻrovlar',
sizeLimitError: 'Maksimal oʻlcham {size} dan oshib ketdi'
},
faker: {
address: 'Manzil',
commerce: 'Tijorat',
company: 'Kompaniya',
database: 'Ma`lumotlar bazasi',
date: 'Sana',
finance: 'Moliyaviy',
git: 'Git',
hacker: 'Xaker',
internet: 'Internet',
lorem: 'Lorem',
name: 'Ism',
music: 'Musiqa',
phone: 'Telefon',
random: 'Tasodifiy',
system: 'Tizim',
time: 'Vaqt',
vehicle: 'Transport vositasi',
zipCode: 'Pochta kodi',
zipCodeByState: 'Shahar pochta kodi',
city: 'Shahar',
cityPrefix: 'Shahar prefiksi',
citySuffix: 'Shahar sufixi',
streetName: 'Ko`cha nomi',
streetAddress: 'Ko`cha manzili',
streetSuffix: 'Ko`cha sufixi',
streetPrefix: 'Ko`cha prefiksi',
secondaryAddress: 'Ikkinchi manzil',
county: 'Okrug',
country: 'Davlat',
countryCode: 'Davlat kodi',
state: 'Viloyat',
stateAbbr: 'Viloyat qisqartmasi',
latitude: 'Kenglik',
longitude: 'Uzunlik',
direction: 'Yo`nalish',
cardinalDirection: 'Kardinal yo`nalish',
ordinalDirection: 'Tartibiy yo`nalish',
nearbyGPSCoordinate: 'Yaqin GPS koordinatasi',
timeZone: 'Vaqt zonasі',
color: 'Rang',
department: 'Bo`lim',
productName: 'Mahsulot nomi',
price: 'Narx',
productAdjective: 'Mahsulotga oid sifat',
productMaterial: 'Mahsulot materiali',
product: 'Mahsulot',
productDescription: 'Mahsulot ta`rifi',
suffixes: 'Sufikslari',
companyName: 'Kompaniya nomi',
companySuffix: 'Kompaniya sufixi',
catchPhrase: 'Slogan',
bs: 'BS',
catchPhraseAdjective: 'Slogan sifat',
catchPhraseDescriptor: 'Slogan tavsifi',
catchPhraseNoun: 'Slogan ot',
bsAdjective: 'BS sifat',
bsBuzz: 'BS shovqin',
bsNoun: 'BS ot',
column: 'Ustun',
type: 'Tur',
collation: 'Taqqoslash',
engine: 'Dvigatel',
past: 'O`tgan',
now: 'Hozir',
future: 'Kelajak',
between: 'Orasida',
recent: 'Yaqinda',
soon: 'Tez orada',
month: 'Oy',
weekday: 'Hafta kuni',
account: 'Hisob',
accountName: 'Hisob nomi',
routingNumber: 'Yo`nalish raqami',
mask: 'Maska',
amount: 'Miqdor',
transactionType: 'Transaksiya turi',
currencyCode: 'Valyuta kodi',
currencyName: 'Valyuta nomi',
currencySymbol: 'Valyuta belgilari',
bitcoinAddress: 'Bitcoin hamyoni',
litecoinAddress: 'Litecoin hamyoni',
creditCardNumber: 'Kredit karta raqami',
creditCardCVV: 'CVV kodi',
ethereumAddress: 'Ethereum hamyoni',
iban: 'Iban',
bic: 'Bic',
transactionDescription: 'Transaksiya ta`rifi',
branch: 'Filial',
commitEntry: 'Kommit',
commitMessage: 'Kommit xabari',
commitSha: 'SHA kommit',
shortSha: 'Qisqa SHA',
abbreviation: 'Qisqartma',
adjective: 'Sifat',
noun: 'Ot',
verb: 'Fe`l',
ingverb: 'Ildiz fe`l',
phrase: 'Ibora',
avatar: 'Avatar',
email: 'Elektron pochta',
exampleEmail: 'Elektron pochta misoli',
userName: 'Login',
protocol: 'Protokol',
url: 'Url',
domainName: 'Domen nomi',
domainSuffix: 'Domen sufixi',
domainWord: 'Domen so`zi',
ip: 'Ip',
ipv6: 'Ipv6',
userAgent: 'User agent',
mac: 'MAC-manzil',
password: 'Parol',
word: 'So`z',
words: 'So`zlar',
sentence: 'Gap',
slug: 'Slug',
sentences: 'Gaplar',
paragraph: 'Paragraf',
paragraphs: 'Paragraflar',
text: 'Matn',
lines: 'Chiziqlar',
genre: 'Janr',
firstName: 'Ism',
lastName: 'Familiya',
middleName: 'Otasining ismi',
findName: 'To`liq ism',
jobTitle: 'Lavozim',
gender: 'Jins',
prefix: 'Prefiks',
suffix: 'Sufiks',
title: 'Sarlavha',
jobDescriptor: 'Lavozim ta`rifi',
jobArea: 'Lavozim sohasi',
jobType: 'Lavozim turi',
phoneNumber: 'Telefon raqami',
phoneNumberFormat: 'Telefon raqami formati',
phoneFormats: 'Telefon raqami formatlari',
number: 'Raqam',
float: 'O`nlik son',
arrayElement: 'Massiv elementi',
arrayElements: 'Massiv elementlari',
objectElement: 'Obyekt elementi',
uuid: 'Uuid',
boolean: 'Mantiqiy',
image: 'Rasm',
locale: 'Mahalliy',
alpha: 'Harflar',
alphaNumeric: 'Alfavitli-sonli',
hexaDecimal: 'O`n oltilik',
fileName: 'Fayl nomi',
commonFileName: 'Ommaviy fayl nomi',
mimeType: 'Mime-turi',
commonFileType: 'Ommaviy fayl turi',
commonFileExt: 'Ommaviy fayl kengaytmasi',
fileType: 'Fayl turi',
fileExt: 'Fayl kengaytmasi',
directoryPath: 'Katalog yo`li',
filePath: 'Fayl yo`li',
semver: 'Semver',
manufacturer: 'Ishlab chiqaruvchi',
model: 'Model',
fuel: 'Yoqilg`i',
vin: 'Vin'
}
};

View File

@@ -117,6 +117,7 @@ export const zhCN = {
insert: '插入', insert: '插入',
indexes: '索引', indexes: '索引',
foreignKeys: '外键', foreignKeys: '外键',
tableChecks: '表检查',
length: '长度', length: '长度',
unsigned: '无符号', unsigned: '无符号',
default: '默认', default: '默认',
@@ -131,6 +132,7 @@ export const zhCN = {
total: '总计', total: '总计',
table: '表 | 表', table: '表 | 表',
view: '视图 | 视图', view: '视图 | 视图',
materializedView: '实体化视图 | 实体化视图',
definer: '定义者', definer: '定义者',
algorithm: '算法', algorithm: '算法',
trigger: '触发器 | 触发器', trigger: '触发器 | 触发器',
@@ -180,12 +182,15 @@ export const zhCN = {
addNewField: '添加新字段', addNewField: '添加新字段',
manageIndexes: '管理索引', manageIndexes: '管理索引',
manageForeignKeys: '管理外键', manageForeignKeys: '管理外键',
manageTableChecks: '管理表 check 约束',
allowNull: '允许 NULL', allowNull: '允许 NULL',
zeroFill: '零填充', zeroFill: '零填充',
customValue: '自定义值', customValue: '自定义值',
onUpdate: '在更新', onUpdate: '在更新',
deleteField: '删除字段', deleteField: '删除字段',
createNewIndex: '创建新索引', createNewIndex: '创建新索引',
createNewCheck: '创建新 check 约束',
checkClause: 'Check 约束',
addToIndex: '添加到索引', addToIndex: '添加到索引',
createNewTable: '创建新表', createNewTable: '创建新表',
emptyTable: '清空表', emptyTable: '清空表',
@@ -195,6 +200,7 @@ export const zhCN = {
emptyConfirm: '您是否确认清空', emptyConfirm: '您是否确认清空',
thereAreNoIndexes: '没有索引', thereAreNoIndexes: '没有索引',
thereAreNoForeign: '没有外键', thereAreNoForeign: '没有外键',
thereAreNoTableChecks: '没有表 check 约束',
createNewForeign: '创建新外键', createNewForeign: '创建新外键',
referenceTable: '参考表', referenceTable: '参考表',
referenceField: '参考字段', referenceField: '参考字段',
@@ -207,6 +213,7 @@ export const zhCN = {
updateOption: '更新选项', updateOption: '更新选项',
deleteView: '删除视图', deleteView: '删除视图',
createNewView: '创建新视图', createNewView: '创建新视图',
createNewMaterializedView: '创建新实体化视图',
deleteTrigger: '删除触发器', deleteTrigger: '删除触发器',
createNewTrigger: '创建新触发器', createNewTrigger: '创建新触发器',
currentUser: '当前用户', currentUser: '当前用户',
@@ -239,6 +246,7 @@ export const zhCN = {
thereAreNoTableFields: '没有表的字段', thereAreNoTableFields: '没有表的字段',
newTable: '新表', newTable: '新表',
newView: '新视图', newView: '新视图',
newMaterializedView: '新实体化视图',
newTrigger: '新触发器', newTrigger: '新触发器',
newRoutine: '新例程', newRoutine: '新例程',
newFunction: '新函数', newFunction: '新函数',
@@ -300,6 +308,7 @@ export const zhCN = {
color: '颜色', color: '颜色',
label: '标签', label: '标签',
icon: '图标', icon: '图标',
customIcon: '定制图标',
fileName: '文件名称', fileName: '文件名称',
choseFile: '选择文件', choseFile: '选择文件',
data: '数据', data: '数据',
@@ -324,7 +333,7 @@ export const zhCN = {
wrapLongLines: '将长行换行显示', wrapLongLines: '将长行换行显示',
markdownSupported: '支持 Markdown', markdownSupported: '支持 Markdown',
plantATree: '种植一棵树', plantATree: '种植一棵树',
dataTabPageSize: '数据标签的页面大小', dataTabPageSize: '每页结果',
noOpenTabs: '没有打开的标签, 请在左侧栏上导航或:', noOpenTabs: '没有打开的标签, 请在左侧栏上导航或:',
restorePreviousSession: '恢复上一个会话', restorePreviousSession: '恢复上一个会话',
closeTab: '关闭标签', closeTab: '关闭标签',
@@ -348,7 +357,6 @@ export const zhCN = {
saveContent: '保存内容', saveContent: '保存内容',
openAllConnections: '打开所有连接', openAllConnections: '打开所有连接',
openSettings: '打开设置', openSettings: '打开设置',
openScratchpad: '打开草稿栏',
runOrReload: '运行或重新加载', runOrReload: '运行或重新加载',
openFilter: '打开过滤器', openFilter: '打开过滤器',
nextResultsPage: '下一个结果页', nextResultsPage: '下一个结果页',
@@ -356,6 +364,8 @@ export const zhCN = {
editFolder: '编辑文件夹', editFolder: '编辑文件夹',
folderName: '文件夹名称', folderName: '文件夹名称',
deleteFolder: '删除文件夹', deleteFolder: '删除文件夹',
newFolder: '新文件夹',
outOfFolder: '',
editConnectionAppearance: '编辑连接的外观', editConnectionAppearance: '编辑连接的外观',
defaultCopyType: '默认复制类型', defaultCopyType: '默认复制类型',
showTableSize: '在侧边栏显示表大小', showTableSize: '在侧边栏显示表大小',
@@ -394,7 +404,10 @@ export const zhCN = {
saveFile: '保存文件', saveFile: '保存文件',
saveFileAs: '将文件另存为', saveFileAs: '将文件另存为',
openFile: '打开文件', openFile: '打开文件',
openNotes: '打开笔记' openNotes: '打开笔记',
debugConsole: '调试控制台',
executedQueries: '执行的查询',
sizeLimitError: '超过 {size} 的最大大小'
}, },
faker: { // Faker.js 方法,用于随机生成的内容 faker: { // Faker.js 方法,用于随机生成的内容
address: '地址', address: '地址',

View File

@@ -1,18 +1,15 @@
import { ConnectionParams, IpcResponse } from 'common/interfaces/antares'; import { ConnectionParams, IpcResponse } from 'common/interfaces/antares';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import connStringConstruct from '../libs/connStringDecode';
import { unproxify } from '../libs/unproxify'; import { unproxify } from '../libs/unproxify';
export default class { export default class {
static makeTest (params: ConnectionParams & { pgConnString?: string }): Promise<IpcResponse> { static makeTest (params: ConnectionParams & { connString?: string }): Promise<IpcResponse> {
const newParams = connStringConstruct(params) as ConnectionParams; return ipcRenderer.invoke('test-connection', unproxify(params));
return ipcRenderer.invoke('test-connection', unproxify(newParams));
} }
static connect (params: ConnectionParams & { pgConnString?: string }): Promise<IpcResponse> { static connect (params: ConnectionParams & { connString?: string }): Promise<IpcResponse> {
const newParams = connStringConstruct(params) as ConnectionParams; return ipcRenderer.invoke('connect', unproxify(params));
return ipcRenderer.invoke('connect', unproxify(newParams));
} }
static abortConnection (uid: string): void { static abortConnection (uid: string): void {

View File

@@ -1,50 +0,0 @@
import { ConnectionParams } from 'common/interfaces/antares';
import * as formatter from 'pg-connection-string'; // parses a connection string
const formatHost = (host: string) => {
const results = host === 'localhost' ? '127.0.0.1' : host;
return results;
};
const checkForSSl = (conn: string) => {
return conn.includes('ssl=true');
};
const connStringConstruct = (args: ConnectionParams & { pgConnString?: string }): ConnectionParams => {
if (!args.pgConnString)
return args;
if (typeof args.pgConnString !== 'string')
return args;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const stringArgs: any = formatter.parse(args.pgConnString);
const client = args.client || 'pg';
args.client = client;
args.host = formatHost(stringArgs.host);
args.database = stringArgs.database;
args.port = stringArgs.port || '5432';
args.user = stringArgs.user;
args.password = stringArgs.password;
// ssh
args.ssh = stringArgs.ssh || args.ssh;
args.sshHost = stringArgs.sshHost;
args.sshUser = stringArgs.sshUser;
args.sshPass = stringArgs.sshPass;
args.sshKey = stringArgs.sshKey;
args.sshPort = stringArgs.sshPort;
// ssl mode
args.ssl = checkForSSl(args.pgConnString) || args.ssl;
args.cert = stringArgs.sslcert;
args.key = stringArgs.sslkey;
args.ca = stringArgs.sslrootcert;
args.ciphers = stringArgs.ciphers;
return args;
};
export default connStringConstruct;

View File

@@ -29,13 +29,7 @@ $explorebar-width: 14rem;
$footer-height: 1.5rem; $footer-height: 1.5rem;
@function get-excluding-size() { @function get-excluding-size() {
@if $platform == linux { @return $footer-height + $titlebar-height;
@return $footer-height;
}
@else {
@return $footer-height + $titlebar-height;
}
} }
/* stylelint-disable-next-line function-no-unknown */ /* stylelint-disable-next-line function-no-unknown */

View File

@@ -20,6 +20,7 @@
body { body {
user-select: none; user-select: none;
background-color: #000;
} }
a { a {

View File

@@ -1,5 +1,7 @@
/* stylelint-disable function-no-unknown */ /* stylelint-disable function-no-unknown */
.theme-light { .theme-light {
background: $body-bg;
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
background: #fff; background: #fff;
} }

View File

@@ -25,12 +25,13 @@ export interface SidebarElement {
export interface CustomIcon {base64: string; uid: string} export interface CustomIcon {base64: string; uid: string}
if (!key) { // If no key in local storace if (!key) { // If no key in local storage
const storedKey = ipcRenderer.sendSync('get-key');// Ask for key stored on disk const storedKey = ipcRenderer.sendSync('get-key');// Ask for key stored on disk
if (!storedKey) { // Of nop stored key on disk if (!storedKey) { // If not stored key on disk
const newKey = crypto.randomBytes(16).toString('hex'); const newKey = crypto.randomBytes(16).toString('hex');
localStorage.setItem('key', newKey); localStorage.setItem('key', newKey);
ipcRenderer.send('set-key', newKey);
key = newKey; key = newKey;
} }
else { else {
@@ -162,7 +163,8 @@ export const useConnectionsStore = defineStore('connections', {
uid: connection.uid, uid: connection.uid,
client: connection.client, client: connection.client,
icon: conn.icon, icon: conn.icon,
name: conn.name name: conn.name,
hasCustomIcon: conn.hasCustomIcon
}; };
} }
return conn; return conn;

View File

@@ -147,11 +147,11 @@ export const useWorkspacesStore = defineStore('workspaces', {
else else
this.selectedWorkspace = uid; this.selectedWorkspace = uid;
}, },
async connectWorkspace (connection: ConnectionParams & { pgConnString?: string }, args?: {mode?: string; signal?: AbortSignal}) { async connectWorkspace (connection: ConnectionParams & { connString?: string }, args?: {mode?: string; signal?: AbortSignal}) {
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid
? { ? {
...workspace, ...workspace,
structure: [], structure: [] as WorkspaceStructure[],
breadcrumbs: {}, breadcrumbs: {},
loadedSchemas: new Set(), loadedSchemas: new Set(),
database: connection.database, database: connection.database,
@@ -167,7 +167,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid
? { ? {
...workspace, ...workspace,
structure: [], structure: [] as WorkspaceStructure[],
breadcrumbs: {}, breadcrumbs: {},
loadedSchemas: new Set(), loadedSchemas: new Set(),
connectionStatus: 'disconnected' connectionStatus: 'disconnected'
@@ -187,7 +187,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid
? { ? {
...workspace, ...workspace,
structure: [], structure: [] as WorkspaceStructure[],
breadcrumbs: {}, breadcrumbs: {},
loadedSchemas: new Set(), loadedSchemas: new Set(),
connectionStatus: 'failed' connectionStatus: 'failed'
@@ -200,9 +200,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
return reject(new Error('Connection aborted by user')); return reject(new Error('Connection aborted by user'));
else { else {
let clientCustomizations: Customizations; let clientCustomizations: Customizations;
const { updateLastConnection } = connectionsStore; connectionsStore.updateLastConnection(connection.uid);
updateLastConnection(connection.uid);
switch (connection.client) { switch (connection.client) {
case 'mysql': case 'mysql':
@@ -418,7 +416,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === uid this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === uid
? { ? {
...workspace, ...workspace,
structure: [], structure: [] as WorkspaceStructure[],
breadcrumbs: {}, breadcrumbs: {},
loadedSchemas: new Set(), loadedSchemas: new Set(),
connectionStatus: 'disconnected' connectionStatus: 'disconnected'
@@ -427,7 +425,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
this.selectTab({ uid, tab: 0 }); this.selectTab({ uid, tab: 0 });
}, },
async switchConnection (connection: ConnectionParams & { pgConnString?: string }) { async switchConnection (connection: ConnectionParams & { connString?: string }) {
await Connection.disconnect(connection.uid); await Connection.disconnect(connection.uid);
return this.connectWorkspace(connection, { mode: 'switch' }); return this.connectWorkspace(connection, { mode: 'switch' });
}, },

View File

@@ -3,6 +3,7 @@
declare module '@/App.vue'; declare module '@/App.vue';
declare module 'v-mask'; declare module 'v-mask';
declare module 'json2php'; declare module 'json2php';
declare module '*/encoding_charset.js';
declare module 'vuedraggable' {// <- to export as default declare module 'vuedraggable' {// <- to export as default
const draggableComponent: import('vue').DefineComponent<{ const draggableComponent: import('vue').DefineComponent<{
list: { list: {

View File

@@ -1,8 +1,7 @@
const path = require('path'); const path = require('path');
const webpack = require('webpack');
const ProgressPlugin = require('progress-webpack-plugin'); const ProgressPlugin = require('progress-webpack-plugin');
const { dependencies, devDependencies, version } = require('./package.json'); const { dependencies, devDependencies } = require('./package.json');
const externals = Object.keys(dependencies).concat(Object.keys(devDependencies)); const externals = Object.keys(dependencies).concat(Object.keys(devDependencies));
const isDevMode = process.env.NODE_ENV === 'development'; const isDevMode = process.env.NODE_ENV === 'development';
@@ -48,13 +47,7 @@ module.exports = { // Main
} }
}, },
plugins: [ plugins: [
new ProgressPlugin(true), new ProgressPlugin(true)
new webpack.DefinePlugin({
'process.env': {
PACKAGE_VERSION: `"${version}"`,
DISTRIBUTION: `"${process.env.DISTRIBUTION || 'none'}"`
}
})
], ],
module: { module: {
rules: [ rules: [