Compare commits
192 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6bd81cbff5 | |||
| b912190c5e | |||
| 8ae64a9dcf | |||
| d650d91d82 | |||
| d3322a4a15 | |||
| a65842e31f | |||
| 74fde66b51 | |||
| c3ea155a7b | |||
| c4b81e3d2c | |||
| 6f6ed1a741 | |||
| 311680c090 | |||
| 5e54aa553a | |||
| 6913970830 | |||
| 014e453e57 | |||
| 25b5341f76 | |||
| 1df51f9609 | |||
| 65d13189b3 | |||
| 0913011120 | |||
| 30ddc18eb1 | |||
| 697d755744 | |||
| e3f23ddc79 | |||
| 094acc40e8 | |||
| ebd4991de8 | |||
| d04a8fad4c | |||
| cb14bffc5a | |||
| a4c4d17381 | |||
| 21eb27f6e3 | |||
| abf0fc7942 | |||
| c2703edfde | |||
| d14b90ab20 | |||
| 48f4924932 | |||
| 765551988a | |||
| 4883eb0d1b | |||
| 06a9a93d1c | |||
| c92a0e1d43 | |||
| c6b5ee164b | |||
| c65075f887 | |||
| a1f678a3a1 | |||
| fe3fefaa4e | |||
| 3ccebcb0d1 | |||
| dbbae0eef2 | |||
| f11c4881f3 | |||
| 6398c6d7ce | |||
| 973ce8c3a7 | |||
| d971869283 | |||
| 987002b8f3 | |||
| f73fe495a5 | |||
| fe7b0e2bc7 | |||
| 23937c54e0 | |||
| b3d0fd9d2f | |||
| 497aaf6143 | |||
| 9d6db3a93b | |||
| 8a6f3e6809 | |||
| 6c419716a4 | |||
| d1a769205c | |||
| b784e342c9 | |||
| 0d82fd51c7 | |||
| 3b4d905485 | |||
| 53c63f0f4b | |||
| 5553e3cd8d | |||
| 1e195e07e0 | |||
| b6f0e15951 | |||
| da224303fe | |||
| 67ee130a9e | |||
| 18d908fa63 | |||
| c61f58854e | |||
| f789ecd2f1 | |||
| 1fdc30804a | |||
| 5ba10d0acb | |||
| 12803d8154 | |||
| 36c391ccff | |||
| 765fb6297c | |||
| 66255769ad | |||
| 04a8d38641 | |||
| 859d020031 | |||
| 3c541117d0 | |||
| 80ca2e5215 | |||
| 19f2aa2997 | |||
| ec657f30c7 | |||
| 7e84d495f5 | |||
| c3baedd93c | |||
| ae9676f744 | |||
| 7ec156a5d1 | |||
| b80cbea1bc | |||
| 4600fa9f32 | |||
| 6e0b3e5cdc | |||
| 519ff87f5d | |||
| d4a363e37e | |||
| a3cfc45fef | |||
| 60602e02d9 | |||
| 44366f7872 | |||
| 2f18d8c204 | |||
| 08efbee52b | |||
| eac8d78c5d | |||
| db73673374 | |||
| 281cdb7264 | |||
| 101c80d820 | |||
| 1e06f65d9e | |||
| eea85709ed | |||
| cbca974529 | |||
| fc27f57580 | |||
| e50dd6606e | |||
| 8c9e232d65 | |||
| ef98888394 | |||
| e4b9ba34df | |||
| 169e0ec9df | |||
| 2779353a32 | |||
| 35aabb987c | |||
| 06f02070c7 | |||
| d138d3e786 | |||
| dbea68d33a | |||
| 72bcabf615 | |||
| 5db68eac24 | |||
| 7d9d88860e | |||
| 06c80ad982 | |||
| c4335527f8 | |||
| 90d27a2ad8 | |||
| 93fd0a9af0 | |||
| 987b0aeb41 | |||
| 8dc5ac0b25 | |||
| d310a47523 | |||
| bd8fa3776d | |||
| 305796af53 | |||
| 60c10a69a3 | |||
| c8aad9839f | |||
| 64016a1326 | |||
| a489c7ad8e | |||
| afb9ba7ad6 | |||
| b1de5b1120 | |||
| 144a23e89b | |||
| a6763a3e5d | |||
| f047ec787a | |||
| d80c368ccb | |||
| 9b60173b8c | |||
| 8556974ef1 | |||
| edf9f3a2be | |||
| 7bb9414be8 | |||
| 6e439adb51 | |||
| f6b783e74a | |||
| 171b81461c | |||
| 83db76aed8 | |||
| e6d1bb7e5c | |||
| a3d9fe76d6 | |||
| 0f4f154637 | |||
| 7d112a208f | |||
| c867d39d8d | |||
| 83b6c939f7 | |||
| e1e4eb5d6f | |||
| a14c08f122 | |||
| 5fd50dcf45 | |||
| b2ac4ee245 | |||
| 0ad7c0546b | |||
| 748381fef3 | |||
| 7eb9e42210 | |||
| 36a4b67ef4 | |||
| 94b35e3d5f | |||
| c5aa82b76a | |||
| c2920e195f | |||
| 92a78a419e | |||
| ece5779b41 | |||
| de622b055b | |||
| 7e88b930ec | |||
| c9adc2b852 | |||
| 91d3aba611 | |||
| 48bc11dcb5 | |||
| 4b6b74604b | |||
| 7ad3fc4751 | |||
| e604450cfb | |||
| 9cfbc83896 | |||
| 98b2f4ec35 | |||
| 47e30f2daf | |||
| 6c21da6959 | |||
| c5fe71d390 | |||
| 3c58cb1b9c | |||
| dc452cdadf | |||
| e855365cbb | |||
| cffa288227 | |||
| 89ddced342 | |||
| 7457328b59 | |||
| 44ff413810 | |||
| d174c2c2d8 | |||
| 480f4c9a8c | |||
| 7474cc5d8a | |||
| dfeb910ac9 | |||
| 98f2b5dd08 | |||
| dca9ea24d7 | |||
| 37d54811e0 | |||
| 0f2af6eb37 | |||
| d6ae3d4f16 | |||
| 9c1819467a | |||
| 417334d140 | |||
| aa7fb74312 |
@@ -43,7 +43,7 @@ jobs:
|
||||
repository: dbgate/dbgate-pro
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
path: dbgate-pro
|
||||
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
|
||||
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
|
||||
- name: Merge dbgate/dbgate-pro
|
||||
run: |
|
||||
mkdir ../dbgate-pro
|
||||
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
repository: dbgate/dbgate-pro
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
path: dbgate-pro
|
||||
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
|
||||
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
|
||||
- name: Merge dbgate/dbgate-pro
|
||||
run: |
|
||||
mkdir ../dbgate-pro
|
||||
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
repository: dbgate/dbgate-pro
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
path: dbgate-pro
|
||||
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
|
||||
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
|
||||
- name: Merge dbgate/dbgate-pro
|
||||
run: |
|
||||
mkdir ../dbgate-pro
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
repository: dbgate/dbgate-pro
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
path: dbgate-pro
|
||||
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
|
||||
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
|
||||
- name: Merge dbgate/dbgate-pro
|
||||
run: |
|
||||
mkdir ../dbgate-pro
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
repository: dbgate/dbgate-pro
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
path: dbgate-pro
|
||||
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
|
||||
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
|
||||
- name: Merge dbgate/dbgate-pro
|
||||
run: |
|
||||
mkdir ../dbgate-pro
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
repository: dbgate/dbgate-pro
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
path: dbgate-pro
|
||||
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
|
||||
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
|
||||
- name: Merge dbgate/dbgate-pro
|
||||
run: |
|
||||
mkdir ../dbgate-pro
|
||||
|
||||
@@ -42,24 +42,6 @@ jobs:
|
||||
run: |
|
||||
cd packages/tools
|
||||
yarn test:ci
|
||||
- uses: tanmen/jest-reporter@v1
|
||||
if: always()
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
result-file: integration-tests/result.json
|
||||
action-name: Integration tests
|
||||
- uses: tanmen/jest-reporter@v1
|
||||
if: always()
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
result-file: packages/filterparser/result.json
|
||||
action-name: Filter parser test results
|
||||
- uses: tanmen/jest-reporter@v1
|
||||
if: always()
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
result-file: packages/datalib/result.json
|
||||
action-name: Datalib (perspectives) test results
|
||||
services:
|
||||
postgres-integr:
|
||||
image: postgres
|
||||
|
||||
@@ -24,6 +24,7 @@ docker/plugins
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.translation
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
|
||||
@@ -8,6 +8,48 @@ Builds:
|
||||
- linux - application for linux
|
||||
- win - application for Windows
|
||||
|
||||
## 6.7.1
|
||||
- ADDED: LANGUAGE environment variable for the web version. #1266
|
||||
- ADDED: New localizations (Italian, Portugese (Brazil), Japanese)
|
||||
- ADDED: Option to detect language from browser settings in web version
|
||||
- FIXED: Check updates option no longer available in 6.7.0 #1263
|
||||
- FIXED: A MERGE statement must be terminated by a semi-colon (;), but dbgate stripped it. #1257
|
||||
- ADDED: Show table size #552
|
||||
- ADDED: Sort tables by size and by row count
|
||||
- ADDED: Connect to Legacy MongoDB (Premium) #540
|
||||
- FIXED: Fixed problems in saving team files in Team Premium edition
|
||||
- CHANGED: Files are by default saved to team folders in Team Premium edition
|
||||
- ADDED: Other files types supported in Team Premium edition (diagrams, query design, perspectives, import/export jobs, shell scripts, database compare jobs)
|
||||
|
||||
## 6.7.0
|
||||
- ADDED: Added localization support, now you can use DbGate in multiple languages (French, Spanish, German, Czech, Slovak, Simplified Chinese) #347 #705 #939 #1079
|
||||
- CHANGED: Solved many issues with binary fields, huge performance improvements in binary fields processing
|
||||
- FIXED: Export to CSV produces empty file #1247
|
||||
- CHANGED: Upgraded electron to version 38 #1243
|
||||
- FIXED: PostgreSQL export to SQL and XML doesn't include bytea field contents #1228
|
||||
- FIXED: Export CSV broken #1080
|
||||
- FIXED: Inconsistent handling of hex-like strings #680
|
||||
- FIXED: Export mongodb binary cell as binary file #292
|
||||
- CHANGED: SSL is used automatically for connections to Azure databases
|
||||
- ADDED: New export formats CSV for Excel, TSV
|
||||
- FIXED: Horizontal scrolling on macOS trackpad/Magic Mouse #1250
|
||||
|
||||
## 6.6.12
|
||||
- FIXED: Cannot paste license key on Mac (special commands like copy/paste were disabled on license screen)
|
||||
|
||||
## 6.6.11
|
||||
- FIXED: Fixed theming on application startup
|
||||
- CHANGED: Improved licensing page
|
||||
|
||||
## 6.6.10
|
||||
- FIXED: License from environment variable is not refreshed #1245
|
||||
- FIXED: connection closing / reconnecting #1237
|
||||
- ADDED: retain history across multiple queries #1236
|
||||
- ADDED: load CSVs to temp tables #1235
|
||||
- FIXED: Not possible to scroll the data view horizontally by pressing shift and scroll mouse middle button on Mac #453
|
||||
- FIXED: Expired trial workflow (Premium)
|
||||
- ADDED: Column name collision resolving #1234 (MySQL)
|
||||
|
||||
## 6.6.8
|
||||
- CHANGED: Windows executable now uses Azure trusted signing certificate
|
||||
- CHANGED: NPM packages now use GitHub OIDC provenance signing for better security
|
||||
|
||||
+1
-1
@@ -128,7 +128,7 @@
|
||||
"devDependencies": {
|
||||
"copyfiles": "^2.2.0",
|
||||
"cross-env": "^6.0.3",
|
||||
"electron": "30.0.2",
|
||||
"electron": "38.6.0",
|
||||
"electron-builder": "25.1.8"
|
||||
}
|
||||
}
|
||||
|
||||
+41
-13
@@ -31,6 +31,16 @@ let mainModule;
|
||||
let appUpdateStatus = '';
|
||||
let settingsJson = {};
|
||||
|
||||
function getTranslated(key) {
|
||||
if (typeof key === 'string' && global.TRANSLATION_DATA?.[key]) {
|
||||
return global.TRANSLATION_DATA?.[key];
|
||||
}
|
||||
if (typeof key?._transKey === 'string') {
|
||||
return global.TRANSLATION_DATA?.[key._transKey] ?? key._transOptions?.defaultMessage;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
process.on('uncaughtException', function (error) {
|
||||
console.error('uncaughtException', error);
|
||||
});
|
||||
@@ -63,6 +73,7 @@ try {
|
||||
let mainWindow;
|
||||
let mainMenu;
|
||||
let runCommandOnLoad = null;
|
||||
let mainWindowMenuSet = false;
|
||||
|
||||
log.transports.file.level = 'debug';
|
||||
autoUpdater.logger = log;
|
||||
@@ -85,17 +96,22 @@ function formatKeyText(keyText) {
|
||||
return keyText.replace('CtrlOrCommand+', 'Ctrl+');
|
||||
}
|
||||
|
||||
function commandItem(item) {
|
||||
function commandItem(item, disableAll = false) {
|
||||
const id = item.command;
|
||||
const command = commands[id];
|
||||
if (item.skipInApp) {
|
||||
return { skip: true };
|
||||
}
|
||||
if (!command) {
|
||||
return { skip: true };
|
||||
}
|
||||
return {
|
||||
id,
|
||||
label: command ? command.menuName || command.toolbarName || command.name : id,
|
||||
label: command
|
||||
? getTranslated(command.menuName) || getTranslated(command.toolbarName) || getTranslated(command.name)
|
||||
: id,
|
||||
accelerator: formatKeyText(command ? command.keyText : undefined),
|
||||
enabled: command ? command.enabled : false,
|
||||
enabled: command ? command.enabled && (!disableAll || command.systemCommand) : false,
|
||||
click() {
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('run-command', id);
|
||||
@@ -107,14 +123,14 @@ function commandItem(item) {
|
||||
};
|
||||
}
|
||||
|
||||
function buildMenu() {
|
||||
function buildMenu(disableAll = false) {
|
||||
let template = _cloneDeepWith(mainMenuDefinition({ editMenu: true, isMac: isMac() }), item => {
|
||||
if (item.divider) {
|
||||
return { type: 'separator' };
|
||||
}
|
||||
|
||||
if (item.command) {
|
||||
return commandItem(item);
|
||||
return commandItem(item, disableAll);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -129,7 +145,7 @@ function buildMenu() {
|
||||
{
|
||||
label: 'DbGate',
|
||||
submenu: [
|
||||
commandItem({ command: 'about.show' }),
|
||||
commandItem({ command: 'about.show' }, disableAll),
|
||||
{ role: 'services' },
|
||||
{ role: 'hide' },
|
||||
{ role: 'hideOthers' },
|
||||
@@ -145,22 +161,28 @@ function buildMenu() {
|
||||
}
|
||||
|
||||
ipcMain.on('update-commands', async (event, arg) => {
|
||||
commands = JSON.parse(arg);
|
||||
const parsed = JSON.parse(arg);
|
||||
commands = parsed.commands;
|
||||
const isModalOpened = parsed.isModalOpened;
|
||||
const dbgatePage = parsed.dbgatePage;
|
||||
for (const key of Object.keys(commands)) {
|
||||
const menu = mainMenu.getMenuItemById(key);
|
||||
if (!menu) continue;
|
||||
const command = commands[key];
|
||||
|
||||
// rebuild menu
|
||||
if (menu.label != command.text || menu.accelerator != command.keyText) {
|
||||
mainMenu = buildMenu();
|
||||
if (global.TRANSLATION_DATA && (menu.label != command.text || menu.accelerator != command.keyText)) {
|
||||
mainMenu = buildMenu(isModalOpened || !!dbgatePage);
|
||||
|
||||
Menu.setApplicationMenu(mainMenu);
|
||||
// mainWindow.setMenu(mainMenu);
|
||||
if (!mainWindowMenuSet) {
|
||||
mainWindow.setMenu(mainMenu);
|
||||
mainWindowMenuSet = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
menu.enabled = command.enabled;
|
||||
menu.enabled = command.enabled && !isModalOpened && !dbgatePage;
|
||||
}
|
||||
});
|
||||
ipcMain.on('quit-app', async (event, arg) => {
|
||||
@@ -303,6 +325,12 @@ ipcMain.on('check-for-updates', async (event, url) => {
|
||||
autoUpdater.autoDownload = false;
|
||||
autoUpdater.checkForUpdates();
|
||||
});
|
||||
ipcMain.on('translation-data', async (event, arg) => {
|
||||
global.TRANSLATION_DATA = JSON.parse(arg);
|
||||
mainMenu = buildMenu();
|
||||
Menu.setApplicationMenu(mainMenu);
|
||||
mainWindow.setMenu(mainMenu);
|
||||
});
|
||||
|
||||
function fillMissingSettings(value) {
|
||||
const res = {
|
||||
@@ -379,8 +407,8 @@ function createWindow() {
|
||||
mainWindow.setFullScreen(true);
|
||||
}
|
||||
|
||||
mainMenu = buildMenu();
|
||||
mainWindow.setMenu(mainMenu);
|
||||
// mainMenu = buildMenu();
|
||||
// mainWindow.setMenu(mainMenu);
|
||||
|
||||
function loadMainWindow() {
|
||||
const startUrl =
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
module.exports = ({ editMenu, isMac }) => [
|
||||
function _t(key, { defaultMessage, currentTranslations } = {}) {
|
||||
return (currentTranslations || global.TRANSLATION_DATA)?.[key] || defaultMessage;
|
||||
}
|
||||
|
||||
module.exports = ({ editMenu, isMac }, currentTranslations = null) => [
|
||||
{
|
||||
label: 'File',
|
||||
label: _t('menu.file', { defaultMessage: 'File', currentTranslations }),
|
||||
submenu: [
|
||||
{ command: 'new.connection', hideDisabled: true },
|
||||
{ command: 'new.sqliteDatabase', hideDisabled: true },
|
||||
@@ -28,7 +32,7 @@ module.exports = ({ editMenu, isMac }) => [
|
||||
},
|
||||
editMenu
|
||||
? {
|
||||
label: 'Edit',
|
||||
label: _t('menu.edit', { defaultMessage: 'Edit', currentTranslations }),
|
||||
submenu: [
|
||||
{ command: 'edit.undo' },
|
||||
{ command: 'edit.redo' },
|
||||
@@ -53,7 +57,7 @@ module.exports = ({ editMenu, isMac }) => [
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
label: 'View',
|
||||
label: _t('menu.view', { defaultMessage: 'View', currentTranslations }),
|
||||
submenu: [
|
||||
{ command: 'app.reload', hideDisabled: true },
|
||||
{ command: 'app.toggleDevTools', hideDisabled: true },
|
||||
@@ -72,10 +76,12 @@ module.exports = ({ editMenu, isMac }) => [
|
||||
{ command: 'app.zoomIn', hideDisabled: true },
|
||||
{ command: 'app.zoomOut', hideDisabled: true },
|
||||
{ command: 'app.zoomReset', hideDisabled: true },
|
||||
{ divider: true },
|
||||
{ command: 'app.showLogs', hideDisabled: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Tools',
|
||||
label: _t('menu.tools', { defaultMessage: 'Tools', currentTranslations }),
|
||||
submenu: [
|
||||
{ command: 'database.search', hideDisabled: true },
|
||||
{ command: 'commandPalette.show', hideDisabled: true },
|
||||
@@ -91,6 +97,8 @@ module.exports = ({ editMenu, isMac }) => [
|
||||
{ divider: true },
|
||||
{ command: 'app.exportConnections', hideDisabled: true },
|
||||
{ command: 'app.importConnections', hideDisabled: true },
|
||||
{ divider: true },
|
||||
{ command: 'app.managePlugins', hideDisabled: true },
|
||||
],
|
||||
},
|
||||
...(isMac
|
||||
@@ -102,7 +110,7 @@ module.exports = ({ editMenu, isMac }) => [
|
||||
]
|
||||
: []),
|
||||
{
|
||||
label: 'Help',
|
||||
label: _t('menu.help', { defaultMessage: 'Help', currentTranslations }),
|
||||
submenu: [
|
||||
{ command: 'app.openDocs', hideDisabled: true },
|
||||
{ command: 'app.openWeb', hideDisabled: true },
|
||||
@@ -114,7 +122,7 @@ module.exports = ({ editMenu, isMac }) => [
|
||||
{ command: 'tabs.changelog', hideDisabled: true },
|
||||
{ command: 'about.show', hideDisabled: true },
|
||||
{ divider: true },
|
||||
{ command: 'file.checkForUpdates', hideDisabled: true },
|
||||
{ command: 'app.checkForUpdates', hideDisabled: true },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
+279
-224
@@ -16,9 +16,9 @@
|
||||
ajv-keywords "^3.4.1"
|
||||
|
||||
"@electron/asar@^3.2.7":
|
||||
version "3.2.17"
|
||||
resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.2.17.tgz#91d28087aad80d1a1c8cc4e667c6476edf50f949"
|
||||
integrity sha512-OcWImUI686w8LkghQj9R2ynZ2ME693Ek6L1SiaAgqGKzBaTIZw3fHDqN82Rcl+EU1Gm9EgkJ5KLIY/q5DCRbbA==
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.4.1.tgz#4e9196a4b54fba18c56cd8d5cac67c5bdc588065"
|
||||
integrity sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==
|
||||
dependencies:
|
||||
commander "^5.0.0"
|
||||
glob "^7.1.6"
|
||||
@@ -98,6 +98,18 @@
|
||||
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
|
||||
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
|
||||
|
||||
"@isaacs/balanced-match@^4.0.1":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz#3081dadbc3460661b751e7591d7faea5df39dd29"
|
||||
integrity sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==
|
||||
|
||||
"@isaacs/brace-expansion@^5.0.0":
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz#4b3dabab7d8e75a429414a96bd67bf4c1d13e0f3"
|
||||
integrity sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==
|
||||
dependencies:
|
||||
"@isaacs/balanced-match" "^4.0.1"
|
||||
|
||||
"@isaacs/cliui@^8.0.2":
|
||||
version "8.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
|
||||
@@ -202,16 +214,23 @@
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/ms@*":
|
||||
version "0.7.34"
|
||||
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433"
|
||||
integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-2.1.0.tgz#052aa67a48eccc4309d7f0191b7e41434b90bb78"
|
||||
integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==
|
||||
|
||||
"@types/node@*", "@types/node@^20.9.0":
|
||||
version "20.12.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.10.tgz#8f0c3f12b0f075eee1fe20c1afb417e9765bef76"
|
||||
integrity sha512-Eem5pH9pmWBHoGAT8Dr5fdc5rYA+4NAovdM4EktRPVAAiJhmWWfQrA0cFhAbOsQdSfIHjAud6YdkbL69+zSKjw==
|
||||
"@types/node@*":
|
||||
version "24.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-24.10.0.tgz#6b79086b0dfc54e775a34ba8114dcc4e0221f31f"
|
||||
integrity sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==
|
||||
dependencies:
|
||||
undici-types "~5.26.4"
|
||||
undici-types "~7.16.0"
|
||||
|
||||
"@types/node@^22.7.7":
|
||||
version "22.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.19.0.tgz#849606ef3920850583a4e7ee0930987c35ad80be"
|
||||
integrity sha512-xpr/lmLPQEj+TUnHmR+Ab91/glhJvsqcjB+yY0Ix9GO70H6Lb4FHH5GeqdOE5btAx7eIMwuHkp4H2MSkLcqWbA==
|
||||
dependencies:
|
||||
undici-types "~6.21.0"
|
||||
|
||||
"@types/plist@^3.0.1":
|
||||
version "3.0.5"
|
||||
@@ -229,9 +248,9 @@
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/verror@^1.10.3":
|
||||
version "1.10.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/verror/-/verror-1.10.10.tgz#d5a4b56abac169bfbc8b23d291363a682e6fa087"
|
||||
integrity sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg==
|
||||
version "1.10.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/verror/-/verror-1.10.11.tgz#d3d6b418978c8aa202d41e5bb3483227b6ecc1bb"
|
||||
integrity sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==
|
||||
|
||||
"@types/yauzl@^2.9.1":
|
||||
version "2.10.3"
|
||||
@@ -241,9 +260,9 @@
|
||||
"@types/node" "*"
|
||||
|
||||
"@xmldom/xmldom@^0.8.8":
|
||||
version "0.8.10"
|
||||
resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99"
|
||||
integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==
|
||||
version "0.8.11"
|
||||
resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.11.tgz#b79de2d67389734c57c52595f7a7305e30c2d608"
|
||||
integrity sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==
|
||||
|
||||
"@yarnpkg/lockfile@^1.1.0":
|
||||
version "1.1.0"
|
||||
@@ -263,14 +282,14 @@ agent-base@6, agent-base@^6.0.2:
|
||||
debug "4"
|
||||
|
||||
agent-base@^7.1.0, agent-base@^7.1.2:
|
||||
version "7.1.3"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1"
|
||||
integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==
|
||||
version "7.1.4"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8"
|
||||
integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==
|
||||
|
||||
agentkeepalive@^4.2.1:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923"
|
||||
integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a"
|
||||
integrity sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==
|
||||
dependencies:
|
||||
humanize-ms "^1.2.1"
|
||||
|
||||
@@ -303,9 +322,9 @@ ansi-regex@^5.0.1:
|
||||
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
|
||||
|
||||
ansi-regex@^6.0.1:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654"
|
||||
integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==
|
||||
version "6.2.2"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1"
|
||||
integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==
|
||||
|
||||
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
||||
version "4.3.0"
|
||||
@@ -315,9 +334,9 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
||||
color-convert "^2.0.1"
|
||||
|
||||
ansi-styles@^6.1.0:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
|
||||
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
|
||||
version "6.2.3"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041"
|
||||
integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==
|
||||
|
||||
app-builder-bin@5.0.0-alpha.10:
|
||||
version "5.0.0-alpha.10"
|
||||
@@ -363,9 +382,9 @@ app-builder-lib@25.1.8:
|
||||
temp-file "^3.4.0"
|
||||
|
||||
"aproba@^1.0.3 || ^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
|
||||
integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.1.0.tgz#75500a190313d95c64e871e7e4284c6ac219f0b1"
|
||||
integrity sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==
|
||||
|
||||
are-we-there-yet@^3.0.0:
|
||||
version "3.0.1"
|
||||
@@ -395,10 +414,10 @@ async-exit-hook@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/async-exit-hook/-/async-exit-hook-2.0.1.tgz#8bd8b024b0ec9b1c01cccb9af9db29bd717dfaf3"
|
||||
integrity sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==
|
||||
|
||||
async@^3.2.3:
|
||||
version "3.2.5"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66"
|
||||
integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==
|
||||
async@^3.2.6:
|
||||
version "3.2.6"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce"
|
||||
integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
@@ -447,33 +466,33 @@ boolean@^3.0.1:
|
||||
integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
||||
version "1.1.12"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843"
|
||||
integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
brace-expansion@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
|
||||
integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7"
|
||||
integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
|
||||
braces@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
||||
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
|
||||
braces@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
|
||||
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
|
||||
dependencies:
|
||||
fill-range "^7.0.1"
|
||||
fill-range "^7.1.1"
|
||||
|
||||
buffer-crc32@~0.2.3:
|
||||
version "0.2.13"
|
||||
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||
integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
buffer-equal-constant-time@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
||||
integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
|
||||
@@ -499,10 +518,10 @@ builder-util-runtime@9.2.10:
|
||||
debug "^4.3.4"
|
||||
sax "^1.2.4"
|
||||
|
||||
builder-util-runtime@9.2.5:
|
||||
version "9.2.5"
|
||||
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-9.2.5.tgz#0afdffa0adb5c84c14926c7dd2cf3c6e96e9be83"
|
||||
integrity sha512-HjIDfhvqx/8B3TDN4GbABQcgpewTU4LMRTQPkVpKYV3lsuxEJoIfvg09GyWTNmfVNSUAYf+fbTN//JX4TH20pg==
|
||||
builder-util-runtime@9.3.1:
|
||||
version "9.3.1"
|
||||
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-9.3.1.tgz#0daedde0f6d381f2a00a50a407b166fe7dca1a67"
|
||||
integrity sha512-2/egrNDDnRaxVwK3A+cJq6UOlqOdedGA7JPqCeJjN2Zjk1/QB/6QUi3b714ScIGS7HafFXTyzJEOr5b44I3kvQ==
|
||||
dependencies:
|
||||
debug "^4.3.4"
|
||||
sax "^1.2.4"
|
||||
@@ -571,7 +590,15 @@ cacheable-request@^7.0.2:
|
||||
normalize-url "^6.0.1"
|
||||
responselike "^2.0.0"
|
||||
|
||||
chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2:
|
||||
call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
|
||||
integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
function-bind "^1.1.2"
|
||||
|
||||
chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
||||
@@ -744,9 +771,9 @@ cross-env@^6.0.3:
|
||||
cross-spawn "^7.0.0"
|
||||
|
||||
cross-spawn@^6.0.5:
|
||||
version "6.0.5"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
||||
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
|
||||
version "6.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.6.tgz#30d0efa0712ddb7eb5a76e1e8721bffafa6b5d57"
|
||||
integrity sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==
|
||||
dependencies:
|
||||
nice-try "^1.0.4"
|
||||
path-key "^2.0.1"
|
||||
@@ -754,26 +781,19 @@ cross-spawn@^6.0.5:
|
||||
shebang-command "^1.2.0"
|
||||
which "^1.2.9"
|
||||
|
||||
cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.3:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
|
||||
cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.3, cross-spawn@^7.0.6:
|
||||
version "7.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
|
||||
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
|
||||
dependencies:
|
||||
path-key "^3.1.0"
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debug@^4.3.3:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
|
||||
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
|
||||
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4:
|
||||
version "4.4.3"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
|
||||
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
|
||||
dependencies:
|
||||
ms "^2.1.3"
|
||||
|
||||
@@ -825,9 +845,9 @@ delegates@^1.0.0:
|
||||
integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
|
||||
|
||||
detect-libc@^2.0.1:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
|
||||
integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad"
|
||||
integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==
|
||||
|
||||
detect-node@^2.0.4:
|
||||
version "2.1.0"
|
||||
@@ -878,9 +898,18 @@ dotenv-expand@^11.0.6:
|
||||
dotenv "^16.4.5"
|
||||
|
||||
dotenv@^16.4.5:
|
||||
version "16.4.7"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.7.tgz#0e20c5b82950140aa99be360a8a5f52335f53c26"
|
||||
integrity sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==
|
||||
version "16.6.1"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.6.1.tgz#773f0e69527a8315c7285d5ee73c4459d20a8020"
|
||||
integrity sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==
|
||||
|
||||
dunder-proto@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
|
||||
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
|
||||
dependencies:
|
||||
call-bind-apply-helpers "^1.0.1"
|
||||
es-errors "^1.3.0"
|
||||
gopd "^1.2.0"
|
||||
|
||||
eastasianwidth@^0.2.0:
|
||||
version "0.2.0"
|
||||
@@ -936,11 +965,11 @@ electron-publish@25.1.7:
|
||||
mime "^2.5.2"
|
||||
|
||||
electron-updater@^6.3.4:
|
||||
version "6.3.4"
|
||||
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-6.3.4.tgz#3934bc89875bb524c2cbbd11041114e97c0c2496"
|
||||
integrity sha512-uZUo7p1Y53G4tl6Cgw07X1yF8Jlz6zhaL7CQJDZ1fVVkOaBfE2cWtx80avwDVi8jHp+I/FWawrMgTAeCCNIfAg==
|
||||
version "6.6.2"
|
||||
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-6.6.2.tgz#3e65e044f1a99b00d61e200e24de8e709c69ce99"
|
||||
integrity sha512-Cr4GDOkbAUqRHP5/oeOmH/L2Bn6+FQPxVLZtPbcmKZC63a1F3uu5EefYOssgZXG3u/zBlubbJ5PJdITdMVggbw==
|
||||
dependencies:
|
||||
builder-util-runtime "9.2.5"
|
||||
builder-util-runtime "9.3.1"
|
||||
fs-extra "^10.1.0"
|
||||
js-yaml "^4.1.0"
|
||||
lazy-val "^1.0.5"
|
||||
@@ -949,13 +978,13 @@ electron-updater@^6.3.4:
|
||||
semver "^7.6.3"
|
||||
tiny-typed-emitter "^2.1.0"
|
||||
|
||||
electron@30.0.2:
|
||||
version "30.0.2"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-30.0.2.tgz#95ba019216bf8be9f3097580123e33ea37497733"
|
||||
integrity sha512-zv7T+GG89J/hyWVkQsLH4Y/rVEfqJG5M/wOBIGNaDdqd8UV9/YZPdS7CuFeaIj0H9LhCt95xkIQNpYB/3svOkQ==
|
||||
electron@38.6.0:
|
||||
version "38.6.0"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-38.6.0.tgz#c862bff41d42776e307bf5cc92503dda23612339"
|
||||
integrity sha512-68OFNxJlrEStA+t8k5atzf4frJddvRR1N1oalr49Ll8YZ0+0nEsDhw4UNhTCoZKTjSYcxFF/4rt+sco+OlnB3g==
|
||||
dependencies:
|
||||
"@electron/get" "^2.0.0"
|
||||
"@types/node" "^20.9.0"
|
||||
"@types/node" "^22.7.7"
|
||||
extract-zip "^2.0.1"
|
||||
|
||||
emoji-regex@^8.0.0:
|
||||
@@ -976,9 +1005,9 @@ encoding@^0.1.13:
|
||||
iconv-lite "^0.6.2"
|
||||
|
||||
end-of-stream@^1.1.0:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
|
||||
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
|
||||
version "1.4.5"
|
||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c"
|
||||
integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==
|
||||
dependencies:
|
||||
once "^1.4.0"
|
||||
|
||||
@@ -992,27 +1021,42 @@ err-code@^2.0.2:
|
||||
resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
|
||||
integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==
|
||||
|
||||
es-define-property@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
|
||||
integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==
|
||||
dependencies:
|
||||
get-intrinsic "^1.2.4"
|
||||
es-define-property@^1.0.0, es-define-property@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
|
||||
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
|
||||
|
||||
es-errors@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
|
||||
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
|
||||
|
||||
es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
|
||||
integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
|
||||
es-set-tostringtag@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d"
|
||||
integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
get-intrinsic "^1.2.6"
|
||||
has-tostringtag "^1.0.2"
|
||||
hasown "^2.0.2"
|
||||
|
||||
es6-error@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
|
||||
integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
|
||||
|
||||
escalade@^3.1.1:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27"
|
||||
integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
|
||||
integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
|
||||
|
||||
escape-string-regexp@^4.0.0:
|
||||
version "4.0.0"
|
||||
@@ -1020,9 +1064,9 @@ escape-string-regexp@^4.0.0:
|
||||
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
|
||||
|
||||
exponential-backoff@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6"
|
||||
integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.3.tgz#51cf92c1c0493c766053f9d3abee4434c244d2f6"
|
||||
integrity sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==
|
||||
|
||||
extract-zip@^2.0.1:
|
||||
version "2.0.1"
|
||||
@@ -1064,10 +1108,10 @@ filelist@^1.0.4:
|
||||
dependencies:
|
||||
minimatch "^5.0.1"
|
||||
|
||||
fill-range@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
||||
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
|
||||
fill-range@^7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
|
||||
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
|
||||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
@@ -1079,20 +1123,22 @@ find-yarn-workspace-root@^2.0.0:
|
||||
micromatch "^4.0.2"
|
||||
|
||||
foreground-child@^3.1.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77"
|
||||
integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f"
|
||||
integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==
|
||||
dependencies:
|
||||
cross-spawn "^7.0.0"
|
||||
cross-spawn "^7.0.6"
|
||||
signal-exit "^4.0.1"
|
||||
|
||||
form-data@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
|
||||
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4"
|
||||
integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
es-set-tostringtag "^2.1.0"
|
||||
hasown "^2.0.2"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
fs-extra@^10.0.0, fs-extra@^10.1.0:
|
||||
@@ -1105,9 +1151,9 @@ fs-extra@^10.0.0, fs-extra@^10.1.0:
|
||||
universalify "^2.0.0"
|
||||
|
||||
fs-extra@^11.1.1:
|
||||
version "11.2.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b"
|
||||
integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==
|
||||
version "11.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.2.tgz#c838aeddc6f4a8c74dd15f85e11fe5511bfe02a4"
|
||||
integrity sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.0"
|
||||
jsonfile "^6.0.1"
|
||||
@@ -1168,16 +1214,29 @@ get-caller-file@^2.0.5:
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||
|
||||
get-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
|
||||
integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
|
||||
get-intrinsic@^1.2.6:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
|
||||
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
|
||||
dependencies:
|
||||
call-bind-apply-helpers "^1.0.2"
|
||||
es-define-property "^1.0.1"
|
||||
es-errors "^1.3.0"
|
||||
es-object-atoms "^1.1.1"
|
||||
function-bind "^1.1.2"
|
||||
has-proto "^1.0.1"
|
||||
has-symbols "^1.0.3"
|
||||
hasown "^2.0.0"
|
||||
get-proto "^1.0.1"
|
||||
gopd "^1.2.0"
|
||||
has-symbols "^1.1.0"
|
||||
hasown "^2.0.2"
|
||||
math-intrinsics "^1.1.0"
|
||||
|
||||
get-proto@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
|
||||
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
|
||||
dependencies:
|
||||
dunder-proto "^1.0.1"
|
||||
es-object-atoms "^1.0.0"
|
||||
|
||||
get-stream@^5.1.0:
|
||||
version "5.2.0"
|
||||
@@ -1241,12 +1300,10 @@ globalthis@^1.0.1:
|
||||
define-properties "^1.2.1"
|
||||
gopd "^1.0.1"
|
||||
|
||||
gopd@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
|
||||
integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
|
||||
dependencies:
|
||||
get-intrinsic "^1.1.3"
|
||||
gopd@^1.0.1, gopd@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
|
||||
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
|
||||
|
||||
got@^11.7.0, got@^11.8.5:
|
||||
version "11.8.6"
|
||||
@@ -1282,22 +1339,24 @@ has-property-descriptors@^1.0.0:
|
||||
dependencies:
|
||||
es-define-property "^1.0.0"
|
||||
|
||||
has-proto@^1.0.1:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd"
|
||||
integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==
|
||||
has-symbols@^1.0.3, has-symbols@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
|
||||
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
|
||||
|
||||
has-symbols@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
|
||||
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
|
||||
has-tostringtag@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
|
||||
integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
|
||||
dependencies:
|
||||
has-symbols "^1.0.3"
|
||||
|
||||
has-unicode@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
|
||||
integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==
|
||||
|
||||
hasown@^2.0.0:
|
||||
hasown@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
|
||||
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
|
||||
@@ -1312,9 +1371,9 @@ hosted-git-info@^4.1.0:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
|
||||
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz#205f4db64f8562b76a4ff9235aa5279839a09dd5"
|
||||
integrity sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==
|
||||
|
||||
http-proxy-agent@^5.0.0:
|
||||
version "5.0.0"
|
||||
@@ -1412,13 +1471,10 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1,
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
ip-address@^9.0.5:
|
||||
version "9.0.5"
|
||||
resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a"
|
||||
integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==
|
||||
dependencies:
|
||||
jsbn "1.1.0"
|
||||
sprintf-js "^1.1.3"
|
||||
ip-address@^10.0.1:
|
||||
version "10.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.1.0.tgz#d8dcffb34d0e02eb241427444a6e23f5b0595aa4"
|
||||
integrity sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==
|
||||
|
||||
is-ci@^2.0.0:
|
||||
version "2.0.0"
|
||||
@@ -1487,9 +1543,9 @@ isbinaryfile@^4.0.8:
|
||||
integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==
|
||||
|
||||
isbinaryfile@^5.0.0:
|
||||
version "5.0.4"
|
||||
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-5.0.4.tgz#2a2edefa76cafa66613fe4c1ea52f7f031017bdf"
|
||||
integrity sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==
|
||||
version "5.0.6"
|
||||
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-5.0.6.tgz#01eac28867aeffaebaee7eaf21d1dd3a67d7c0c7"
|
||||
integrity sha512-I+NmIfBHUl+r2wcDd6JwE9yWje/PIVY/R5/CmV8dXLZd5K+L9X2klAOwfAHNnondLXkbHyTAleQAWonpTJBTtw==
|
||||
|
||||
isexe@^2.0.0:
|
||||
version "2.0.0"
|
||||
@@ -1506,14 +1562,13 @@ jackspeak@^3.1.2:
|
||||
"@pkgjs/parseargs" "^0.11.0"
|
||||
|
||||
jake@^10.8.5:
|
||||
version "10.9.1"
|
||||
resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.1.tgz#8dc96b7fcc41cb19aa502af506da4e1d56f5e62b"
|
||||
integrity sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==
|
||||
version "10.9.4"
|
||||
resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.4.tgz#d626da108c63d5cfb00ab5c25fadc7e0084af8e6"
|
||||
integrity sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==
|
||||
dependencies:
|
||||
async "^3.2.3"
|
||||
chalk "^4.0.2"
|
||||
async "^3.2.6"
|
||||
filelist "^1.0.4"
|
||||
minimatch "^3.1.2"
|
||||
picocolors "^1.1.1"
|
||||
|
||||
js-yaml@^4.1.0:
|
||||
version "4.1.0"
|
||||
@@ -1522,11 +1577,6 @@ js-yaml@^4.1.0:
|
||||
dependencies:
|
||||
argparse "^2.0.1"
|
||||
|
||||
jsbn@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
|
||||
integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==
|
||||
|
||||
json-buffer@3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
|
||||
@@ -1555,9 +1605,9 @@ jsonfile@^4.0.0:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
jsonfile@^6.0.1:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
|
||||
integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.2.0.tgz#7c265bd1b65de6977478300087c99f1c84383f62"
|
||||
integrity sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==
|
||||
dependencies:
|
||||
universalify "^2.0.0"
|
||||
optionalDependencies:
|
||||
@@ -1580,11 +1630,11 @@ jsonwebtoken@^9.0.2:
|
||||
semver "^7.5.4"
|
||||
|
||||
jwa@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
|
||||
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.2.tgz#16011ac6db48de7b102777e57897901520eec7b9"
|
||||
integrity sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==
|
||||
dependencies:
|
||||
buffer-equal-constant-time "1.0.1"
|
||||
buffer-equal-constant-time "^1.0.1"
|
||||
ecdsa-sig-formatter "1.0.11"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
@@ -1729,12 +1779,17 @@ matcher@^3.0.0:
|
||||
dependencies:
|
||||
escape-string-regexp "^4.0.0"
|
||||
|
||||
math-intrinsics@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
|
||||
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
|
||||
|
||||
micromatch@^4.0.2:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
|
||||
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
|
||||
integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
|
||||
dependencies:
|
||||
braces "^3.0.2"
|
||||
braces "^3.0.3"
|
||||
picomatch "^2.3.1"
|
||||
|
||||
mime-db@1.52.0:
|
||||
@@ -1770,13 +1825,13 @@ mimic-response@^3.1.0:
|
||||
integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
|
||||
|
||||
minimatch@^10.0.0:
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b"
|
||||
integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==
|
||||
version "10.1.1"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.1.1.tgz#e6e61b9b0c1dcab116b5a7d1458e8b6ae9e73a55"
|
||||
integrity sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==
|
||||
dependencies:
|
||||
brace-expansion "^2.0.1"
|
||||
"@isaacs/brace-expansion" "^5.0.0"
|
||||
|
||||
minimatch@^3.0.3, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
||||
minimatch@^3.0.3, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||
@@ -1871,11 +1926,6 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
|
||||
ms@2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
ms@^2.0.0, ms@^2.1.1, ms@^2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
@@ -1892,9 +1942,9 @@ nice-try@^1.0.4:
|
||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||
|
||||
node-abi@^3.45.0:
|
||||
version "3.71.0"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.71.0.tgz#52d84bbcd8575efb71468fbaa1f9a49b2c242038"
|
||||
integrity sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==
|
||||
version "3.80.0"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.80.0.tgz#d7390951f27caa129cceeec01e1c20fc9f07581c"
|
||||
integrity sha512-LyPuZJcI9HVwzXK1GPxWNzrr+vr8Hp/3UqlmWxxh8p54U1ZbclOqbSog9lWHaCX+dBaiGi6n/hIX+mKu74GmPA==
|
||||
dependencies:
|
||||
semver "^7.3.5"
|
||||
|
||||
@@ -1904,9 +1954,9 @@ node-addon-api@^1.6.3:
|
||||
integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==
|
||||
|
||||
node-api-version@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/node-api-version/-/node-api-version-0.2.0.tgz#5177441da2b1046a4d4547ab9e0972eed7b1ac1d"
|
||||
integrity sha512-fthTTsi8CxaBXMaBAD7ST2uylwvsnYxh2PfaScwpMhos6KlSFajXQPcM4ogNE1q2s3Lbz9GCGqeIHC+C6OZnKg==
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/node-api-version/-/node-api-version-0.2.1.tgz#19bad54f6d65628cbee4e607a325e4488ace2de9"
|
||||
integrity sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==
|
||||
dependencies:
|
||||
semver "^7.3.5"
|
||||
|
||||
@@ -2081,6 +2131,11 @@ pend@~1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
||||
integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
|
||||
|
||||
picocolors@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
||||
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
||||
|
||||
picomatch@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||
@@ -2119,9 +2174,9 @@ promise-retry@^2.0.1:
|
||||
retry "^0.12.0"
|
||||
|
||||
pump@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
|
||||
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.3.tgz#151d979f1a29668dc0025ec589a455b53282268d"
|
||||
integrity sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==
|
||||
dependencies:
|
||||
end-of-stream "^1.1.0"
|
||||
once "^1.3.1"
|
||||
@@ -2261,9 +2316,9 @@ sanitize-filename@^1.6.3:
|
||||
truncate-utf8-bytes "^1.0.0"
|
||||
|
||||
sax@^1.2.4:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0"
|
||||
integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==
|
||||
version "1.4.3"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.3.tgz#fcebae3b756cdc8428321805f4b70f16ec0ab5db"
|
||||
integrity sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==
|
||||
|
||||
semver-compare@^1.0.0:
|
||||
version "1.0.0"
|
||||
@@ -2280,15 +2335,10 @@ semver@^6.2.0:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
|
||||
semver@^7.3.2:
|
||||
version "7.6.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.1.tgz#60bfe090bf907a25aa8119a72b9f90ef7ca281b2"
|
||||
integrity sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==
|
||||
|
||||
semver@^7.3.5, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3:
|
||||
version "7.6.3"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
|
||||
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
|
||||
semver@^7.3.2, semver@^7.3.5, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3:
|
||||
version "7.7.3"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946"
|
||||
integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==
|
||||
|
||||
serialize-error@^7.0.1:
|
||||
version "7.0.1"
|
||||
@@ -2372,11 +2422,11 @@ socks-proxy-agent@^7.0.0:
|
||||
socks "^2.6.2"
|
||||
|
||||
socks@^2.6.2:
|
||||
version "2.8.3"
|
||||
resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5"
|
||||
integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==
|
||||
version "2.8.7"
|
||||
resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.7.tgz#e2fb1d9a603add75050a2067db8c381a0b5669ea"
|
||||
integrity sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==
|
||||
dependencies:
|
||||
ip-address "^9.0.5"
|
||||
ip-address "^10.0.1"
|
||||
smart-buffer "^4.2.0"
|
||||
|
||||
source-map-support@^0.5.19:
|
||||
@@ -2392,7 +2442,7 @@ source-map@^0.6.0:
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
sprintf-js@^1.1.2, sprintf-js@^1.1.3:
|
||||
sprintf-js@^1.1.2:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a"
|
||||
integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==
|
||||
@@ -2454,9 +2504,9 @@ string_decoder@~1.1.1:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^7.0.1:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
|
||||
integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba"
|
||||
integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==
|
||||
dependencies:
|
||||
ansi-regex "^6.0.1"
|
||||
|
||||
@@ -2522,9 +2572,9 @@ tmp@^0.0.33:
|
||||
os-tmpdir "~1.0.2"
|
||||
|
||||
tmp@^0.2.0:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae"
|
||||
integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.5.tgz#b06bcd23f0f3c8357b426891726d16015abfd8f8"
|
||||
integrity sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==
|
||||
|
||||
to-regex-range@^5.0.1:
|
||||
version "5.0.1"
|
||||
@@ -2546,14 +2596,19 @@ type-fest@^0.13.1:
|
||||
integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==
|
||||
|
||||
typescript@^5.4.3:
|
||||
version "5.7.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6"
|
||||
integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==
|
||||
version "5.9.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f"
|
||||
integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==
|
||||
|
||||
undici-types@~5.26.4:
|
||||
version "5.26.5"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
|
||||
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
|
||||
undici-types@~6.21.0:
|
||||
version "6.21.0"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb"
|
||||
integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==
|
||||
|
||||
undici-types@~7.16.0:
|
||||
version "7.16.0"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46"
|
||||
integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==
|
||||
|
||||
unique-filename@^2.0.0:
|
||||
version "2.0.1"
|
||||
@@ -2592,9 +2647,9 @@ uri-js@^4.2.2:
|
||||
punycode "^2.1.0"
|
||||
|
||||
utf8-byte-length@^1.0.1:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61"
|
||||
integrity sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz#f9f63910d15536ee2b2d5dd4665389715eac5c1e"
|
||||
integrity sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==
|
||||
|
||||
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
|
||||
@@ -160,4 +160,31 @@ program
|
||||
}
|
||||
});
|
||||
|
||||
program
|
||||
.command('sort')
|
||||
.description('Sort translation files by keys')
|
||||
.action(() => {
|
||||
try {
|
||||
const languages = getAllNonDefaultLanguages();
|
||||
for (const language of languages) {
|
||||
const filePath = `./translations/${language}.json`;
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
const translations = JSON.parse(content);
|
||||
const sortedTranslations = {};
|
||||
Object.keys(translations)
|
||||
.sort()
|
||||
.forEach(key => {
|
||||
// @ts-ignore
|
||||
sortedTranslations[key] = translations[key];
|
||||
});
|
||||
fs.writeFileSync(filePath, JSON.stringify(sortedTranslations, null, 2), 'utf-8');
|
||||
console.log(`Sorted translations for language: ${language}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
console.error('Error during sort:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = { program };
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
require('dotenv').config({ path: '.env.translation' });
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const OpenAI = require('openai');
|
||||
|
||||
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
||||
|
||||
const translationsDir = path.join(__dirname, '../../translations');
|
||||
const enFilePath = path.join(translationsDir, 'en.json');
|
||||
|
||||
const languageNames = {
|
||||
'cs.json': 'Czech',
|
||||
'de.json': 'German',
|
||||
'es.json': 'Spanish',
|
||||
'fr.json': 'French',
|
||||
'it.json': 'Italian',
|
||||
'ja.json': 'Japanese',
|
||||
'pt.json': 'Portuguese',
|
||||
'sk.json': 'Slovak',
|
||||
'zh.json': 'Chinese'
|
||||
};
|
||||
|
||||
// Read source (english)
|
||||
const enTranslations = JSON.parse(fs.readFileSync(enFilePath, 'utf8'));
|
||||
const enKeys = Object.keys(enTranslations);
|
||||
|
||||
// Get all translation files
|
||||
const translationFiles = fs.readdirSync(translationsDir)
|
||||
.filter(file => file.endsWith('.json') && file !== 'en.json')
|
||||
.sort();
|
||||
|
||||
console.log(`Found ${enKeys.length} keys in en.json\n`);
|
||||
console.log('='.repeat(80));
|
||||
|
||||
async function translateMissingIds({file, translations, missingIds}){
|
||||
const languageName = languageNames[file];
|
||||
if (!languageName) {
|
||||
console.log(`No language name mapping for file: ${file}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build object with only missing translations
|
||||
const needed = {};
|
||||
missingIds.forEach(key => {
|
||||
needed[key] = enTranslations[key];
|
||||
});
|
||||
|
||||
// Get all existing translations as style examples
|
||||
const existingTranslations = {};
|
||||
Object.keys(translations).forEach(key => {
|
||||
if (translations[key] && !translations[key].startsWith('***')) {
|
||||
existingTranslations[key] = {
|
||||
en: enTranslations[key],
|
||||
translated: translations[key]
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const prompt = `You are a professional translator for DbGate, a database management application.
|
||||
|
||||
Translate the following English UI strings to ${languageName}.
|
||||
|
||||
IMPORTANT RULES:
|
||||
1. Preserve ALL placeholders exactly as they appear: {plugin}, {columnNumber}, {0}, {1}, etc.
|
||||
2. Maintain technical terminology appropriately for database software
|
||||
3. Match the translation style, tone, and formality of the existing translations shown below
|
||||
4. Keep the same level of brevity or verbosity as the existing translations
|
||||
5. Return ONLY valid JSON - no markdown, no explanations, no code blocks
|
||||
6. Use the same keys as provided
|
||||
|
||||
EXISTING TRANSLATIONS (for style reference):
|
||||
${JSON.stringify(existingTranslations, null, 2)}
|
||||
|
||||
STRINGS TO TRANSLATE:
|
||||
${JSON.stringify(needed, null, 2)}
|
||||
|
||||
Return format: {"key": "translated value", ...}`;
|
||||
|
||||
const response = await client.chat.completions.create({
|
||||
model: 'gpt-5.1',
|
||||
messages: [
|
||||
{ role: 'system', content: 'You are a professional translator specializing in software localization. Match the style and tone of existing translations. Return only valid JSON.' },
|
||||
{ role: 'user', content: prompt }
|
||||
],
|
||||
temperature: 0.2
|
||||
});
|
||||
|
||||
let translatedJson = response.choices[0].message.content.trim();
|
||||
|
||||
// Remove markdown code blocks if present
|
||||
translatedJson = translatedJson.replace(/^```json\n?/, '').replace(/\n?```$/, '');
|
||||
|
||||
return JSON.parse(translatedJson);
|
||||
}
|
||||
|
||||
(async () => {
|
||||
for (const file of translationFiles) {
|
||||
const filePath = path.join(translationsDir, file);
|
||||
const translations = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
|
||||
const missingIds = enKeys.filter(key => !translations.hasOwnProperty(key) || (typeof translations[key] === 'string' && translations[key].startsWith('***')));
|
||||
|
||||
|
||||
console.log(`\n${file.toUpperCase()}`);
|
||||
console.log('-'.repeat(80));
|
||||
|
||||
if (missingIds.length === 0) {
|
||||
console.log('✓ All translations complete!');
|
||||
continue;
|
||||
} else {
|
||||
console.log(`Found ${missingIds.length} untranslated IDs\n`);
|
||||
}
|
||||
|
||||
const newTranslations = await translateMissingIds({file, translations, missingIds});
|
||||
|
||||
if (!newTranslations) {
|
||||
console.log(`Skipping file due to translation error: ${file}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(newTranslations)) {
|
||||
translations[key] = value;
|
||||
console.log(`Translated: ${key} => ${value}`);
|
||||
}
|
||||
|
||||
fs.writeFileSync(filePath, JSON.stringify(translations, null, 2) + '\n', 'utf8');
|
||||
console.log(`\n✓ Updated translations written to ${file}`);
|
||||
}
|
||||
|
||||
console.log('\n' + '='.repeat(80));
|
||||
console.log('Translation complete!\n');
|
||||
})();
|
||||
@@ -4,6 +4,7 @@ const volatilePackages = [
|
||||
'@clickhouse/client',
|
||||
'bson', // this package is already bundled and is used in mongodb
|
||||
'mongodb',
|
||||
'mongodb-old',
|
||||
'mongodb-client-encryption',
|
||||
'tedious',
|
||||
'msnodesqlv8',
|
||||
|
||||
@@ -60,7 +60,7 @@ describe('Data browser data', () => {
|
||||
cy.contains('MyChinook').click();
|
||||
cy.testid('SqlObjectList_search').clear().type('album');
|
||||
cy.contains('Tables (1/11)');
|
||||
cy.contains('347 rows, InnoDB');
|
||||
cy.contains('347 rows, 65.5 KB, InnoDB');
|
||||
cy.testid('SqlObjectList_searchMenuDropDown').click();
|
||||
cy.contains('Column name').click();
|
||||
cy.contains('Tables (2/11)');
|
||||
|
||||
@@ -110,7 +110,7 @@ describe('Charts', () => {
|
||||
cy.themeshot('new-object-window');
|
||||
});
|
||||
|
||||
it.only('Database chat - charts', () => {
|
||||
it('Database chat - charts', () => {
|
||||
cy.contains('MySql-connection').click();
|
||||
cy.contains('MyChinook').click();
|
||||
cy.testid('TabsPanel_buttonNewObject').click();
|
||||
@@ -119,7 +119,9 @@ describe('Charts', () => {
|
||||
cy.get('body').realType('show me chart of most popular genres');
|
||||
cy.get('body').realPress('{enter}');
|
||||
cy.testid('DatabaseChatTab_executeAllQueries', { timeout: 30000 }).click();
|
||||
cy.testid('chart-canvas', { timeout: 30000 }).should($c => expect($c[0].toDataURL()).to.match(/^data:image\/png;base64/));
|
||||
cy.testid('chart-canvas', { timeout: 30000 }).should($c =>
|
||||
expect($c[0].toDataURL()).to.match(/^data:image\/png;base64/)
|
||||
);
|
||||
cy.themeshot('database-chat-chart');
|
||||
});
|
||||
|
||||
@@ -155,15 +157,51 @@ describe('Charts', () => {
|
||||
cy.testid('MessageViewRow-explainErrorButton-1').click();
|
||||
cy.testid('ChatCodeRenderer_useSqlButton', { timeout: 30000 });
|
||||
cy.themeshot('explain-query-error');
|
||||
});
|
||||
|
||||
// cy.testid('TabsPanel_buttonNewObject').click();
|
||||
// cy.testid('NewObjectModal_databaseChat').click();
|
||||
// cy.wait(1000);
|
||||
// cy.get('body').realType('show me chart of most popular genres');
|
||||
// cy.get('body').realPress('{enter}');
|
||||
// cy.testid('DatabaseChatTab_executeAllQueries', { timeout: 30000 }).click();
|
||||
// cy.wait(5000);
|
||||
// cy.testid('chart-canvas').should($c => expect($c[0].toDataURL()).to.match(/^data:image\/png;base64/));
|
||||
// cy.themeshot('database-chat-chart');
|
||||
it('Switch language', () => {
|
||||
cy.contains('MySql-connection').click();
|
||||
cy.contains('MyChinook').click();
|
||||
cy.testid('WidgetIconPanel_settings').click();
|
||||
cy.contains('Settings').click();
|
||||
|
||||
cy.testid('SettingsModal_languageSelect').select('Deutsch');
|
||||
cy.testid('ConfirmModal_okButton').click();
|
||||
cy.testid('WidgetIconPanel_settings').click();
|
||||
cy.contains('Einstellungen').click();
|
||||
cy.contains('Lokalisierung');
|
||||
cy.themeshot('switch-language-de');
|
||||
|
||||
cy.testid('SettingsModal_languageSelect').select('Français');
|
||||
cy.testid('ConfirmModal_okButton').click();
|
||||
cy.testid('WidgetIconPanel_settings').click();
|
||||
cy.contains('Paramètres').click();
|
||||
cy.contains('Localisation');
|
||||
cy.themeshot('switch-language-fr');
|
||||
|
||||
cy.testid('SettingsModal_languageSelect').select('Español');
|
||||
cy.testid('ConfirmModal_okButton').click();
|
||||
cy.testid('WidgetIconPanel_settings').click();
|
||||
cy.contains('Configuración').click();
|
||||
cy.contains('Localización');
|
||||
cy.themeshot('switch-language-es');
|
||||
|
||||
cy.testid('SettingsModal_languageSelect').select('Čeština');
|
||||
cy.testid('ConfirmModal_okButton').click();
|
||||
cy.testid('WidgetIconPanel_settings').click();
|
||||
cy.contains('Nastavení').click();
|
||||
cy.contains('Lokalizace');
|
||||
cy.themeshot('switch-language-cs');
|
||||
|
||||
cy.testid('SettingsModal_languageSelect').select('中文');
|
||||
cy.testid('ConfirmModal_okButton').click();
|
||||
cy.testid('WidgetIconPanel_settings').click();
|
||||
cy.contains('设置').click();
|
||||
cy.contains('本地化');
|
||||
cy.themeshot('switch-language-zh');
|
||||
|
||||
cy.testid('SettingsModal_languageSelect').select('English');
|
||||
cy.testid('ConfirmModal_okButton').click();
|
||||
cy.testid('WidgetIconPanel_settings');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ const {
|
||||
} = require('dbgate-tools');
|
||||
|
||||
function pickImportantTableInfo(engine, table) {
|
||||
if (!table) return table;
|
||||
const props = ['columnName', 'defaultValue'];
|
||||
if (!engine.skipNullability) props.push('notNull');
|
||||
if (!engine.skipAutoIncrement) props.push('autoIncrement');
|
||||
@@ -25,6 +26,13 @@ function pickImportantTableInfo(engine, table) {
|
||||
.map(props =>
|
||||
_.omitBy(props, (v, k) => k == 'defaultValue' && v == 'NULL' && engine.setNullDefaultInsteadOfDrop)
|
||||
),
|
||||
// foreignKeys: table.foreignKeys
|
||||
// .sort((a, b) => a.refTableName.localeCompare(b.refTableName))
|
||||
// .map(fk => ({
|
||||
// constraintType: fk.constraintType,
|
||||
// refTableName: fk.refTableName,
|
||||
// columns: fk.columns.map(col => ({ columnName: col.columnName, refColumnName: col.refColumnName })),
|
||||
// })),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -33,7 +41,7 @@ function checkTableStructure(engine, t1, t2) {
|
||||
expect(pickImportantTableInfo(engine, t1)).toEqual(pickImportantTableInfo(engine, t2));
|
||||
}
|
||||
|
||||
async function testTableDiff(engine, conn, driver, mangle) {
|
||||
async function testTableDiff(engine, conn, driver, mangle, changedTable = 't1') {
|
||||
const initQuery = formatQueryWithoutParams(driver, `create table ~t0 (~id int not null primary key)`);
|
||||
await driver.query(conn, transformSqlForEngine(engine, initQuery));
|
||||
|
||||
@@ -68,17 +76,38 @@ async function testTableDiff(engine, conn, driver, mangle) {
|
||||
await driver.query(conn, transformSqlForEngine(engine, query));
|
||||
}
|
||||
|
||||
const tget = x => x.tables.find(y => y.pureName == 't1');
|
||||
const structure1 = generateDbPairingId(extendDatabaseInfo(await driver.analyseFull(conn)));
|
||||
if (!engine.skipReferences) {
|
||||
const query = formatQueryWithoutParams(
|
||||
driver,
|
||||
`create table ~t3 (~id int not null primary key, ~fkval int ${
|
||||
driver.dialect.implicitNullDeclaration ? '' : 'null'
|
||||
})`
|
||||
);
|
||||
|
||||
await driver.query(conn, transformSqlForEngine(engine, query));
|
||||
}
|
||||
|
||||
const tget = x => x?.tables?.find(y => y.pureName == changedTable);
|
||||
const structure1Source = await driver.analyseFull(conn);
|
||||
const structure1 = generateDbPairingId(extendDatabaseInfo(structure1Source));
|
||||
let structure2 = _.cloneDeep(structure1);
|
||||
mangle(tget(structure2));
|
||||
structure2 = extendDatabaseInfo(structure2);
|
||||
|
||||
const { sql } = getAlterTableScript(tget(structure1), tget(structure2), {}, structure1, structure2, driver);
|
||||
|
||||
// sleep 1s - some engines have update datetime precision only to seconds
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
console.log('RUNNING ALTER SQL', driver.engine, ':', sql);
|
||||
|
||||
await driver.script(conn, sql);
|
||||
|
||||
// if (!engine.skipIncrementalAnalysis) {
|
||||
// const structure2RealIncremental = await driver.analyseIncremental(conn, structure1Source);
|
||||
// checkTableStructure(engine, tget(structure2RealIncremental), tget(structure2));
|
||||
// }
|
||||
|
||||
const structure2Real = extendDatabaseInfo(await driver.analyseFull(conn));
|
||||
|
||||
checkTableStructure(engine, tget(structure2Real), tget(structure2));
|
||||
@@ -214,6 +243,48 @@ describe('Alter table', () => {
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.filter(x => !x.skipReferences).map(engine => [engine.label, engine]))(
|
||||
'Drop FK - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testTableDiff(
|
||||
engine,
|
||||
conn,
|
||||
driver,
|
||||
tbl => {
|
||||
tbl.foreignKeys = [];
|
||||
},
|
||||
't2'
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.filter(x => !x.skipReferences).map(engine => [engine.label, engine]))(
|
||||
'Create FK - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testTableDiff(
|
||||
engine,
|
||||
conn,
|
||||
driver,
|
||||
tbl => {
|
||||
tbl.foreignKeys = [
|
||||
{
|
||||
constraintType: 'foreignKey',
|
||||
pureName: 't3',
|
||||
refTableName: 't1',
|
||||
columns: [
|
||||
{
|
||||
columnName: 'fkval',
|
||||
refColumnName: 'col_ref',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
't3'
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
// test.each(engines.map(engine => [engine.label, engine]))(
|
||||
// 'Change autoincrement - %s',
|
||||
// testWrapper(async (conn, driver, engine) => {
|
||||
|
||||
@@ -49,6 +49,32 @@ class StreamHandler {
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryTestStreamHandler {
|
||||
constructor(resolve, reject, expectedValue) {
|
||||
this.resolve = resolve;
|
||||
this.reject = reject;
|
||||
this.expectedValue = expectedValue;
|
||||
this.rowsReceived = [];
|
||||
}
|
||||
row(row) {
|
||||
try {
|
||||
this.rowsReceived.push(row);
|
||||
if (this.expectedValue) {
|
||||
expect(row).toEqual(this.expectedValue);
|
||||
}
|
||||
} catch (error) {
|
||||
this.reject(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
recordset(columns) {}
|
||||
done(result) {
|
||||
this.resolve(this.rowsReceived);
|
||||
}
|
||||
info(msg) {}
|
||||
}
|
||||
|
||||
|
||||
function executeStreamItem(driver, conn, sql) {
|
||||
return new Promise(resolve => {
|
||||
const handler = new StreamHandler(resolve);
|
||||
@@ -223,4 +249,51 @@ describe('Query', () => {
|
||||
expect(row[keys[0]] == 1).toBeTruthy();
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.filter(x => x.binaryDataType).map(engine => [engine.label, engine]))(
|
||||
'Binary - %s',
|
||||
testWrapper(async (dbhan, driver, engine) => {
|
||||
await runCommandOnDriver(dbhan, driver, dmp =>
|
||||
dmp.createTable({
|
||||
pureName: 't1',
|
||||
columns: [
|
||||
{ columnName: 'id', dataType: 'int', notNull: true, autoIncrement: true },
|
||||
{ columnName: 'val', dataType: engine.binaryDataType },
|
||||
],
|
||||
primaryKey: {
|
||||
columns: [{ columnName: 'id' }],
|
||||
},
|
||||
})
|
||||
);
|
||||
const structure = await driver.analyseFull(dbhan);
|
||||
const table = structure.tables.find(x => x.pureName == 't1');
|
||||
|
||||
const dmp = driver.createDumper();
|
||||
dmp.putCmd("INSERT INTO ~t1 (~val) VALUES (%v)", {
|
||||
$binary: { base64: 'iVBORw0KWgo=' },
|
||||
});
|
||||
await driver.query(dbhan, dmp.s, {discardResult: true});
|
||||
|
||||
const dmp2 = driver.createDumper();
|
||||
dmp2.put('SELECT ~val FROM ~t1');
|
||||
const res = await driver.query(dbhan, dmp2.s);
|
||||
|
||||
const row = res.rows[0];
|
||||
const keys = Object.keys(row);
|
||||
expect(keys.length).toEqual(1);
|
||||
expect(row[keys[0]]).toEqual({$binary: {base64: 'iVBORw0KWgo='}});
|
||||
|
||||
const res2 = await driver.readQuery(dbhan, dmp2.s);
|
||||
const rows = await Array.fromAsync(res2);
|
||||
const rowsVal = rows.filter(r => r.val != null);
|
||||
|
||||
expect(rowsVal.length).toEqual(1);
|
||||
expect(rowsVal[0].val).toEqual({$binary: {base64: 'iVBORw0KWgo='}});
|
||||
|
||||
const res3 = await new Promise((resolve, reject) => {
|
||||
const handler = new BinaryTestStreamHandler(resolve, reject, {val: {$binary: {base64: 'iVBORw0KWgo='}}});
|
||||
driver.stream(dbhan, dmp2.s, handler);
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -44,6 +44,7 @@ const mysqlEngine = {
|
||||
supportRenameSqlObject: false,
|
||||
dbSnapshotBySeconds: true,
|
||||
dumpFile: 'data/chinook-mysql.sql',
|
||||
binaryDataType: 'blob',
|
||||
dumpChecks: [
|
||||
{
|
||||
sql: 'select count(*) as res from genre',
|
||||
@@ -186,6 +187,7 @@ const mariaDbEngine = {
|
||||
/** @type {import('dbgate-types').TestEngineInfo} */
|
||||
const postgreSqlEngine = {
|
||||
label: 'PostgreSQL',
|
||||
skipIncrementalAnalysis: true,
|
||||
connection: {
|
||||
engine: 'postgres@dbgate-plugin-postgres',
|
||||
password: 'Pwd2020Db',
|
||||
@@ -216,6 +218,7 @@ const postgreSqlEngine = {
|
||||
supportSchemas: true,
|
||||
supportRenameSqlObject: true,
|
||||
defaultSchemaName: 'public',
|
||||
binaryDataType: 'bytea',
|
||||
dumpFile: 'data/chinook-postgre.sql',
|
||||
dumpChecks: [
|
||||
{
|
||||
@@ -446,6 +449,7 @@ const sqlServerEngine = {
|
||||
supportTableComments: true,
|
||||
supportColumnComments: true,
|
||||
// skipSeparateSchemas: true,
|
||||
binaryDataType: 'varbinary(100)',
|
||||
triggers: [
|
||||
{
|
||||
testName: 'triggers before each row',
|
||||
@@ -506,6 +510,7 @@ const sqliteEngine = {
|
||||
},
|
||||
},
|
||||
],
|
||||
binaryDataType: 'blob',
|
||||
};
|
||||
|
||||
const libsqlFileEngine = {
|
||||
@@ -619,6 +624,7 @@ const oracleEngine = {
|
||||
},
|
||||
},
|
||||
],
|
||||
binaryDataType: 'blob',
|
||||
};
|
||||
|
||||
/** @type {import('dbgate-types').TestEngineInfo} */
|
||||
@@ -754,16 +760,16 @@ const enginesOnLocal = [
|
||||
// cassandraEngine,
|
||||
// mysqlEngine,
|
||||
// mariaDbEngine,
|
||||
// postgreSqlEngine,
|
||||
// sqlServerEngine,
|
||||
postgreSqlEngine,
|
||||
//sqlServerEngine,
|
||||
// sqliteEngine,
|
||||
// cockroachDbEngine,
|
||||
// clickhouseEngine,
|
||||
// libsqlFileEngine,
|
||||
// libsqlWsEngine,
|
||||
// oracleEngine,
|
||||
//oracleEngine,
|
||||
// duckdbEngine,
|
||||
firebirdEngine,
|
||||
//firebirdEngine,
|
||||
];
|
||||
|
||||
/** @type {import('dbgate-types').TestEngineInfo[] & Record<string, import('dbgate-types').TestEngineInfo>} */
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
module.exports = {
|
||||
setupFilesAfterEnv: ['<rootDir>/setupTests.js'],
|
||||
reporters: ['default', 'github-actions'],
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "^7.0.3",
|
||||
"jest": "^27.0.1",
|
||||
"jest": "^28.1.3",
|
||||
"pino-pretty": "^11.2.2",
|
||||
"tmp": "^0.2.3"
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "6.6.9",
|
||||
"version": "6.7.2-alpha.1",
|
||||
"name": "dbgate-all",
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
@@ -74,6 +74,7 @@
|
||||
"translations:add-missing": "node common/translations-cli/index.js add-missing",
|
||||
"translations:remove-unused": "node common/translations-cli/index.js remove-unused",
|
||||
"translations:check": "node common/translations-cli/index.js check",
|
||||
"translations:sort": "node common/translations-cli/index.js sort",
|
||||
"errors": "node common/assign-dbgm-codes.mjs ."
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ DEVMODE=1
|
||||
SHELL_SCRIPTING=1
|
||||
ALLOW_DBGATE_PRIVATE_CLOUD=1
|
||||
DEVWEB=1
|
||||
LOCAL_AUTH_PROXY=1
|
||||
# LOCAL_AUTH_PROXY=1
|
||||
# LOCAL_AI_GATEWAY=true
|
||||
|
||||
# REDIRECT_TO_DBGATE_CLOUD_LOGIN=1
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"cors": "^2.8.5",
|
||||
"cross-env": "^6.0.3",
|
||||
"dbgate-datalib": "^6.0.0-alpha.1",
|
||||
"dbgate-query-splitter": "^4.11.7",
|
||||
"dbgate-query-splitter": "^4.11.9",
|
||||
"dbgate-sqltree": "^6.0.0-alpha.1",
|
||||
"dbgate-tools": "^6.0.0-alpha.1",
|
||||
"debug": "^4.3.4",
|
||||
|
||||
@@ -35,8 +35,8 @@ module.exports = {
|
||||
},
|
||||
|
||||
refreshPublicFiles_meta: true,
|
||||
async refreshPublicFiles({ isRefresh }) {
|
||||
await refreshPublicFiles(isRefresh);
|
||||
async refreshPublicFiles({ isRefresh }, req) {
|
||||
await refreshPublicFiles(isRefresh, req?.headers?.['x-ui-language']);
|
||||
return {
|
||||
status: 'ok',
|
||||
};
|
||||
|
||||
@@ -71,6 +71,7 @@ module.exports = {
|
||||
const isLicenseValid = checkedLicense?.status == 'ok';
|
||||
const logoutUrl = storageConnectionError ? null : await authProvider.getLogoutUrl();
|
||||
const adminConfig = storageConnectionError ? null : await storage.readConfig({ group: 'admin' });
|
||||
const settingsConfig = storageConnectionError ? null : await storage.readConfig({ group: 'settings' });
|
||||
|
||||
storage.startRefreshLicense();
|
||||
|
||||
@@ -121,6 +122,7 @@ module.exports = {
|
||||
allowPrivateCloud: platformInfo.isElectron || !!process.env.ALLOW_DBGATE_PRIVATE_CLOUD,
|
||||
...currentVersion,
|
||||
redirectToDbGateCloudLogin: !!process.env.REDIRECT_TO_DBGATE_CLOUD_LOGIN,
|
||||
preferrendLanguage: settingsConfig?.['storage.language'] || process.env.LANGUAGE || null,
|
||||
};
|
||||
|
||||
return configResult;
|
||||
|
||||
@@ -14,7 +14,11 @@ const JsonLinesDatabase = require('../utility/JsonLinesDatabase');
|
||||
const processArgs = require('../utility/processArgs');
|
||||
const { safeJsonParse, getLogger, extractErrorLogData } = require('dbgate-tools');
|
||||
const platformInfo = require('../utility/platformInfo');
|
||||
const { connectionHasPermission, testConnectionPermission, loadPermissionsFromRequest } = require('../utility/hasPermission');
|
||||
const {
|
||||
connectionHasPermission,
|
||||
testConnectionPermission,
|
||||
loadPermissionsFromRequest,
|
||||
} = require('../utility/hasPermission');
|
||||
const pipeForkLogs = require('../utility/pipeForkLogs');
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const { getAuthProviderById } = require('../auth/authProvider');
|
||||
@@ -116,7 +120,10 @@ function getPortalCollections() {
|
||||
}
|
||||
}
|
||||
|
||||
logger.info({ connections: connections.map(pickSafeConnectionInfo) }, 'DBGM-00005 Using connections from ENV variables');
|
||||
logger.info(
|
||||
{ connections: connections.map(pickSafeConnectionInfo) },
|
||||
'DBGM-00005 Using connections from ENV variables'
|
||||
);
|
||||
const noengine = connections.filter(x => !x.engine);
|
||||
if (noengine.length > 0) {
|
||||
logger.warn(
|
||||
@@ -502,7 +509,11 @@ module.exports = {
|
||||
state,
|
||||
client: 'web',
|
||||
});
|
||||
res.redirect(authResp.url);
|
||||
if (authResp?.url) {
|
||||
res.redirect(authResp.url);
|
||||
return;
|
||||
}
|
||||
res.json({ error: 'No URL returned from auth provider' });
|
||||
},
|
||||
|
||||
dbloginApp_meta: true,
|
||||
|
||||
@@ -1533,6 +1533,12 @@ module.exports = {
|
||||
"columnName": "name",
|
||||
"dataType": "varchar(250)",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"pureName": "team_file_types",
|
||||
"columnName": "format",
|
||||
"dataType": "varchar(50)",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"foreignKeys": [],
|
||||
@@ -1549,7 +1555,38 @@ module.exports = {
|
||||
"preloadedRows": [
|
||||
{
|
||||
"id": -1,
|
||||
"name": "sql"
|
||||
"name": "sql",
|
||||
"format": "text"
|
||||
},
|
||||
{
|
||||
"id": -2,
|
||||
"name": "diagrams",
|
||||
"format": "json"
|
||||
},
|
||||
{
|
||||
"id": -3,
|
||||
"name": "query",
|
||||
"format": "json"
|
||||
},
|
||||
{
|
||||
"id": -4,
|
||||
"name": "perspectives",
|
||||
"format": "json"
|
||||
},
|
||||
{
|
||||
"id": -5,
|
||||
"name": "impexp",
|
||||
"format": "json"
|
||||
},
|
||||
{
|
||||
"id": -6,
|
||||
"name": "shell",
|
||||
"format": "text"
|
||||
},
|
||||
{
|
||||
"id": -7,
|
||||
"name": "dbcompare",
|
||||
"format": "json"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -193,7 +193,7 @@ async function getCloudSigninHeaders(holder = null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
async function updateCloudFiles(isRefresh) {
|
||||
async function updateCloudFiles(isRefresh, language) {
|
||||
let lastCloudFilesTags;
|
||||
try {
|
||||
lastCloudFilesTags = await fs.readFile(path.join(datadir(), 'cloud-files-tags.txt'), 'utf-8');
|
||||
@@ -218,6 +218,7 @@ async function updateCloudFiles(isRefresh) {
|
||||
...getLicenseHttpHeaders(),
|
||||
...(await getCloudInstanceHeaders()),
|
||||
'x-app-version': currentVersion.version,
|
||||
'x-app-language': language || 'en',
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -274,7 +275,7 @@ async function ensurePromoWidgetDataLoaded() {
|
||||
promoWidgetDataLoaded = true;
|
||||
}
|
||||
|
||||
async function updatePremiumPromoWidget() {
|
||||
async function updatePremiumPromoWidget(language) {
|
||||
await ensurePromoWidgetDataLoaded();
|
||||
|
||||
const tags = (await collectCloudFilesSearchTags()).join(',');
|
||||
@@ -283,8 +284,10 @@ async function updatePremiumPromoWidget() {
|
||||
`${DBGATE_CLOUD_URL}/premium-promo-widget?identifier=${promoWidgetData?.identifier ?? 'empty'}&tags=${tags}`,
|
||||
{
|
||||
headers: {
|
||||
...getLicenseHttpHeaders(),
|
||||
...(await getCloudInstanceHeaders()),
|
||||
'x-app-version': currentVersion.version,
|
||||
'x-app-language': language || 'en',
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -299,17 +302,21 @@ async function updatePremiumPromoWidget() {
|
||||
socket.emitChanged(`promo-widget-changed`);
|
||||
}
|
||||
|
||||
async function refreshPublicFiles(isRefresh) {
|
||||
async function refreshPublicFiles(isRefresh, uiLanguage) {
|
||||
const language = platformInfo.isElectron
|
||||
? (await config.getCachedSettings())?.['localization.language'] || 'en'
|
||||
: uiLanguage;
|
||||
if (!cloudFiles) {
|
||||
await loadCloudFiles();
|
||||
}
|
||||
try {
|
||||
await updateCloudFiles(isRefresh);
|
||||
await updateCloudFiles(isRefresh, language);
|
||||
} catch (err) {
|
||||
logger.error(extractErrorLogData(err), 'DBGM-00166 Error updating cloud files');
|
||||
}
|
||||
if (!isProApp()) {
|
||||
await updatePremiumPromoWidget();
|
||||
const configSettings = await config.get();
|
||||
if (!isProApp() || configSettings?.trialDaysLeft != null) {
|
||||
await updatePremiumPromoWidget(language);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class QueryStreamTableWriter {
|
||||
this.started = new Date().getTime();
|
||||
}
|
||||
|
||||
initializeFromQuery(structure, resultIndex, chartDefinition, autoDetectCharts = false) {
|
||||
initializeFromQuery(structure, resultIndex, chartDefinition, autoDetectCharts = false, options = {}) {
|
||||
this.jslid = crypto.randomUUID();
|
||||
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
|
||||
fs.writeFileSync(
|
||||
@@ -25,6 +25,7 @@ class QueryStreamTableWriter {
|
||||
JSON.stringify({
|
||||
...structure,
|
||||
__isStreamHeader: true,
|
||||
...options
|
||||
}) + '\n'
|
||||
);
|
||||
this.currentStream = fs.createWriteStream(this.currentFile, { flags: 'a' });
|
||||
@@ -179,7 +180,7 @@ class StreamHandler {
|
||||
process.send({ msgtype: 'changedCurrentDatabase', database, sesid: this.sesid });
|
||||
}
|
||||
|
||||
recordset(columns) {
|
||||
recordset(columns, options) {
|
||||
if (this.rowsLimitOverflow) {
|
||||
return;
|
||||
}
|
||||
@@ -189,7 +190,8 @@ class StreamHandler {
|
||||
Array.isArray(columns) ? { columns } : columns,
|
||||
this.queryStreamInfoHolder.resultIndex,
|
||||
this.frontMatter?.[`chart-${this.queryStreamInfoHolder.resultIndex + 1}`],
|
||||
this.autoDetectCharts
|
||||
this.autoDetectCharts,
|
||||
options
|
||||
);
|
||||
this.queryStreamInfoHolder.resultIndex += 1;
|
||||
this.rowCounter = 0;
|
||||
|
||||
@@ -2,4 +2,5 @@ module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
moduleFileExtensions: ['js'],
|
||||
reporters: ['default', 'github-actions'],
|
||||
};
|
||||
|
||||
@@ -35,6 +35,12 @@ program
|
||||
.option('-u, --user <user>', 'user name')
|
||||
.option('-p, --password <password>', 'password')
|
||||
.option('-d, --database <database>', 'database name')
|
||||
.option('--url <url>', 'database url')
|
||||
.option('--file <file>', 'database file')
|
||||
.option('--socket-path <socketPath>', 'socket path')
|
||||
.option('--service-name <serviceName>', 'service name (for Oracle)')
|
||||
.option('--auth-type <authType>', 'authentication type')
|
||||
.option('--use-ssl', 'use SSL connection')
|
||||
.option('--auto-index-foreign-keys', 'automatically adds indexes to all foreign keys')
|
||||
.option(
|
||||
'--load-data-condition <condition>',
|
||||
@@ -48,7 +54,7 @@ program
|
||||
.command('deploy <modelFolder>')
|
||||
.description('Deploys model to database')
|
||||
.action(modelFolder => {
|
||||
const { engine, server, user, password, database, transaction } = program.opts();
|
||||
const { engine, server, user, password, database, url, file, transaction } = program.opts();
|
||||
// const hooks = [];
|
||||
// if (program.autoIndexForeignKeys) hooks.push(dbmodel.hooks.autoIndexForeignKeys);
|
||||
|
||||
@@ -60,6 +66,13 @@ program
|
||||
user,
|
||||
password,
|
||||
database,
|
||||
databaseUrl: url,
|
||||
useDatabaseUrl: !!url,
|
||||
databaseFile: file,
|
||||
socketPath: program.socketPath,
|
||||
serviceName: program.serviceName,
|
||||
authType: program.authType,
|
||||
useSsl: program.useSsl,
|
||||
},
|
||||
modelFolder,
|
||||
useTransaction: transaction,
|
||||
|
||||
@@ -2,4 +2,5 @@ module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
moduleFileExtensions: ['js'],
|
||||
reporters: ['default', 'github-actions'],
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { arrayToHexString, evalFilterBehaviour, isTypeDateTime } from 'dbgate-tools';
|
||||
import { arrayToHexString, base64ToHex, evalFilterBehaviour, isTypeDateTime } from 'dbgate-tools';
|
||||
import { format, toDate } from 'date-fns';
|
||||
import _isString from 'lodash/isString';
|
||||
import _cloneDeepWith from 'lodash/cloneDeepWith';
|
||||
@@ -24,7 +24,9 @@ export function getFilterValueExpression(value, dataType?) {
|
||||
if (value.type == 'Buffer' && Array.isArray(value.data)) {
|
||||
return '0x' + arrayToHexString(value.data);
|
||||
}
|
||||
|
||||
if (value?.$binary?.base64) {
|
||||
return base64ToHex(value.$binary.base64);
|
||||
}
|
||||
return `="${value}"`;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import P from 'parsimmon';
|
||||
import moment from 'moment';
|
||||
import { Condition } from 'dbgate-sqltree';
|
||||
import { interpretEscapes, token, word, whitespace } from './common';
|
||||
import { hexStringToArray, parseNumberSafe } from 'dbgate-tools';
|
||||
import { hexToBase64, parseNumberSafe } from 'dbgate-tools';
|
||||
import { FilterBehaviour, TransformType } from 'dbgate-types';
|
||||
|
||||
const binaryCondition =
|
||||
@@ -385,10 +385,7 @@ const createParser = (filterBehaviour: FilterBehaviour) => {
|
||||
|
||||
hexstring: () =>
|
||||
token(P.regexp(/0x(([0-9a-fA-F][0-9a-fA-F])+)/, 1))
|
||||
.map(x => ({
|
||||
type: 'Buffer',
|
||||
data: hexStringToArray(x),
|
||||
}))
|
||||
.map(x => ({ $binary: { base64: hexToBase64(x) } }))
|
||||
.desc('hex string'),
|
||||
|
||||
noQuotedString: () => P.regexp(/[^\s^,^'^"]+/).desc('string unquoted'),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { SqlDumper } from 'dbgate-types';
|
||||
import { Command, Select, Update, Delete, Insert } from './types';
|
||||
import { dumpSqlExpression } from './dumpSqlExpression';
|
||||
import { dumpSqlFromDefinition, dumpSqlSourceRef } from './dumpSqlSource';
|
||||
import { dumpSqlFromDefinition, dumpSqlSourceDef, dumpSqlSourceRef } from './dumpSqlSource';
|
||||
import { dumpSqlCondition } from './dumpSqlCondition';
|
||||
|
||||
export function dumpSqlSelect(dmp: SqlDumper, cmd: Select) {
|
||||
@@ -115,7 +115,10 @@ export function dumpSqlInsert(dmp: SqlDumper, cmd: Insert) {
|
||||
cmd.fields.map(x => x.targetColumn)
|
||||
);
|
||||
dmp.putCollection(',', cmd.fields, x => dumpSqlExpression(dmp, x));
|
||||
if (dmp.dialect.requireFromDual) {
|
||||
if (cmd.whereNotExistsSource) {
|
||||
dmp.put(' ^from ');
|
||||
dumpSqlSourceDef(dmp, cmd.whereNotExistsSource);
|
||||
} else if (dmp.dialect.requireFromDual) {
|
||||
dmp.put(' ^from ^dual ');
|
||||
}
|
||||
dmp.put(' ^where ^not ^exists (^select * ^from %f ^where ', cmd.targetTable);
|
||||
|
||||
@@ -2,6 +2,7 @@ import _ from 'lodash';
|
||||
import type { SqlDumper } from 'dbgate-types';
|
||||
import { Expression, ColumnRefExpression } from './types';
|
||||
import { dumpSqlSourceRef } from './dumpSqlSource';
|
||||
import { dumpSqlSelect } from './dumpSqlCommand';
|
||||
|
||||
export function dumpSqlExpression(dmp: SqlDumper, expr: Expression) {
|
||||
switch (expr.exprType) {
|
||||
@@ -67,5 +68,11 @@ export function dumpSqlExpression(dmp: SqlDumper, expr: Expression) {
|
||||
});
|
||||
dmp.put(')');
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
dmp.put('(');
|
||||
dumpSqlSelect(dmp, expr.select);
|
||||
dmp.put(')');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ export interface Insert {
|
||||
fields: UpdateField[];
|
||||
targetTable: NamedObjectInfo;
|
||||
insertWhereNotExistsCondition?: Condition;
|
||||
whereNotExistsSource?: Source;
|
||||
}
|
||||
|
||||
export interface AllowIdentityInsert {
|
||||
@@ -226,6 +227,11 @@ export interface RowNumberExpression {
|
||||
orderBy: OrderByExpression[];
|
||||
}
|
||||
|
||||
export interface SelectExpression {
|
||||
exprType: 'select';
|
||||
select: Select;
|
||||
}
|
||||
|
||||
export type Expression =
|
||||
| ColumnRefExpression
|
||||
| ValueExpression
|
||||
@@ -235,7 +241,8 @@ export type Expression =
|
||||
| CallExpression
|
||||
| MethodCallExpression
|
||||
| TranformExpression
|
||||
| RowNumberExpression;
|
||||
| RowNumberExpression
|
||||
| SelectExpression;
|
||||
export type OrderByExpression = Expression & { direction: 'ASC' | 'DESC' };
|
||||
|
||||
export type ResultField = Expression & { alias?: string };
|
||||
|
||||
@@ -2,4 +2,5 @@ module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
moduleFileExtensions: ['js'],
|
||||
reporters: ['default', 'github-actions'],
|
||||
};
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"blueimp-md5": "^2.19.0",
|
||||
"dbgate-query-splitter": "^4.11.7",
|
||||
"dbgate-query-splitter": "^4.11.9",
|
||||
"dbgate-sqltree": "^6.0.0-alpha.1",
|
||||
"debug": "^4.3.4",
|
||||
"json-stable-stringify": "^1.0.1",
|
||||
|
||||
@@ -78,6 +78,14 @@ export class SqlDumper implements AlterProcessor {
|
||||
else if (_isNumber(value)) this.putRaw(value.toString());
|
||||
else if (_isDate(value)) this.putStringValue(new Date(value).toISOString());
|
||||
else if (value?.type == 'Buffer' && _isArray(value?.data)) this.putByteArrayValue(value?.data);
|
||||
else if (value?.$binary?.base64) {
|
||||
const binary = atob(value.$binary.base64);
|
||||
const bytes = new Array(binary.length);
|
||||
for (let i = 0; i < binary.length; i++) {
|
||||
bytes[i] = binary.charCodeAt(i);
|
||||
}
|
||||
this.putByteArrayValue(bytes);
|
||||
}
|
||||
else if (value?.$bigint) this.putRaw(value?.$bigint);
|
||||
else if (_isPlainObject(value) || _isArray(value)) this.putStringValue(JSON.stringify(value));
|
||||
else this.put('^null');
|
||||
|
||||
@@ -47,6 +47,7 @@ export const mongoFilterBehaviour: FilterBehaviour = {
|
||||
allowStringToken: true,
|
||||
allowNumberDualTesting: true,
|
||||
allowObjectIdTesting: true,
|
||||
allowHexString: true,
|
||||
};
|
||||
|
||||
export const evalFilterBehaviour: FilterBehaviour = {
|
||||
|
||||
@@ -43,6 +43,19 @@ export function hexStringToArray(inputString) {
|
||||
return res;
|
||||
}
|
||||
|
||||
export function base64ToHex(base64String) {
|
||||
const binaryString = atob(base64String);
|
||||
const hexString = Array.from(binaryString, c =>
|
||||
c.charCodeAt(0).toString(16).padStart(2, '0')
|
||||
).join('');
|
||||
return '0x' + hexString.toUpperCase();
|
||||
};
|
||||
|
||||
export function hexToBase64(hexString) {
|
||||
const binaryString = hexString.match(/.{1,2}/g).map(byte => String.fromCharCode(parseInt(byte, 16))).join('');
|
||||
return btoa(binaryString);
|
||||
}
|
||||
|
||||
export function parseCellValue(value, editorTypes?: DataEditorTypesBehaviour) {
|
||||
if (!_isString(value)) return value;
|
||||
|
||||
@@ -54,9 +67,10 @@ export function parseCellValue(value, editorTypes?: DataEditorTypesBehaviour) {
|
||||
const mHex = value.match(/^0x([0-9a-fA-F][0-9a-fA-F])+$/);
|
||||
if (mHex) {
|
||||
return {
|
||||
type: 'Buffer',
|
||||
data: hexStringToArray(value.substring(2)),
|
||||
};
|
||||
$binary: {
|
||||
base64: hexToBase64(value.substring(2))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,11 +244,19 @@ export function stringifyCellValue(
|
||||
if (value === true) return { value: 'true', gridStyle: 'valueCellStyle' };
|
||||
if (value === false) return { value: 'false', gridStyle: 'valueCellStyle' };
|
||||
|
||||
if (editorTypes?.parseHexAsBuffer) {
|
||||
if (value?.type == 'Buffer' && _isArray(value.data)) {
|
||||
return { value: '0x' + arrayToHexString(value.data), gridStyle: 'valueCellStyle' };
|
||||
}
|
||||
if (value?.$binary?.base64) {
|
||||
return {
|
||||
value: base64ToHex(value.$binary.base64),
|
||||
gridStyle: 'valueCellStyle',
|
||||
};
|
||||
}
|
||||
|
||||
if (editorTypes?.parseHexAsBuffer) {
|
||||
// if (value?.type == 'Buffer' && _isArray(value.data)) {
|
||||
// return { value: '0x' + arrayToHexString(value.data), gridStyle: 'valueCellStyle' };
|
||||
// }
|
||||
}
|
||||
|
||||
if (editorTypes?.parseObjectIdAsDollar) {
|
||||
if (value?.$oid) {
|
||||
switch (intent) {
|
||||
@@ -482,6 +504,9 @@ export function getAsImageSrc(obj) {
|
||||
if (obj?.type == 'Buffer' && _isArray(obj?.data)) {
|
||||
return `data:image/png;base64, ${arrayBufferToBase64(obj?.data)}`;
|
||||
}
|
||||
if (obj?.$binary?.base64) {
|
||||
return `data:image/png;base64, ${obj.$binary.base64}`;
|
||||
}
|
||||
|
||||
if (_isString(obj) && (obj.startsWith('http://') || obj.startsWith('https://'))) {
|
||||
return obj;
|
||||
|
||||
Vendored
+1
@@ -16,6 +16,7 @@ export interface SqlDumper extends AlterProcessor {
|
||||
transform(type: TransformType, dumpExpr: () => void);
|
||||
createDatabase(name: string);
|
||||
dropDatabase(name: string);
|
||||
comment(value: string);
|
||||
|
||||
callableTemplate(func: CallableObjectInfo);
|
||||
|
||||
|
||||
Vendored
+2
@@ -96,4 +96,6 @@ export type TestEngineInfo = {
|
||||
}>;
|
||||
|
||||
objects?: Array<TestObjectInfo>;
|
||||
|
||||
binaryDataType?: string;
|
||||
};
|
||||
|
||||
@@ -26,12 +26,23 @@
|
||||
|
||||
<script lang="javascript">
|
||||
window.dbgate_page = '{{page}}';
|
||||
</script>
|
||||
|
||||
if (localStorage.getItem('currentThemeType') == 'dark') {
|
||||
document.documentElement.style.setProperty('--theme-background', '#111');
|
||||
document.documentElement.style.setProperty('--theme-foreground', '#e3e3e3');
|
||||
} else {
|
||||
document.documentElement.style.setProperty('--theme-background', '#fff');
|
||||
document.documentElement.style.setProperty('--theme-foreground', '#262626');
|
||||
}
|
||||
</script>
|
||||
|
||||
<script defer src="build/bundle.js"></script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
background-color: var(--theme-background);
|
||||
}
|
||||
|
||||
.lds-ellipsis {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
@@ -44,7 +55,7 @@
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
border-radius: 50%;
|
||||
background: #000;
|
||||
background: var(--theme-foreground);
|
||||
animation-timing-function: cubic-bezier(0, 1, 1, 0);
|
||||
}
|
||||
.lds-ellipsis div:nth-child(1) {
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"chartjs-plugin-datalabels": "^2.2.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"dbgate-datalib": "^6.0.0-alpha.1",
|
||||
"dbgate-query-splitter": "^4.11.7",
|
||||
"dbgate-query-splitter": "^4.11.9",
|
||||
"dbgate-sqltree": "^6.0.0-alpha.1",
|
||||
"dbgate-tools": "^6.0.0-alpha.1",
|
||||
"dbgate-types": "^6.0.0-alpha.1",
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
import SettingsListener from './utility/SettingsListener.svelte';
|
||||
import { handleAuthOnStartup } from './clientAuth';
|
||||
import { initializeAppUpdates } from './utility/appUpdate';
|
||||
import { _t, saveSelectedLanguageToCache } from './translations';
|
||||
import { _t, getCurrentTranslations, saveSelectedLanguageToCache } from './translations';
|
||||
import { installCloudListeners } from './utility/cloudListeners';
|
||||
|
||||
export let isAdminPage = false;
|
||||
@@ -61,7 +61,13 @@
|
||||
initializeAppUpdates();
|
||||
installCloudListeners();
|
||||
refreshPublicCloudFiles();
|
||||
saveSelectedLanguageToCache();
|
||||
saveSelectedLanguageToCache(config.preferrendLanguage);
|
||||
|
||||
const electron = getElectron();
|
||||
if (electron) {
|
||||
electron.send('translation-data', JSON.stringify(getCurrentTranslations()));
|
||||
global.TRANSLATION_DATA = getCurrentTranslations();
|
||||
}
|
||||
}
|
||||
|
||||
loadedApi = loadedApiValue;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
import ErrorInfo from './elements/ErrorInfo.svelte';
|
||||
import { isOneOfPage } from './utility/pageDefs';
|
||||
import { openWebLink } from './utility/simpleTools';
|
||||
import FontIcon from './icons/FontIcon.svelte';
|
||||
|
||||
const config = useConfig();
|
||||
const values = writable({ amoid: null, databaseServer: null });
|
||||
@@ -22,17 +23,15 @@
|
||||
$: trialDaysLeft = $config?.trialDaysLeft;
|
||||
|
||||
let errorMessage = '';
|
||||
let expiredMessageSet = false;
|
||||
|
||||
$: if (isExpired && !expiredMessageSet) {
|
||||
errorMessage = 'Your license is expired';
|
||||
expiredMessageSet = true;
|
||||
}
|
||||
let isInsertingLicense = false;
|
||||
|
||||
$: trialButtonAvailable = !isExpired && trialDaysLeft == null;
|
||||
|
||||
// $: console.log('CONFIG', $config);
|
||||
|
||||
$: {
|
||||
if ($config?.isLicenseValid) {
|
||||
if ($config?.isLicenseValid && trialDaysLeft == null) {
|
||||
internalRedirectTo(isOneOfPage('admin-license') ? '/admin.html' : '/index.html');
|
||||
}
|
||||
}
|
||||
@@ -41,83 +40,124 @@
|
||||
<FormProviderCore {values}>
|
||||
<SpecialPageLayout>
|
||||
{#if getElectron() || ($config?.storageDatabase && hasPermission('admin/license'))}
|
||||
<div class="heading">License</div>
|
||||
<FormTextAreaField label="Enter your license key" name="licenseKey" rows={5} />
|
||||
<div class="heading">Thank you for using DbGate!</div>
|
||||
|
||||
<div class="submit">
|
||||
<FormSubmit
|
||||
value="Save license"
|
||||
on:click={async e => {
|
||||
sessionStorage.setItem('continueTrialConfirmed', '1');
|
||||
const { licenseKey } = e.detail;
|
||||
const resp = await apiCall('config/save-license-key', { licenseKey, tryToRenew: true });
|
||||
if (resp?.status == 'ok') {
|
||||
internalRedirectTo(isOneOfPage('admin-license') ? '/admin.html' : '/index.html');
|
||||
} else {
|
||||
errorMessage = resp?.errorMessage || 'Error saving license key';
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{#if isExpired}
|
||||
<div class="infotext"><FontIcon icon="img warn" /> Your license has expired. Please insert new license.</div>
|
||||
{:else if trialDaysLeft > 0}
|
||||
<div class="infotext">
|
||||
<FontIcon icon="img warn" /> Your trial period will expire in {trialDaysLeft} day{trialDaysLeft != 1
|
||||
? 's'
|
||||
: ''}.
|
||||
</div>
|
||||
{:else}
|
||||
<div class="infotext">
|
||||
<FontIcon icon="img info" /> Proceed by selecting a licensing option or providing your license key.
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !isExpired && trialDaysLeft == null}
|
||||
{#if isInsertingLicense}
|
||||
<FormTextAreaField label="Enter your license key" name="licenseKey" rows={5} />
|
||||
|
||||
<div class="submit">
|
||||
<div class="flex flex1">
|
||||
<div class="col-6 flex">
|
||||
<FormSubmit
|
||||
value="Save license"
|
||||
on:click={async e => {
|
||||
sessionStorage.setItem('continueTrialConfirmed', '1');
|
||||
const { licenseKey } = e.detail;
|
||||
const resp = await apiCall('config/save-license-key', { licenseKey, tryToRenew: true });
|
||||
if (resp?.status == 'ok') {
|
||||
internalRedirectTo(isOneOfPage('admin-license') ? '/admin.html' : '/index.html');
|
||||
} else {
|
||||
errorMessage = resp?.errorMessage || 'Error saving license key';
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="col-6 flex">
|
||||
<FormStyledButton
|
||||
value="Cancel"
|
||||
on:click={() => {
|
||||
isInsertingLicense = false;
|
||||
errorMessage = '';
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !isInsertingLicense}
|
||||
<div class="submit">
|
||||
<FormStyledButton
|
||||
value="Start 30-day trial"
|
||||
on:click={async e => {
|
||||
errorMessage = '';
|
||||
const license = await apiCall('config/start-trial');
|
||||
if (license?.status == 'ok') {
|
||||
value="Insert license key"
|
||||
on:click={() => {
|
||||
isInsertingLicense = true;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if trialButtonAvailable}
|
||||
<div class="submit">
|
||||
<FormStyledButton
|
||||
value="Start 30-day trial"
|
||||
on:click={async e => {
|
||||
errorMessage = '';
|
||||
const license = await apiCall('config/start-trial');
|
||||
if (license?.status == 'ok') {
|
||||
sessionStorage.setItem('continueTrialConfirmed', '1');
|
||||
internalRedirectTo(isOneOfPage('admin-license') ? '/admin.html' : '/index.html');
|
||||
} else {
|
||||
errorMessage = license?.errorMessage || 'Error starting trial';
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if trialDaysLeft > 0}
|
||||
<div class="submit">
|
||||
<FormStyledButton
|
||||
value={`Continue trial (${trialDaysLeft} days left)`}
|
||||
on:click={async e => {
|
||||
sessionStorage.setItem('continueTrialConfirmed', '1');
|
||||
internalRedirectTo(isOneOfPage('admin-license') ? '/admin.html' : '/index.html');
|
||||
} else {
|
||||
errorMessage = license?.errorMessage || 'Error starting trial';
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if trialDaysLeft > 0}
|
||||
<div class="submit">
|
||||
<FormStyledButton
|
||||
value={`Continue trial (${trialDaysLeft} days left)`}
|
||||
value="Purchase DbGate Premium"
|
||||
on:click={async e => {
|
||||
sessionStorage.setItem('continueTrialConfirmed', '1');
|
||||
internalRedirectTo(isOneOfPage('admin-license') ? '/admin.html' : '/index.html');
|
||||
// openWebLink(
|
||||
// `https://auth.dbgate.eu/create-checkout-session-simple?source=trial-${isExpired ? 'expired' : (trialDaysLeft ?? 'no')}`
|
||||
// );
|
||||
|
||||
// openWebLink(
|
||||
// `https://auth-proxy.dbgate.udolni.net/redirect-to-purchase?product=${getElectron() ? 'premium' : 'teram-premium'}&source=trial-${isExpired ? 'expired' : (trialDaysLeft ?? 'no')}`
|
||||
// );
|
||||
|
||||
openWebLink(
|
||||
`https://auth.dbgate.eu/redirect-to-purchase?product=${getElectron() ? 'premium' : 'team-premium'}&source=trial-${isExpired ? 'expired' : (trialDaysLeft ?? 'no')}`
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="submit">
|
||||
<FormStyledButton
|
||||
value="Purchase DbGate Premium"
|
||||
on:click={async e => {
|
||||
// openWebLink(
|
||||
// `https://auth.dbgate.eu/create-checkout-session-simple?source=trial-${isExpired ? 'expired' : (trialDaysLeft ?? 'no')}`
|
||||
// );
|
||||
|
||||
// openWebLink(
|
||||
// `https://auth-proxy.dbgate.udolni.net/redirect-to-purchase?product=${getElectron() ? 'premium' : 'teram-premium'}&source=trial-${isExpired ? 'expired' : (trialDaysLeft ?? 'no')}`
|
||||
// );
|
||||
|
||||
openWebLink(
|
||||
`https://auth.dbgate.eu/redirect-to-purchase?product=${getElectron() ? 'premium' : 'team-premium'}&source=trial-${isExpired ? 'expired' : (trialDaysLeft ?? 'no')}`
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if getElectron()}
|
||||
<div class="submit">
|
||||
<FormStyledButton
|
||||
value="Exit"
|
||||
on:click={e => {
|
||||
getElectron().send('quit-app');
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{#if getElectron()}
|
||||
<div class="submit">
|
||||
<FormStyledButton
|
||||
value="Exit"
|
||||
on:click={e => {
|
||||
getElectron().send('quit-app');
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if errorMessage}
|
||||
@@ -141,6 +181,10 @@
|
||||
font-size: xx-large;
|
||||
}
|
||||
|
||||
.infotext {
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.submit {
|
||||
margin: var(--dim-large-form-margin);
|
||||
display: flex;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import Link from '../elements/Link.svelte';
|
||||
import { focusedConnectionOrDatabase } from '../stores';
|
||||
import { tick } from 'svelte';
|
||||
import { _val } from '../translations';
|
||||
import { _tval } from '../translations';
|
||||
|
||||
export let list;
|
||||
export let module;
|
||||
@@ -41,12 +41,12 @@
|
||||
|
||||
$: listTranslated = (list || []).map(data => ({
|
||||
...data,
|
||||
group: data?.group && _val(data.group),
|
||||
title: data?.title && _val(data.title),
|
||||
description: data?.description && _val(data.description),
|
||||
group: data?.group && _tval(data.group),
|
||||
title: data?.title && _tval(data.title),
|
||||
description: data?.description && _tval(data.description),
|
||||
args: (data?.args || []).map(x => ({
|
||||
...x,
|
||||
label: x?.label && _val(x.label),
|
||||
label: x?.label && _tval(x.label),
|
||||
})),
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts" context="module">
|
||||
import { copyTextToClipboard } from '../utility/clipboard';
|
||||
import { _t, _val } from '../translations';
|
||||
import { _t, _tval, DefferedTranslationResult } from '../translations';
|
||||
import sqlFormatter from 'sql-formatter';
|
||||
|
||||
export const extractKey = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName);
|
||||
export const createMatcher =
|
||||
@@ -76,7 +77,7 @@
|
||||
}
|
||||
|
||||
interface DbObjMenuItem {
|
||||
label?: string | (() => string);
|
||||
label?: string | DefferedTranslationResult;
|
||||
tab?: string;
|
||||
forceNewTab?: boolean;
|
||||
initialData?: any;
|
||||
@@ -88,7 +89,8 @@
|
||||
isRename?: boolean;
|
||||
isTruncate?: boolean;
|
||||
isCopyTableName?: boolean;
|
||||
isDuplicateTable?: boolean;
|
||||
isTableBackup?: boolean;
|
||||
isTableRestore?: boolean;
|
||||
isDiagram?: boolean;
|
||||
functionName?: string;
|
||||
isExport?: boolean;
|
||||
@@ -106,6 +108,8 @@
|
||||
}
|
||||
|
||||
function createMenusCore(objectTypeField, driver, data): DbObjMenuItem[] {
|
||||
const backupMatch = data.objectTypeField === 'tables' ? data.pureName.match(TABLE_BACKUP_REGEX) : null;
|
||||
|
||||
switch (objectTypeField) {
|
||||
case 'tables':
|
||||
return [
|
||||
@@ -175,11 +179,18 @@
|
||||
isCopyTableName: true,
|
||||
requiresWriteAccess: false,
|
||||
},
|
||||
hasPermission('dbops/table/backup') && {
|
||||
label: _t('dbObject.createTableBackup', { defaultMessage: 'Create table backup' }),
|
||||
isDuplicateTable: true,
|
||||
requiresWriteAccess: true,
|
||||
},
|
||||
hasPermission('dbops/table/backup') &&
|
||||
!backupMatch && {
|
||||
label: _t('dbObject.createTableBackup', { defaultMessage: 'Create table backup' }),
|
||||
isTableBackup: true,
|
||||
requiresWriteAccess: true,
|
||||
},
|
||||
hasPermission('dbops/table/restore') &&
|
||||
backupMatch && {
|
||||
label: _t('dbObject.createRestoreScript', { defaultMessage: 'Create restore script' }),
|
||||
isTableRestore: true,
|
||||
requiresWriteAccess: true,
|
||||
},
|
||||
hasPermission('dbops/model/view') && {
|
||||
label: _t('dbObject.showDiagram', { defaultMessage: 'Show diagram' }),
|
||||
isDiagram: true,
|
||||
@@ -396,17 +407,17 @@
|
||||
functionName: 'tableReader',
|
||||
},
|
||||
hasPermission('dbops/model/edit') && {
|
||||
label: _t('dbObject.dropCollection', { defaultMessage: 'Drop collection/container'}),
|
||||
label: _t('dbObject.dropCollection', { defaultMessage: 'Drop collection/container' }),
|
||||
isDropCollection: true,
|
||||
requiresWriteAccess: true,
|
||||
},
|
||||
hasPermission('dbops/table/rename') && {
|
||||
label: _t('dbObject.renameCollection', { defaultMessage: 'Rename collection/container'}),
|
||||
label: _t('dbObject.renameCollection', { defaultMessage: 'Rename collection/container' }),
|
||||
isRenameCollection: true,
|
||||
requiresWriteAccess: true,
|
||||
},
|
||||
hasPermission('dbops/table/backup') && {
|
||||
label: _t('dbObject.createCollectionBackup', { defaultMessage: 'Create collection/container backup'}),
|
||||
label: _t('dbObject.createCollectionBackup', { defaultMessage: 'Create collection/container backup' }),
|
||||
isDuplicateCollection: true,
|
||||
requiresWriteAccess: true,
|
||||
},
|
||||
@@ -590,7 +601,10 @@
|
||||
});
|
||||
} else if (menu.isDropCollection) {
|
||||
showModal(ConfirmModal, {
|
||||
message: _t('dbObject.confirmDropCollection', { defaultMessage: 'Really drop collection {name}?', values: { name: data.pureName } }),
|
||||
message: _t('dbObject.confirmDropCollection', {
|
||||
defaultMessage: 'Really drop collection {name}?',
|
||||
values: { name: data.pureName },
|
||||
}),
|
||||
onConfirm: async () => {
|
||||
const dbid = _.pick(data, ['conid', 'database']);
|
||||
runOperationOnDatabase(dbid, {
|
||||
@@ -621,7 +635,10 @@
|
||||
const driver = await getDriver();
|
||||
|
||||
showModal(ConfirmModal, {
|
||||
message: _t('dbObject.confirmCloneCollection', { defaultMessage: 'Really create collection/container copy named {name}?', values: { name: newName } }),
|
||||
message: _t('dbObject.confirmCloneCollection', {
|
||||
defaultMessage: 'Really create collection/container copy named {name}?',
|
||||
values: { name: newName },
|
||||
}),
|
||||
onConfirm: async () => {
|
||||
const dbid = _.pick(data, ['conid', 'database']);
|
||||
runOperationOnDatabase(dbid, {
|
||||
@@ -631,7 +648,7 @@
|
||||
});
|
||||
},
|
||||
});
|
||||
} else if (menu.isDuplicateTable) {
|
||||
} else if (menu.isTableBackup) {
|
||||
const driver = await getDriver();
|
||||
const dmp = driver.createDumper();
|
||||
const newTable = _.cloneDeep(data);
|
||||
@@ -665,6 +682,25 @@
|
||||
},
|
||||
engine: driver.engine,
|
||||
});
|
||||
} else if (menu.isTableRestore) {
|
||||
const backupMatch = data.objectTypeField === 'tables' ? data.pureName.match(TABLE_BACKUP_REGEX) : null;
|
||||
|
||||
const driver = await getDriver();
|
||||
const dmp = driver.createDumper();
|
||||
const db = await getDatabaseInfo(data);
|
||||
if (db) {
|
||||
const originalTable = db?.tables?.find(x => x.pureName == backupMatch[1] && x.schemaName == data.schemaName);
|
||||
if (originalTable) {
|
||||
createTableRestoreScript(data, originalTable, dmp);
|
||||
newQuery({
|
||||
title: _t('dbObject.restoreScript', {
|
||||
defaultMessage: 'Restore {name} #',
|
||||
values: { name: backupMatch[1] },
|
||||
}),
|
||||
initialData: sqlFormatter.format(dmp.s),
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (menu.isImport) {
|
||||
const { conid, database } = data;
|
||||
openImportExportTab({
|
||||
@@ -719,7 +755,9 @@
|
||||
|
||||
const filteredSumenus = coreMenus.map(item => {
|
||||
if (!item.submenu) {
|
||||
return { ...item , label: _val(item.label)};
|
||||
if (!item) return item;
|
||||
|
||||
return { ...item, label: _tval(item.label) };
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
@@ -769,7 +807,9 @@
|
||||
openNewTab(
|
||||
{
|
||||
// title: getObjectTitle(connection, schemaName, pureName),
|
||||
title: tabComponent ? getObjectTitle(connection, schemaName, pureName) : _t('dbObject.query', { defaultMessage: 'Query #' }),
|
||||
title: tabComponent
|
||||
? getObjectTitle(connection, schemaName, pureName)
|
||||
: _t('dbObject.query', { defaultMessage: 'Query #' }),
|
||||
focused: tabComponent == null,
|
||||
tooltip,
|
||||
icon:
|
||||
@@ -998,6 +1038,8 @@
|
||||
|
||||
return handleDatabaseObjectClick(data, { forceNewTab, tabPreviewMode, focusTab });
|
||||
}
|
||||
|
||||
export const TABLE_BACKUP_REGEX = /^_(.*)_(\d\d\d\d)-(\d\d)-(\d\d)-(\d\d)-(\d\d)-(\d\d)$/;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -1015,7 +1057,7 @@
|
||||
} from '../stores';
|
||||
import openNewTab from '../utility/openNewTab';
|
||||
import { extractDbNameFromComposite, filterNameCompoud, getConnectionLabel } from 'dbgate-tools';
|
||||
import { getConnectionInfo } from '../utility/metadataLoaders';
|
||||
import { getConnectionInfo, getDatabaseInfo } from '../utility/metadataLoaders';
|
||||
import fullDisplayName from '../utility/fullDisplayName';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import { findEngineDriver } from 'dbgate-tools';
|
||||
@@ -1036,6 +1078,9 @@
|
||||
import { getSupportedScriptTemplates } from '../utility/applyScriptTemplate';
|
||||
import { getBoolSettingsValue, getOpenDetailOnArrowsSettings } from '../settings/settingsTools';
|
||||
import { isProApp } from '../utility/proTools';
|
||||
import formatFileSize from '../utility/formatFileSize';
|
||||
import { createTableRestoreScript } from '../utility/tableRestoreScript';
|
||||
import newQuery from '../query/newQuery';
|
||||
|
||||
export let data;
|
||||
export let passProps;
|
||||
@@ -1064,6 +1109,9 @@
|
||||
if (data.tableRowCount != null) {
|
||||
res.push(`${formatRowCount(data.tableRowCount)} rows`);
|
||||
}
|
||||
if (data.sizeBytes) {
|
||||
res.push(formatFileSize(data.sizeBytes));
|
||||
}
|
||||
if (data.tableEngine) {
|
||||
res.push(data.tableEngine);
|
||||
}
|
||||
@@ -1072,14 +1120,21 @@
|
||||
}
|
||||
|
||||
$: isPinned = !!$pinnedTables.find(x => testEqual(data, x));
|
||||
|
||||
$: backupParsed = data.objectTypeField === 'tables' ? data.pureName.match(TABLE_BACKUP_REGEX) : null;
|
||||
$: backupTitle =
|
||||
backupParsed != null
|
||||
? `${backupParsed[1]} (${backupParsed[2]}-${backupParsed[3]}-${backupParsed[4]} ${backupParsed[5]}:${backupParsed[6]}:${backupParsed[7]})`
|
||||
: null;
|
||||
</script>
|
||||
|
||||
<AppObjectCore
|
||||
{...$$restProps}
|
||||
module={$$props.module}
|
||||
{data}
|
||||
title={data.schemaName && !passProps?.hideSchemaName ? `${data.schemaName}.${data.pureName}` : data.pureName}
|
||||
icon={databaseObjectIcons[data.objectTypeField]}
|
||||
title={backupTitle ??
|
||||
(data.schemaName && !passProps?.hideSchemaName ? `${data.schemaName}.${data.pureName}` : data.pureName)}
|
||||
icon={backupParsed ? 'img table-backup' : databaseObjectIcons[data.objectTypeField]}
|
||||
menu={createMenu}
|
||||
showPinnedInsteadOfUnpin={passProps?.showPinnedInsteadOfUnpin}
|
||||
onPin={passProps?.ingorePin ? null : isPinned ? null : () => pinnedTables.update(list => [...list, data])}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import getElectron from '../utility/getElectron';
|
||||
import InlineButtonLabel from '../buttons/InlineButtonLabel.svelte';
|
||||
import resolveApi, { resolveApiHeaders } from '../utility/resolveApi';
|
||||
import { _t } from '../translations';
|
||||
|
||||
import uuidv1 from 'uuid/v1';
|
||||
|
||||
@@ -49,11 +50,11 @@
|
||||
</script>
|
||||
|
||||
{#if electron}
|
||||
<InlineButton on:click={handleOpenElectronFile} title="Open file" data-testid={$$props['data-testid']}>
|
||||
<InlineButton on:click={handleOpenElectronFile} title={_t('files.openFile', { defaultMessage: "Open file" })} data-testid={$$props['data-testid']}>
|
||||
<FontIcon {icon} />
|
||||
</InlineButton>
|
||||
{:else}
|
||||
<InlineButtonLabel on:click={() => {}} title="Upload file" data-testid={$$props['data-testid']} htmlFor={inputId}>
|
||||
<InlineButtonLabel on:click={() => {}} title={_t('files.uploadFile', { defaultMessage: "Upload file" })} data-testid={$$props['data-testid']} htmlFor={inputId}>
|
||||
<FontIcon {icon} />
|
||||
</InlineButtonLabel>
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script context="module">
|
||||
function getCommandTitle(command) {
|
||||
let res = _val(command.text);
|
||||
let res = _tval(command.text);
|
||||
if (command.keyText || command.keyTextFromGroup) {
|
||||
res += ` (${formatKeyText(command.keyText || command.keyTextFromGroup)})`;
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
import { formatKeyText } from '../utility/common';
|
||||
import ToolStripButton from './ToolStripButton.svelte';
|
||||
import _ from 'lodash';
|
||||
import { _val } from '../translations';
|
||||
import { _tval } from '../translations';
|
||||
|
||||
export let command;
|
||||
export let component = ToolStripButton;
|
||||
@@ -34,6 +34,6 @@
|
||||
{iconAfter}
|
||||
{...$$restProps}
|
||||
>
|
||||
{(_val(buttonLabel) || _val(cmd?.toolbarName) || _val(cmd?.name))}
|
||||
{(_tval(buttonLabel) || _tval(cmd?.toolbarName) || _tval(cmd?.name))}
|
||||
</svelte:component>
|
||||
{/if}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
import ToolStripCommandButton from './ToolStripCommandButton.svelte';
|
||||
import ToolStripDropDownButton from './ToolStripDropDownButton.svelte';
|
||||
import _ from 'lodash';
|
||||
import { _val } from '../translations';
|
||||
import { _tval } from '../translations';
|
||||
export let quickExportHandlerRef = null;
|
||||
export let command = 'sqlDataGrid.export';
|
||||
export let label = 'Export';
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
{#if hasPermission('dbops/export')}
|
||||
{#if quickExportHandlerRef}
|
||||
<ToolStripDropDownButton menu={getExportMenu} label={_val(label)} icon="icon export" />
|
||||
<ToolStripDropDownButton menu={getExportMenu} label={_tval(label)} icon="icon export" />
|
||||
{:else}
|
||||
<ToolStripCommandButton {command} />
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import FormStyledButtonLikeLabel from '../buttons/FormStyledButtonLikeLabel.svelte';
|
||||
import uploadFiles from '../utility/uploadFiles';
|
||||
import { _t } from '../translations';
|
||||
|
||||
const handleChange = e => {
|
||||
const files = [...e.target.files];
|
||||
@@ -9,6 +10,6 @@
|
||||
</script>
|
||||
|
||||
<div class="m-1">
|
||||
<FormStyledButtonLikeLabel htmlFor="uploadFileButton">Upload file</FormStyledButtonLikeLabel>
|
||||
<FormStyledButtonLikeLabel htmlFor="uploadFileButton">{_t('files.uploadFile', { defaultMessage: "Upload file" })}</FormStyledButtonLikeLabel>
|
||||
<input type="file" id="uploadFileButton" hidden on:change={handleChange} />
|
||||
</div>
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
if (value?.type == 'Buffer' && _.isArray(value?.data)) {
|
||||
return 'data:image/png;base64, ' + btoa(String.fromCharCode.apply(null, value?.data));
|
||||
}
|
||||
if (value?.$binary?.base64) {
|
||||
return 'data:image/png;base64, ' + value.$binary.base64;
|
||||
}
|
||||
return null;
|
||||
} catch (err) {
|
||||
console.log('Error showing picture', err);
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
import { getLocalStorage } from '../utility/storageCache';
|
||||
import registerCommand from './registerCommand';
|
||||
import { formatKeyText, switchCurrentDatabase } from '../utility/common';
|
||||
import { _val, __t } from '../translations';
|
||||
import { _tval, __t } from '../translations';
|
||||
|
||||
let domInput;
|
||||
let filter = '';
|
||||
@@ -114,11 +114,11 @@
|
||||
($visibleCommandPalette == 'database'
|
||||
? extractDbItems($databaseInfo, { conid, database }, $connectionList)
|
||||
: parentCommand
|
||||
? parentCommand.getSubCommands()
|
||||
: sortedComands
|
||||
? parentCommand.getSubCommands()
|
||||
: sortedComands
|
||||
).filter(x => !x.isGroupCommand),
|
||||
{
|
||||
extract: x => _val(x.text),
|
||||
extract: x => _tval(x.text),
|
||||
pre: '<b>',
|
||||
post: '</b>',
|
||||
}
|
||||
@@ -163,10 +163,10 @@
|
||||
on:clickOutside={() => {
|
||||
$visibleCommandPalette = null;
|
||||
}}
|
||||
data-testid='CommandPalette_main'
|
||||
data-testid="CommandPalette_main"
|
||||
>
|
||||
<div
|
||||
class="overlay"
|
||||
<div
|
||||
class="overlay"
|
||||
on:click={() => {
|
||||
$visibleCommandPalette = null;
|
||||
}}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { currentDatabase, getCurrentDatabase } from '../stores';
|
||||
import getElectron from '../utility/getElectron';
|
||||
import registerCommand from './registerCommand';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { switchCurrentDatabase } from '../utility/common';
|
||||
import { getDatabasStatusMenu, switchCurrentDatabase } from '../utility/common';
|
||||
import { __t } from '../translations';
|
||||
|
||||
registerCommand({
|
||||
@@ -18,33 +18,7 @@ registerCommand({
|
||||
conid: connection._id,
|
||||
database: name,
|
||||
};
|
||||
return [
|
||||
{
|
||||
text: 'Sync model (incremental)',
|
||||
onClick: () => {
|
||||
apiCall('database-connections/sync-model', dbid);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Sync model (full)',
|
||||
onClick: () => {
|
||||
apiCall('database-connections/sync-model', { ...dbid, isFullRefresh: true });
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Reopen',
|
||||
onClick: () => {
|
||||
apiCall('database-connections/refresh', dbid);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Disconnect',
|
||||
onClick: () => {
|
||||
const electron = getElectron();
|
||||
if (electron) apiCall('database-connections/disconnect', dbid);
|
||||
switchCurrentDatabase(null);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return getDatabasStatusMenu(dbid);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@ registerCommand({
|
||||
id: 'database.switch',
|
||||
category: __t('command.database', { defaultMessage: 'Database' }),
|
||||
name: __t('command.database.changeRecent', { defaultMessage: 'Change to recent' }),
|
||||
menuName: 'Switch recent database',
|
||||
menuName: __t('command.database.switchRecent', { defaultMessage: 'Switch recent database' }),
|
||||
keyText: 'CtrlOrCommand+D',
|
||||
getSubCommands: () => getRecentDatabases().map(switchDatabaseCommand),
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { commands } from '../stores';
|
||||
import { invalidateCommandDefinitions } from './invalidateCommands';
|
||||
import _ from 'lodash';
|
||||
import { _val } from '../translations';
|
||||
import { _tval, DefferedTranslationResult, isDefferedTranslationResult } from '../translations';
|
||||
|
||||
export interface SubCommand {
|
||||
text: string;
|
||||
@@ -10,10 +10,10 @@ export interface SubCommand {
|
||||
|
||||
export interface GlobalCommand {
|
||||
id: string;
|
||||
category: string | (() => string); // null for group commands
|
||||
category: string | DefferedTranslationResult; // null for group commands
|
||||
isGroupCommand?: boolean;
|
||||
name: string | (() => string);
|
||||
text?: string | (() => string);
|
||||
name: string | DefferedTranslationResult;
|
||||
text?: string | DefferedTranslationResult;
|
||||
keyText?: string;
|
||||
keyTextFromGroup?: string; // automatically filled from group
|
||||
group?: string;
|
||||
@@ -25,8 +25,8 @@ export interface GlobalCommand {
|
||||
toolbar?: boolean;
|
||||
enabled?: boolean;
|
||||
showDisabled?: boolean;
|
||||
toolbarName?: string | (() => string);
|
||||
menuName?: string;
|
||||
toolbarName?: string | DefferedTranslationResult;
|
||||
menuName?: string | DefferedTranslationResult;
|
||||
toolbarOrder?: number;
|
||||
disableHandleKeyText?: string;
|
||||
isRelatedToTab?: boolean;
|
||||
@@ -44,9 +44,10 @@ export default function registerCommand(command: GlobalCommand) {
|
||||
...x,
|
||||
[command.id]: {
|
||||
text:
|
||||
_val(command.category) || _val(command.name)
|
||||
? () =>
|
||||
`${_val(command.category)}: ${_val(command.name)}`
|
||||
isDefferedTranslationResult(command.category) || isDefferedTranslationResult(command.name)
|
||||
? {
|
||||
_transCallback: () => `${_tval(command.category)}: ${_tval(command.name)}`,
|
||||
}
|
||||
: `${command.category}: ${command.name}`,
|
||||
...command,
|
||||
enabled: !testEnabled,
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
promoWidgetPreview,
|
||||
visibleToolbar,
|
||||
visibleWidgetSideBar,
|
||||
selectedWidget,
|
||||
} from '../stores';
|
||||
import registerCommand from './registerCommand';
|
||||
import { get } from 'svelte/store';
|
||||
@@ -115,13 +116,13 @@ registerCommand({
|
||||
toolbar: true,
|
||||
icon: 'icon new-connection',
|
||||
toolbarName: __t('command.new.connection', { defaultMessage: 'Add connection' }),
|
||||
category: __t('command.new', { defaultMessage: 'New'}),
|
||||
category: __t('command.new', { defaultMessage: 'New' }),
|
||||
toolbarOrder: 1,
|
||||
name: __t('command.new.connection', { defaultMessage: 'Connection' }),
|
||||
testEnabled: () => !getCurrentConfig()?.runAsPortal && !getCurrentConfig()?.storageDatabase,
|
||||
onClick: () => {
|
||||
openNewTab({
|
||||
title: 'New Connection',
|
||||
title: _t('common.newConnection', { defaultMessage: 'New Connection' }),
|
||||
icon: 'img connection',
|
||||
tabComponent: 'ConnectionTab',
|
||||
});
|
||||
@@ -140,7 +141,7 @@ registerCommand({
|
||||
!getCurrentConfig()?.runAsPortal && !getCurrentConfig()?.storageDatabase && !!getCloudSigninTokenHolder(),
|
||||
onClick: () => {
|
||||
openNewTab({
|
||||
title: 'New Connection on Cloud',
|
||||
title: _t('common.newConnectionCloud', { defaultMessage: 'New Connection on Cloud' }),
|
||||
icon: 'img cloud-connection',
|
||||
tabComponent: 'ConnectionTab',
|
||||
props: {
|
||||
@@ -192,7 +193,7 @@ registerCommand({
|
||||
category: __t('command.new', { defaultMessage: 'New' }),
|
||||
icon: 'img shell',
|
||||
name: __t('command.new.shell', { defaultMessage: 'JavaScript Shell' }),
|
||||
menuName: 'New JavaScript shell',
|
||||
menuName: __t('command.new.JSShell', { defaultMessage: 'New JavaScript shell' }),
|
||||
onClick: () => {
|
||||
openNewTab({
|
||||
title: 'Shell #',
|
||||
@@ -208,7 +209,7 @@ if (isProApp()) {
|
||||
category: __t('command.new', { defaultMessage: 'New' }),
|
||||
icon: 'img query-design',
|
||||
name: __t('command.new.queryDesign', { defaultMessage: 'Query design' }),
|
||||
menuName: 'New query design',
|
||||
menuName: __t('command.new.newQueryDesign', { defaultMessage: 'New query design' }),
|
||||
onClick: () => newQueryDesign(),
|
||||
testEnabled: () =>
|
||||
getCurrentDatabase() &&
|
||||
@@ -222,7 +223,7 @@ if (isProApp()) {
|
||||
category: __t('command.new', { defaultMessage: 'New' }),
|
||||
icon: 'img transform',
|
||||
name: __t('command.new.modelTransform', { defaultMessage: 'Model transform' }),
|
||||
menuName: 'New model transform',
|
||||
menuName: __t('command.new.newModelTransform', { defaultMessage: 'New model transform' }),
|
||||
onClick: () => {
|
||||
openNewTab(
|
||||
{
|
||||
@@ -266,7 +267,7 @@ if (isProApp()) {
|
||||
category: __t('command.new', { defaultMessage: 'New' }),
|
||||
icon: 'img perspective',
|
||||
name: __t('command.new.perspective', { defaultMessage: 'Perspective' }),
|
||||
menuName: 'New perspective',
|
||||
menuName: __t('command.new.newPerspective', { defaultMessage: 'New perspective' }),
|
||||
onClick: () => newPerspective(),
|
||||
});
|
||||
}
|
||||
@@ -277,7 +278,7 @@ if (isProApp()) {
|
||||
category: __t('command.new', { defaultMessage: 'New' }),
|
||||
icon: 'img app',
|
||||
name: __t('command.new.application', { defaultMessage: 'Application' }),
|
||||
menuName: 'New application',
|
||||
menuName: __t('command.new.newApplication', { defaultMessage: 'New application' }),
|
||||
onClick: () => {
|
||||
openNewTab({
|
||||
title: 'Application #',
|
||||
@@ -293,7 +294,7 @@ registerCommand({
|
||||
category: __t('command.new', { defaultMessage: 'New' }),
|
||||
icon: 'img diagram',
|
||||
name: __t('command.new.diagram', { defaultMessage: 'ER Diagram' }),
|
||||
menuName: 'New ER diagram',
|
||||
menuName: __t('command.new.newDiagram', { defaultMessage: 'New ER diagram' }),
|
||||
testEnabled: () =>
|
||||
getCurrentDatabase() &&
|
||||
findEngineDriver(getCurrentDatabase()?.connection, getExtensions())?.databaseEngineTypes?.includes('sql'),
|
||||
@@ -416,7 +417,7 @@ registerCommand({
|
||||
category: __t('command.new', { defaultMessage: 'New' }),
|
||||
icon: 'img archive',
|
||||
name: __t('command.new.jsonl', { defaultMessage: 'JSON Lines' }),
|
||||
menuName: 'New JSON lines file',
|
||||
menuName: __t('command.new.newJsonl', { defaultMessage: 'New JSON lines file' }),
|
||||
onClick: () => {
|
||||
openNewTab(
|
||||
{
|
||||
@@ -436,7 +437,7 @@ registerCommand({
|
||||
category: __t('command.new', { defaultMessage: 'New' }),
|
||||
icon: 'img sqlite-database',
|
||||
name: __t('command.new.sqliteDatabase', { defaultMessage: 'SQLite database' }),
|
||||
menuName: _t('command.new.sqliteDatabase', { defaultMessage: 'New SQLite database' }),
|
||||
menuName: __t('command.new.sqliteDatabase', { defaultMessage: 'New SQLite database' }),
|
||||
onClick: () => {
|
||||
showModal(InputTextModal, {
|
||||
value: 'newdb',
|
||||
@@ -456,7 +457,7 @@ registerCommand({
|
||||
category: __t('command.new', { defaultMessage: 'New' }),
|
||||
icon: 'img sqlite-database',
|
||||
name: __t('command.new.duckdbDatabase', { defaultMessage: 'DuckDB database' }),
|
||||
menuName: _t('command.new.duckdbDatabase', { defaultMessage: 'New DuckDB database' }),
|
||||
menuName: __t('command.new.duckdbDatabase', { defaultMessage: 'New DuckDB database' }),
|
||||
onClick: () => {
|
||||
showModal(InputTextModal, {
|
||||
value: 'newdb',
|
||||
@@ -561,7 +562,10 @@ registerCommand({
|
||||
testEnabled: () => true,
|
||||
onClick: () => {
|
||||
showModal(ConfirmModal, {
|
||||
message: _t('command.file.resetLayoutConfirm', { defaultMessage: 'Really reset layout data? All opened tabs, settings and layout data will be lost. Connections and saved files will be preserved. After this, restart DbGate for applying changes.' }),
|
||||
message: _t('command.file.resetLayoutConfirm', {
|
||||
defaultMessage:
|
||||
'Really reset layout data? All opened tabs, settings and layout data will be lost. Connections and saved files will be preserved. After this, restart DbGate for applying changes.',
|
||||
}),
|
||||
onConfirm: async () => {
|
||||
await apiCall('config/delete-settings');
|
||||
localStorage.clear();
|
||||
@@ -665,7 +669,9 @@ registerCommand({
|
||||
'currentArchive',
|
||||
];
|
||||
for (const key of keys) removeLocalStorage(key);
|
||||
showSnackbarSuccess(_t('command.view.restart', { defaultMessage: 'Restart DbGate (or reload on web) for applying changes' }));
|
||||
showSnackbarSuccess(
|
||||
_t('command.view.restart', { defaultMessage: 'Restart DbGate (or reload on web) for applying changes' })
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -762,13 +768,26 @@ if (isProApp()) {
|
||||
}
|
||||
|
||||
if (hasPermission('settings/change')) {
|
||||
registerCommand({
|
||||
id: 'settings.settingsTab',
|
||||
category: __t('command.settings', { defaultMessage: 'Settings' }),
|
||||
name: __t('command.settings.settingsTab', { defaultMessage: 'Settings tab' }),
|
||||
onClick: () => {
|
||||
openNewTab({
|
||||
title: _t('command.settings.settingsTab', { defaultMessage: 'Settings tab' }),
|
||||
icon: 'icon settings',
|
||||
tabComponent: 'SettingsTab',
|
||||
props: {},
|
||||
});
|
||||
},
|
||||
});
|
||||
registerCommand({
|
||||
id: 'settings.commands',
|
||||
category: __t('command.settings', { defaultMessage: 'Settings' }),
|
||||
name: __t('command.settings.shortcuts', { defaultMessage: 'Keyboard shortcuts' }),
|
||||
onClick: () => {
|
||||
openNewTab({
|
||||
title: __t('command.settings.shortcuts', { defaultMessage: 'Keyboard shortcuts' }),
|
||||
title: _t('command.settings.shortcuts', { defaultMessage: 'Keyboard shortcuts' }),
|
||||
icon: 'icon keyboard',
|
||||
tabComponent: 'CommandListTab',
|
||||
props: {},
|
||||
@@ -777,14 +796,14 @@ if (hasPermission('settings/change')) {
|
||||
testEnabled: () => hasPermission('settings/change'),
|
||||
});
|
||||
|
||||
registerCommand({
|
||||
id: 'settings.show',
|
||||
category: __t('command.settings', { defaultMessage: 'Settings' }),
|
||||
name: __t('command.settings.change', { defaultMessage: 'Change' }),
|
||||
toolbarName: __t('command.settings', { defaultMessage: 'Settings' }),
|
||||
onClick: () => showModal(SettingsModal),
|
||||
testEnabled: () => hasPermission('settings/change'),
|
||||
});
|
||||
// registerCommand({
|
||||
// id: 'settings.show',
|
||||
// category: __t('command.settings', { defaultMessage: 'Settings' }),
|
||||
// name: __t('command.settings.change', { defaultMessage: 'Change' }),
|
||||
// toolbarName: __t('command.settings', { defaultMessage: 'Settings' }),
|
||||
// onClick: () => showModal(SettingsModal),
|
||||
// testEnabled: () => hasPermission('settings/change'),
|
||||
// });
|
||||
}
|
||||
|
||||
registerCommand({
|
||||
@@ -799,7 +818,9 @@ registerCommand({
|
||||
registerCommand({
|
||||
id: 'file.exit',
|
||||
category: __t('command.file', { defaultMessage: 'File' }),
|
||||
name: isMac() ? __t('command.file.quit', { defaultMessage: 'Quit' }) : __t('command.file.exit', { defaultMessage: 'Exit' }),
|
||||
name: isMac()
|
||||
? __t('command.file.quit', { defaultMessage: 'Quit' })
|
||||
: __t('command.file.exit', { defaultMessage: 'Exit' }),
|
||||
// keyText: isMac() ? 'Command+Q' : null,
|
||||
testEnabled: () => getElectron() != null,
|
||||
onClick: () => getElectron().send('quit-app'),
|
||||
@@ -862,6 +883,7 @@ export function registerFileCommands({
|
||||
undoRedo = false,
|
||||
executeAdditionalCondition = null,
|
||||
copyPaste = false,
|
||||
defaultTeamFolder = false,
|
||||
}) {
|
||||
if (save) {
|
||||
registerCommand({
|
||||
@@ -874,7 +896,7 @@ export function registerFileCommands({
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
testEnabled: () => getCurrentEditor() != null,
|
||||
onClick: () => saveTabFile(getCurrentEditor(), 'save', folder, format, fileExtension),
|
||||
onClick: () => saveTabFile(getCurrentEditor(), 'save', folder, format, fileExtension, defaultTeamFolder),
|
||||
});
|
||||
registerCommand({
|
||||
id: idPrefix + '.saveAs',
|
||||
@@ -882,14 +904,14 @@ export function registerFileCommands({
|
||||
category,
|
||||
name: __t('command.saveAs', { defaultMessage: 'Save As' }),
|
||||
testEnabled: () => getCurrentEditor() != null,
|
||||
onClick: () => saveTabFile(getCurrentEditor(), 'save-as', folder, format, fileExtension),
|
||||
onClick: () => saveTabFile(getCurrentEditor(), 'save-as', folder, format, fileExtension, defaultTeamFolder),
|
||||
});
|
||||
registerCommand({
|
||||
id: idPrefix + '.saveToDisk',
|
||||
category,
|
||||
name: __t('command.saveToDisk', { defaultMessage: 'Save to disk' }),
|
||||
testEnabled: () => getCurrentEditor() != null && getElectron() != null,
|
||||
onClick: () => saveTabFile(getCurrentEditor(), 'save-to-disk', folder, format, fileExtension),
|
||||
onClick: () => saveTabFile(getCurrentEditor(), 'save-to-disk', folder, format, fileExtension, defaultTeamFolder),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1202,6 +1224,35 @@ registerCommand({
|
||||
},
|
||||
});
|
||||
|
||||
if ( hasPermission('application-log'))
|
||||
{
|
||||
registerCommand({
|
||||
id: 'app.showLogs',
|
||||
category: __t('command.application', { defaultMessage: 'Application' }),
|
||||
name: __t('command.application.showLogs', { defaultMessage: 'View application logs' }),
|
||||
onClick: () => {
|
||||
openNewTab({
|
||||
title: 'Application log',
|
||||
icon: 'img applog',
|
||||
tabComponent: 'AppLogTab',
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (hasPermission('widgets/plugins'))
|
||||
{
|
||||
registerCommand({
|
||||
id: 'app.managePlugins',
|
||||
category: __t('command.application', { defaultMessage: 'Application' }),
|
||||
name: __t('command.application.managePlugins', { defaultMessage: 'Manage plugins' }),
|
||||
onClick: () => {
|
||||
selectedWidget.set('plugins');
|
||||
visibleWidgetSideBar.set(true);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const electron = getElectron();
|
||||
if (electron) {
|
||||
electron.addEventListener('run-command', (e, commandId) => runCommand(commandId));
|
||||
|
||||
@@ -361,6 +361,7 @@
|
||||
detectSqlFilterBehaviour,
|
||||
stringifyCellValue,
|
||||
shouldOpenMultilineDialog,
|
||||
base64ToHex,
|
||||
} from 'dbgate-tools';
|
||||
import { getContext, onDestroy } from 'svelte';
|
||||
import _, { map } from 'lodash';
|
||||
@@ -421,7 +422,7 @@
|
||||
import { openJsonLinesData } from '../utility/openJsonLinesData';
|
||||
import contextMenuActivator from '../utility/contextMenuActivator';
|
||||
import InputTextModal from '../modals/InputTextModal.svelte';
|
||||
import { __t, _t, _val } from '../translations';
|
||||
import { __t, _t, _tval } from '../translations';
|
||||
import { isProApp } from '../utility/proTools';
|
||||
import SaveArchiveModal from '../modals/SaveArchiveModal.svelte';
|
||||
import hasPermission from '../utility/hasPermission';
|
||||
@@ -758,7 +759,7 @@
|
||||
|
||||
export function saveCellToFileEnabled() {
|
||||
const value = getSelectedExportableCell();
|
||||
return _.isString(value) || (value?.type == 'Buffer' && _.isArray(value?.data));
|
||||
return _.isString(value) || (value?.type == 'Buffer' && _.isArray(value?.data)) || (value?.$binary?.base64);
|
||||
}
|
||||
|
||||
export async function saveCellToFile() {
|
||||
@@ -771,6 +772,8 @@
|
||||
fs.promises.writeFile(file, value);
|
||||
} else if (value?.type == 'Buffer' && _.isArray(value?.data)) {
|
||||
fs.promises.writeFile(file, window['Buffer'].from(value.data));
|
||||
} else if (value?.$binary?.base64) {
|
||||
fs.promises.writeFile(file, window['Buffer'].from(value.$binary.base64, 'base64'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -796,8 +799,9 @@
|
||||
isText
|
||||
? data
|
||||
: {
|
||||
type: 'Buffer',
|
||||
data: [...data],
|
||||
$binary: {
|
||||
base64: data.toString('base64'),
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1795,12 +1799,12 @@
|
||||
text: _t('datagrid.copyAdvanced', { defaultMessage: 'Copy advanced'}),
|
||||
submenu: [
|
||||
_.keys(copyRowsFormatDefs).map(format => ({
|
||||
text: _val(copyRowsFormatDefs[format].label),
|
||||
text: _tval(copyRowsFormatDefs[format].label),
|
||||
onClick: () => copyToClipboardCore(format),
|
||||
})),
|
||||
{ divider: true },
|
||||
_.keys(copyRowsFormatDefs).map(format => ({
|
||||
text: _t('datagrid.setFormat', { defaultMessage: 'Set format: ' }) + (_val(copyRowsFormatDefs[format].name)),
|
||||
text: _t('datagrid.setFormat', { defaultMessage: 'Set format: ' }) + (_tval(copyRowsFormatDefs[format].name)),
|
||||
onClick: () => ($copyRowsFormat = format),
|
||||
})),
|
||||
|
||||
@@ -1870,7 +1874,7 @@
|
||||
return [
|
||||
menu,
|
||||
{
|
||||
text: _val(copyRowsFormatDefs[$copyRowsFormat].label),
|
||||
text: _tval(copyRowsFormatDefs[$copyRowsFormat].label),
|
||||
onClick: () => copyToClipboardCore($copyRowsFormat),
|
||||
keyText: 'CtrlOrCommand+C',
|
||||
tag: 'copy',
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import moveDrag from '../utility/moveDrag';
|
||||
import ColumnLine from './ColumnLine.svelte';
|
||||
import DomTableRef from './DomTableRef';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let conid;
|
||||
export let database;
|
||||
@@ -185,8 +186,8 @@
|
||||
const handleSetTableAlias = () => {
|
||||
showModal(InputTextModal, {
|
||||
value: alias || '',
|
||||
label: 'New alias',
|
||||
header: 'Set table alias',
|
||||
label: _t('designerTable.newAlias', { defaultMessage: 'New alias' }),
|
||||
header: _t('designerTable.setTableAlias', { defaultMessage: 'Set table alias' }),
|
||||
onConfirm: newAlias => {
|
||||
onChangeTable({
|
||||
...table,
|
||||
@@ -210,13 +211,13 @@
|
||||
return settings?.tableMenu({ designer, designerId, onRemoveTable });
|
||||
}
|
||||
return [
|
||||
{ text: 'Remove', onClick: () => onRemoveTable({ designerId }) },
|
||||
{ text: _t('common.remove', { defaultMessage: 'Remove' }), onClick: () => onRemoveTable({ designerId }) },
|
||||
{ divider: true },
|
||||
settings?.allowTableAlias &&
|
||||
!isMultipleTableSelection && [
|
||||
{ text: 'Set table alias', onClick: handleSetTableAlias },
|
||||
{ text: _t('designerTable.setTableAlias', { defaultMessage: 'Set table alias' }), onClick: handleSetTableAlias },
|
||||
alias && {
|
||||
text: 'Remove table alias',
|
||||
text: _t('designerTable.removeTableAlias', { defaultMessage: 'Remove table alias' }),
|
||||
onClick: () =>
|
||||
onChangeTable({
|
||||
...table,
|
||||
@@ -225,11 +226,11 @@
|
||||
},
|
||||
],
|
||||
settings?.allowAddAllReferences &&
|
||||
!isMultipleTableSelection && { text: 'Add references', onClick: () => onAddAllReferences(table) },
|
||||
settings?.allowChangeColor && { text: 'Change color', onClick: () => onChangeTableColor(table) },
|
||||
!isMultipleTableSelection && { text: _t('designerTable.addReferences', { defaultMessage: 'Add references' }), onClick: () => onAddAllReferences(table) },
|
||||
settings?.allowChangeColor && { text: _t('designerTable.changeColor', { defaultMessage: 'Change color' }), onClick: () => onChangeTableColor(table) },
|
||||
settings?.allowDefineVirtualReferences &&
|
||||
!isMultipleTableSelection && {
|
||||
text: 'Define virtual foreign key',
|
||||
text: _t('designerTable.defineVirtualForeignKey', { defaultMessage: 'Define virtual foreign key' }),
|
||||
onClick: () => handleDefineVirtualForeignKey(table),
|
||||
},
|
||||
settings?.appendTableSystemMenu &&
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
import HorizontalSplitter from './HorizontalSplitter.svelte';
|
||||
|
||||
interface MenuItemDef {
|
||||
label: string;
|
||||
slot?: number;
|
||||
component?: any;
|
||||
props?: any;
|
||||
testid?: string;
|
||||
identifier?: string;
|
||||
}
|
||||
|
||||
export let items: MenuItemDef[];
|
||||
export let value: string | number = 0;
|
||||
export let containerMaxWidth = undefined;
|
||||
export let containerMaxHeight = undefined;
|
||||
export let flex1 = true;
|
||||
export let flexColContainer = false;
|
||||
export let maxHeight100 = false;
|
||||
export let scrollableContentContainer = false;
|
||||
export let contentTestId = undefined;
|
||||
export let onUserChange = null;
|
||||
|
||||
export function setValue(index) {
|
||||
value = index;
|
||||
}
|
||||
export function getValue() {
|
||||
return value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="main" class:maxHeight100 class:flex1>
|
||||
<HorizontalSplitter initialValue="20%">
|
||||
<svelte:fragment slot="1">
|
||||
<div class="menu">
|
||||
{#each _.compact(items) as item, index}
|
||||
<div
|
||||
class="menu-item"
|
||||
class:selected={value == (item.identifier ?? index)}
|
||||
on:click={() => {
|
||||
value = item.identifier ?? index;
|
||||
onUserChange?.(item.identifier ?? index);
|
||||
}}
|
||||
data-testid={item.testid}
|
||||
>
|
||||
<span class="ml-2 noselect">
|
||||
{item.label}
|
||||
</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="2">
|
||||
<div
|
||||
class="content-container"
|
||||
class:scrollableContentContainer
|
||||
style:max-height={containerMaxHeight}
|
||||
data-testid={contentTestId}
|
||||
>
|
||||
{#each _.compact(items) as item, index}
|
||||
<div
|
||||
class="container"
|
||||
class:flexColContainer
|
||||
class:maxHeight100
|
||||
class:itemVisible={(item.identifier ?? index) == value}
|
||||
style:max-width={containerMaxWidth}
|
||||
>
|
||||
<svelte:component
|
||||
this={item.component}
|
||||
{...item.props}
|
||||
itemVisible={(item.identifier ?? index) == value}
|
||||
menuControlHiddenItem={(item.identifier ?? index) != value}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</HorizontalSplitter>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.main {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.main.flex1 {
|
||||
flex: 1;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.main.maxHeight100 {
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: var(--theme-bg-2);
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.menu::-webkit-scrollbar {
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
white-space: nowrap;
|
||||
padding: 12px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid var(--theme-border);
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background-color: var(--theme-bg-hover);
|
||||
}
|
||||
|
||||
.menu-item.selected {
|
||||
background-color: var(--theme-bg-1);
|
||||
font-weight: 600;
|
||||
border-left: 3px solid var(--theme-font-link);
|
||||
}
|
||||
|
||||
.content-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.scrollableContentContainer {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.container.maxHeight100 {
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.container.flexColContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.container {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container:not(.itemVisible) {
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -27,7 +27,7 @@
|
||||
import { evaluateCondition } from 'dbgate-sqltree';
|
||||
import { compileCompoudEvalCondition } from 'dbgate-filterparser';
|
||||
import { chevronExpandIcon } from '../icons/expandIcons';
|
||||
import { _val } from '../translations';
|
||||
import { _tval } from '../translations';
|
||||
|
||||
export let columns: (TableControlColumn | false)[];
|
||||
export let rows = null;
|
||||
@@ -369,7 +369,7 @@
|
||||
{/if}
|
||||
{/key}
|
||||
{:else}
|
||||
{ _val(row[col.fieldName]) || '' }
|
||||
{ _tval(row[col.fieldName]) || '' }
|
||||
{/if}
|
||||
</td>
|
||||
{/each}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { getFormContext } from './FormProviderCore.svelte';
|
||||
|
||||
import FormSelectField from './FormSelectField.svelte';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let folderName;
|
||||
export let name;
|
||||
@@ -28,10 +29,10 @@
|
||||
<div>
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value="All files"
|
||||
value={_t('common.allFiles', { defaultMessage: "All files" })}
|
||||
on:click={() => setFieldValue(name, _.uniq([...($values[name] || []), ...($files && $files.map(x => x.name))]))}
|
||||
/>
|
||||
<FormStyledButton type="button" value="Remove all" on:click={() => setFieldValue(name, [])} />
|
||||
<FormStyledButton type="button" value={_t('common.removeAll', { defaultMessage: "Remove all" })} on:click={() => setFieldValue(name, [])} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { getFormContext } from './FormProviderCore.svelte';
|
||||
import TextField from './TextField.svelte';
|
||||
import { _val } from '../translations';
|
||||
import { _tval } from '../translations';
|
||||
|
||||
export let name;
|
||||
export let defaultValue;
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
<TextField
|
||||
{...$$restProps}
|
||||
value={$values?.[name] ? _val($values[name]) : defaultValue}
|
||||
value={$values?.[name] ? _tval($values[name]) : defaultValue}
|
||||
on:input={e => setFieldValue(name, e.target['value'])}
|
||||
on:input={e => {
|
||||
if (saveOnInput) {
|
||||
|
||||
@@ -353,6 +353,7 @@
|
||||
'img data-deploy': 'mdi mdi-database-settings color-icon-green',
|
||||
'img arrow-start-here': 'mdi mdi-arrow-down-bold-circle color-icon-green',
|
||||
'img team-file': 'mdi mdi-account-file color-icon-red',
|
||||
'img table-backup': 'mdi mdi-cube color-icon-yellow',
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
import ElectronFilesInput from './ElectronFilesInput.svelte';
|
||||
import { addFilesToSourceList } from './ImportExportConfigurator.svelte';
|
||||
import UploadButton from '../buttons/UploadButton.svelte';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let setPreviewSource = undefined;
|
||||
|
||||
@@ -55,10 +56,10 @@
|
||||
{:else}
|
||||
<UploadButton />
|
||||
{/if}
|
||||
<FormStyledButton value="Add web URL" on:click={handleAddUrl} />
|
||||
<FormStyledButton value={_t('importExport.addWebUrl', { defaultMessage: "Add web URL" })} on:click={handleAddUrl} />
|
||||
</div>
|
||||
|
||||
<div class="wrapper">Drag & drop imported files here</div>
|
||||
<div class="wrapper">{_t('importExport.dragDropImportedFilesHere', { defaultMessage: "Drag & drop imported files here" })}</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { getFormContext } from '../forms/FormProviderCore.svelte';
|
||||
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||
import { useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let conidName;
|
||||
export let databaseName;
|
||||
@@ -41,7 +42,7 @@
|
||||
{#if $dbinfo && $dbinfo[field]?.length > 0}
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value={`All ${field}`}
|
||||
value={_t('common.allFields', { defaultMessage: 'All {field}', values: { field } })}
|
||||
data-testid={`FormTablesSelect_buttonAll_${field}`}
|
||||
on:click={() =>
|
||||
setFieldValue(
|
||||
@@ -52,7 +53,7 @@
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<FormStyledButton type="button" value="Remove all" on:click={() => setFieldValue(name, [])} />
|
||||
<FormStyledButton type="button" value={_t('common.removeAll', { defaultMessage: "Remove all" })} on:click={() => setFieldValue(name, [])} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
import createRef from '../utility/createRef';
|
||||
import DropDownButton from '../buttons/DropDownButton.svelte';
|
||||
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
|
||||
import { _t } from '../translations';
|
||||
|
||||
// export let uploadedFile = undefined;
|
||||
// export let openedFile = undefined;
|
||||
@@ -211,7 +212,7 @@
|
||||
</div>
|
||||
|
||||
<div class="m-2">
|
||||
<div class="title"><FontIcon icon="icon tables" /> Map source tables/files</div>
|
||||
<div class="title"><FontIcon icon="icon tables" /> {_t('importExport.mapSourceTablesFiles', { defaultMessage: "Map source tables/files" })}</div>
|
||||
|
||||
{#key targetEditKey}
|
||||
{#key progressHolder}
|
||||
@@ -220,34 +221,34 @@
|
||||
columns={[
|
||||
{
|
||||
fieldName: 'source',
|
||||
header: 'Source',
|
||||
header: _t('importExport.source', { defaultMessage: "Source" }),
|
||||
component: SourceName,
|
||||
getProps: row => ({ name: row }),
|
||||
},
|
||||
{
|
||||
fieldName: 'action',
|
||||
header: 'Action',
|
||||
header: _t('importExport.action', { defaultMessage: "Action" }),
|
||||
component: SourceAction,
|
||||
getProps: row => ({ name: row, targetDbinfo }),
|
||||
},
|
||||
{
|
||||
fieldName: 'target',
|
||||
header: 'Target',
|
||||
header: _t('importExport.target', { defaultMessage: "Target" }),
|
||||
slot: 1,
|
||||
},
|
||||
supportsPreview && {
|
||||
fieldName: 'preview',
|
||||
header: 'Preview',
|
||||
header: _t('importExport.preview', { defaultMessage: "Preview" }),
|
||||
slot: 0,
|
||||
},
|
||||
!!progressHolder && {
|
||||
fieldName: 'status',
|
||||
header: 'Status',
|
||||
header: _t('importExport.status', { defaultMessage: "Status" }),
|
||||
slot: 3,
|
||||
},
|
||||
{
|
||||
fieldName: 'columns',
|
||||
header: 'Columns',
|
||||
header: _t('importExport.columns', { defaultMessage: "Columns" }),
|
||||
slot: 2,
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -73,19 +73,19 @@
|
||||
<div class="column">
|
||||
{#if direction == 'source'}
|
||||
<div class="title">
|
||||
<FontIcon icon="icon import" /> Source configuration
|
||||
<FontIcon icon="icon import" /> {_t('importExport.sourceConfiguration', { defaultMessage: 'Source configuration' })}
|
||||
</div>
|
||||
{/if}
|
||||
{#if direction == 'target'}
|
||||
<div class="title">
|
||||
<FontIcon icon="icon export" /> Target configuration
|
||||
<FontIcon icon="icon export" /> {_t('importExport.targetConfiguration', { defaultMessage: 'Target configuration' })}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="buttons">
|
||||
{#if $currentDatabase}
|
||||
<FormStyledButton
|
||||
value="Current DB"
|
||||
value={_t('importExport.currentDatabase', { defaultMessage: "Current DB" })}
|
||||
on:click={() => {
|
||||
values.update(x => ({
|
||||
...x,
|
||||
@@ -97,7 +97,7 @@
|
||||
/>
|
||||
{/if}
|
||||
<FormStyledButton
|
||||
value="Current archive"
|
||||
value={_t('importExport.currentArchive', { defaultMessage: "Current archive" })}
|
||||
data-testid={direction == 'source'
|
||||
? 'SourceTargetConfig_buttonCurrentArchive_source'
|
||||
: 'SourceTargetConfig_buttonCurrentArchive_target'}
|
||||
@@ -111,7 +111,7 @@
|
||||
/>
|
||||
{#if direction == 'target'}
|
||||
<FormStyledButton
|
||||
value="New archive"
|
||||
value={_t('importExport.newArchive', { defaultMessage: "New archive" })}
|
||||
on:click={() => {
|
||||
showModal(InputTextModal, {
|
||||
header: 'Archive',
|
||||
@@ -133,7 +133,7 @@
|
||||
<FormSelectField
|
||||
options={types.filter(x => x.directions.includes(direction))}
|
||||
name={storageTypeField}
|
||||
label="Storage type"
|
||||
label={_t('importExport.storageType', { defaultMessage: "Storage type" })}
|
||||
/>
|
||||
|
||||
{#if format && isProApp()}
|
||||
@@ -172,9 +172,9 @@
|
||||
{/if}
|
||||
|
||||
{#if storageType == 'database' || storageType == 'query'}
|
||||
<FormConnectionSelect name={connectionIdField} label="Server" {direction} />
|
||||
<FormConnectionSelect name={connectionIdField} label={_t('common.server', { defaultMessage: 'Server' })} {direction} />
|
||||
{#if !$connectionInfo?.singleDatabase}
|
||||
<FormDatabaseSelect conidName={connectionIdField} name={databaseNameField} label="Database" />
|
||||
<FormDatabaseSelect conidName={connectionIdField} name={databaseNameField} label={_t('common.database', { defaultMessage: 'Database' })} />
|
||||
{/if}
|
||||
{/if}
|
||||
{#if storageType == 'database'}
|
||||
@@ -210,7 +210,7 @@
|
||||
|
||||
{#if storageType == 'archive'}
|
||||
<FormArchiveFolderSelect
|
||||
label="Archive folder"
|
||||
label={_t('importExport.archiveFolder', { defaultMessage: "Archive folder" })}
|
||||
name={archiveFolderField}
|
||||
additionalFolders={_.compact([$values[archiveFolderField]])}
|
||||
allowCreateNew={direction == 'target'}
|
||||
|
||||
@@ -86,14 +86,26 @@
|
||||
submenuKey += 1;
|
||||
return;
|
||||
}
|
||||
if (item.switchStore && item.switchValue) {
|
||||
item.switchStore.update(x => ({
|
||||
...x,
|
||||
[item.switchValue]: !x[item.switchValue],
|
||||
}));
|
||||
if (item.switchStore) {
|
||||
if (item.switchValue) {
|
||||
item.switchStore.update(x => ({
|
||||
...x,
|
||||
[item.switchValue]: !x[item.switchValue],
|
||||
}));
|
||||
}
|
||||
|
||||
if (item.switchOption && item.switchOptionValue) {
|
||||
item.switchStore.update(x => ({
|
||||
...x,
|
||||
[item.switchOption]: item.switchOptionValue,
|
||||
}));
|
||||
}
|
||||
switchIndex++;
|
||||
return;
|
||||
if (!item.closeOnSwitchClick) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dispatchClose();
|
||||
if (onCloseParent) onCloseParent();
|
||||
if (item.onClick) item.onClick();
|
||||
@@ -163,6 +175,16 @@
|
||||
{/if}
|
||||
{/key}
|
||||
{/if}
|
||||
{#if item.switchOption && item.switchStoreGetter}
|
||||
{@const optionValue = item.switchStoreGetter()[item.switchOption]}
|
||||
{#key switchIndex}
|
||||
{#if optionValue === item.switchOptionValue || (item.switchOptionIsDefault && !optionValue)}
|
||||
<FontIcon icon="icon check" padRight />
|
||||
{:else}
|
||||
<FontIcon icon="icon invisible-box" padRight />
|
||||
{/if}
|
||||
{/key}
|
||||
{/if}
|
||||
{item.text || item.label}
|
||||
</span>
|
||||
{#if item.keyText}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { commandsCustomized, currentDropDownMenu } from '../stores';
|
||||
import { prepareMenuItems } from '../utility/contextMenu';
|
||||
import DropDownMenu from './DropDownMenu.svelte';
|
||||
|
||||
export let items;
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
import { closeCurrentModal, showModal } from './modalTools';
|
||||
import FormCloudFolderSelect from '../forms/FormCloudFolderSelect.svelte';
|
||||
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
|
||||
import { useConfig } from '../utility/metadataLoaders';
|
||||
import { showSnackbarError } from '../utility/snackbar';
|
||||
|
||||
export let data;
|
||||
export let name;
|
||||
@@ -24,26 +26,39 @@
|
||||
export let onSave = undefined;
|
||||
export let folid;
|
||||
export let skipLocal = false;
|
||||
export let defaultTeamFolder = false;
|
||||
// export let cntid;
|
||||
|
||||
const values = writable({ name, cloudFolder: folid ?? '__local' });
|
||||
const configValue = useConfig();
|
||||
|
||||
const values = writable({
|
||||
name,
|
||||
cloudFolder: folid ?? '__local',
|
||||
saveToTeamFolder: !!(getCurrentConfig()?.storageDatabase && defaultTeamFolder),
|
||||
});
|
||||
|
||||
const electron = getElectron();
|
||||
|
||||
const handleSubmit = async e => {
|
||||
const { name, cloudFolder } = e.detail;
|
||||
if ($values['saveToTeamFolder']) {
|
||||
const { teamFileId } = await apiCall('team-files/create-new', { fileType: folder, file: name, data });
|
||||
closeCurrentModal();
|
||||
if (onSave) {
|
||||
onSave(name, {
|
||||
savedFile: name,
|
||||
savedFolder: folder,
|
||||
savedFilePath: null,
|
||||
savedCloudFolderId: null,
|
||||
savedCloudContentId: null,
|
||||
savedTeamFileId: teamFileId,
|
||||
});
|
||||
const resp = await apiCall('team-files/create-new', { fileType: folder, file: name, data });
|
||||
if (resp?.apiErrorMessage) {
|
||||
showSnackbarError(resp.apiErrorMessage);
|
||||
} else if (resp?.teamFileId) {
|
||||
closeCurrentModal();
|
||||
if (onSave) {
|
||||
onSave(name, {
|
||||
savedFile: name,
|
||||
savedFolder: folder,
|
||||
savedFilePath: null,
|
||||
savedCloudFolderId: null,
|
||||
savedCloudContentId: null,
|
||||
savedTeamFileId: resp.teamFileId,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
showSnackbarError('Failed to save to team folder.');
|
||||
}
|
||||
} else if (cloudFolder === '__local') {
|
||||
await apiCall('files/save', { folder, file: name, data, format });
|
||||
@@ -124,7 +139,7 @@
|
||||
]}
|
||||
/>
|
||||
{/if}
|
||||
{#if getCurrentConfig().storageDatabase}
|
||||
{#if $configValue?.storageDatabase}
|
||||
<FormCheckboxField label="Save to team folder" name="saveToTeamFolder" />
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import _ from 'lodash';
|
||||
import { apiCall } from '../utility/api';
|
||||
import ErrorInfo from '../elements/ErrorInfo.svelte';
|
||||
import { base64ToHex } from 'dbgate-tools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let onConfirm;
|
||||
@@ -113,7 +114,7 @@
|
||||
{
|
||||
fieldName: 'value',
|
||||
header: _t('dataGrid.value', { defaultMessage: 'Value' }),
|
||||
formatter: row => (row.value == null ? '(NULL)' : row.value),
|
||||
formatter: row => (row.value == null ? '(NULL)' : row.value?.$binary?.base64 ? base64ToHex(row.value.$binary.base64) : row.value),
|
||||
},
|
||||
]}
|
||||
>
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
if (force && value?.type == 'Buffer' && _.isArray(value.data)) {
|
||||
return String.fromCharCode.apply(String, value.data);
|
||||
}
|
||||
else if (force && value?.$binary?.base64) {
|
||||
return atob(value.$binary.base64);
|
||||
}
|
||||
return stringifyCellValue(value, 'gridCellIntent').value;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import WidgetsInnerContainer from '../widgets/WidgetsInnerContainer.svelte';
|
||||
import PluginsList from './PluginsList.svelte';
|
||||
import { filterName } from 'dbgate-tools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
let filter = '';
|
||||
// let search = '';
|
||||
@@ -20,7 +21,7 @@
|
||||
</script>
|
||||
|
||||
<SearchBoxWrapper>
|
||||
<SearchInput placeholder="Search extensions on web" {filter} bind:value={filter} />
|
||||
<SearchInput placeholder={_t('plugins.searchExtensionsOnWeb', { defaultMessage: 'Search extensions on web' })} {filter} bind:value={filter} />
|
||||
</SearchBoxWrapper>
|
||||
<WidgetsInnerContainer>
|
||||
{#if $plugins?.errorMessage}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<script lang="ts">
|
||||
import FormCheckboxField from "../forms/FormCheckboxField.svelte";
|
||||
import { _t } from "../translations";
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import FormValues from "../forms/FormValues.svelte";
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<FormValues let:values>
|
||||
<div class="heading">{_t('settings.behaviour', { defaultMessage: 'Behaviour' })}</div>
|
||||
|
||||
<FormCheckboxField
|
||||
name="behaviour.useTabPreviewMode"
|
||||
label={_t('settings.behaviour.useTabPreviewMode', { defaultMessage: 'Use tab preview mode' })}
|
||||
defaultValue={true}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="behaviour.jsonPreviewWrap"
|
||||
label={_t('settings.behaviour.jsonPreviewWrap', { defaultMessage: 'Wrap JSON in preview' })}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
<div class="tip">
|
||||
<FontIcon icon="img tip" />
|
||||
{_t('settings.behaviour.singleClickPreview', {
|
||||
defaultMessage:
|
||||
'When you single-click or select a file in the "Tables, Views, Functions" view, it is shown in a preview mode and reuses an existing tab (preview tab). This is useful if you are quickly browsing tables and don\'t want every visited table to have its own tab. When you start editing the table or use double-click to open the table from the "Tables" view, a new tab is dedicated to that table.',
|
||||
})}
|
||||
</div>
|
||||
|
||||
<FormCheckboxField
|
||||
name="behaviour.openDetailOnArrows"
|
||||
label={_t('settings.behaviour.openDetailOnArrows', {
|
||||
defaultMessage: 'Open detail on keyboard navigation',
|
||||
})}
|
||||
defaultValue={true}
|
||||
disabled={values['behaviour.useTabPreviewMode'] === false}
|
||||
/>
|
||||
|
||||
<div class="heading">{_t('settings.confirmations', { defaultMessage: 'Confirmations' })}</div>
|
||||
|
||||
<FormCheckboxField
|
||||
name="skipConfirm.tableDataSave"
|
||||
label={_t('settings.confirmations.skipConfirm.tableDataSave', {
|
||||
defaultMessage: 'Skip confirmation when saving table data (SQL)',
|
||||
})}
|
||||
/>
|
||||
<FormCheckboxField
|
||||
name="skipConfirm.collectionDataSave"
|
||||
label={_t('settings.confirmations.skipConfirm.collectionDataSave', {
|
||||
defaultMessage: 'Skip confirmation when saving collection data (NoSQL)',
|
||||
})}
|
||||
/>
|
||||
</FormValues>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.heading {
|
||||
font-size: 20px;
|
||||
margin: 5px;
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
|
||||
.tip {
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
</style>
|
||||
@@ -193,7 +193,7 @@
|
||||
|
||||
{#if driver?.showConnectionField('authToken', $values, showConnectionFieldArgs)}
|
||||
<FormTextField
|
||||
label={_t('authToken', { defaultMessage: 'Auth token' })}
|
||||
label={_t('connection.authToken', { defaultMessage: 'Auth token' })}
|
||||
name="authToken"
|
||||
data-testid="ConnectionDriverFields_authToken"
|
||||
disabled={isConnected || disabledFields.includes('authToken')}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<script lang="ts">
|
||||
import CheckboxField from "../forms/CheckboxField.svelte";
|
||||
import FormCheckboxField from "../forms/FormCheckboxField.svelte";
|
||||
import FormFieldTemplateLarge from "../forms/FormFieldTemplateLarge.svelte";
|
||||
import FormSelectField from "../forms/FormSelectField.svelte";
|
||||
import FormTextField from "../forms/FormTextField.svelte";
|
||||
import FormValues from "../forms/FormValues.svelte";
|
||||
import { lockedDatabaseMode } from "../stores";
|
||||
import { _t } from "../translations";
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<FormValues let:values>
|
||||
<div class="heading">{_t('settings.connection', { defaultMessage: 'Connection' })}</div>
|
||||
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.connection.showOnlyTabsFromSelectedDatabase', {
|
||||
defaultMessage: 'Show only tabs from selected database',
|
||||
})}
|
||||
type="checkbox"
|
||||
labelProps={{
|
||||
onClick: () => {
|
||||
$lockedDatabaseMode = !$lockedDatabaseMode;
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CheckboxField checked={$lockedDatabaseMode} on:change={e => ($lockedDatabaseMode = e.target.checked)} />
|
||||
</FormFieldTemplateLarge>
|
||||
|
||||
<FormCheckboxField
|
||||
name="connection.autoRefresh"
|
||||
label={_t('settings.connection.autoRefresh', {
|
||||
defaultMessage: 'Automatic refresh of database model on background',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
<FormTextField
|
||||
name="connection.autoRefreshInterval"
|
||||
label={_t('settings.connection.autoRefreshInterval', {
|
||||
defaultMessage: 'Interval between automatic DB structure reloads in seconds',
|
||||
})}
|
||||
defaultValue="30"
|
||||
disabled={values['connection.autoRefresh'] === false}
|
||||
/>
|
||||
<FormSelectField
|
||||
label={_t('settings.connection.sshBindHost', { defaultMessage: 'Local host address for SSH connections' })}
|
||||
name="connection.sshBindHost"
|
||||
isNative
|
||||
defaultValue="127.0.0.1"
|
||||
options={[
|
||||
{ value: '127.0.0.1', label: '127.0.0.1 (IPv4)' },
|
||||
{ value: '::1', label: '::1 (IPv6)' },
|
||||
{ value: 'localhost', label: 'localhost (domain name)' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<div class="heading">{_t('settings.session', { defaultMessage: 'Query sessions' })}</div>
|
||||
<FormCheckboxField
|
||||
name="session.autoClose"
|
||||
label={_t('settings.session.autoClose', {
|
||||
defaultMessage: 'Automatic close query sessions after period without any activity',
|
||||
})}
|
||||
defaultValue={true}
|
||||
/>
|
||||
<FormTextField
|
||||
name="session.autoCloseTimeout"
|
||||
label={_t('settings.session.autoCloseTimeout', {
|
||||
defaultMessage: 'Interval, after which query session without activity is closed (in minutes)',
|
||||
})}
|
||||
defaultValue="15"
|
||||
disabled={values['session.autoClose'] === false}
|
||||
/>
|
||||
</FormValues>
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
.heading {
|
||||
font-size: 20px;
|
||||
margin: 5px;
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,100 @@
|
||||
<script lang="ts">
|
||||
import FormCheckboxField from "../forms/FormCheckboxField.svelte";
|
||||
import FormSelectField from "../forms/FormSelectField.svelte";
|
||||
import FormTextField from "../forms/FormTextField.svelte";
|
||||
import { _t } from "../translations";
|
||||
import { isProApp } from "../utility/proTools";
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="heading">{_t('settings.dataGrid.title', { defaultMessage: 'Data grid' })}</div>
|
||||
<FormTextField
|
||||
name="dataGrid.pageSize"
|
||||
label={_t('settings.dataGrid.pageSize', {
|
||||
defaultMessage: 'Page size (number of rows for incremental loading, must be between 5 and 50000)',
|
||||
})}
|
||||
defaultValue="100"
|
||||
/>
|
||||
{#if isProApp()}
|
||||
<FormCheckboxField
|
||||
name="dataGrid.showHintColumns"
|
||||
label={_t('settings.dataGrid.showHintColumns', { defaultMessage: 'Show foreign key hints' })}
|
||||
defaultValue={true}
|
||||
/>
|
||||
{/if}
|
||||
<!-- <FormCheckboxField name="dataGrid.showHintColumns" label="Show foreign key hints" defaultValue={true} /> -->
|
||||
|
||||
<FormCheckboxField
|
||||
name="dataGrid.thousandsSeparator"
|
||||
label={_t('settings.dataGrid.thousandsSeparator', {
|
||||
defaultMessage: 'Use thousands separator for numbers',
|
||||
})}
|
||||
/>
|
||||
|
||||
<FormTextField
|
||||
name="dataGrid.defaultAutoRefreshInterval"
|
||||
label={_t('settings.dataGrid.defaultAutoRefreshInterval', {
|
||||
defaultMessage: 'Default grid auto refresh interval in seconds',
|
||||
})}
|
||||
defaultValue="10"
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="dataGrid.alignNumbersRight"
|
||||
label={_t('settings.dataGrid.alignNumbersRight', { defaultMessage: 'Align numbers to right' })}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
<FormTextField
|
||||
name="dataGrid.collectionPageSize"
|
||||
label={_t('settings.dataGrid.collectionPageSize', {
|
||||
defaultMessage: 'Collection page size (for MongoDB JSON view, must be between 5 and 1000)',
|
||||
})}
|
||||
defaultValue="50"
|
||||
/>
|
||||
|
||||
<FormSelectField
|
||||
label={_t('settings.dataGrid.coloringMode', { defaultMessage: 'Row coloring mode' })}
|
||||
name="dataGrid.coloringMode"
|
||||
isNative
|
||||
defaultValue="36"
|
||||
options={[
|
||||
{
|
||||
value: '36',
|
||||
label: _t('settings.dataGrid.coloringMode.36', { defaultMessage: 'Every 3rd and 6th row' }),
|
||||
},
|
||||
{
|
||||
value: '2-primary',
|
||||
label: _t('settings.dataGrid.coloringMode.2-primary', {
|
||||
defaultMessage: 'Every 2-nd row, primary color',
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: '2-secondary',
|
||||
label: _t('settings.dataGrid.coloringMode.2-secondary', {
|
||||
defaultMessage: 'Every 2-nd row, secondary color',
|
||||
}),
|
||||
},
|
||||
{ value: 'none', label: _t('settings.dataGrid.coloringMode.none', { defaultMessage: 'None' }) },
|
||||
]}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="dataGrid.showAllColumnsWhenSearch"
|
||||
label={_t('settings.dataGrid.showAllColumnsWhenSearch', {
|
||||
defaultMessage: 'Show all columns when searching',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.heading {
|
||||
font-size: 20px;
|
||||
margin: 5px;
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,103 @@
|
||||
<script lang="ts">
|
||||
import FormCheckboxField from "../forms/FormCheckboxField.svelte";
|
||||
import FormSelectField from "../forms/FormSelectField.svelte";
|
||||
import FormValues from "../forms/FormValues.svelte";
|
||||
import { _t } from "../translations";
|
||||
import FormDefaultActionField from "./FormDefaultActionField.svelte";
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<FormValues let:values>
|
||||
<div class="heading">{_t('settings.defaultActions', { defaultMessage: 'Default actions' })}</div>
|
||||
|
||||
<FormSelectField
|
||||
label={_t('settings.defaultActions.connectionClick', { defaultMessage: 'Connection click' })}
|
||||
name="defaultAction.connectionClick"
|
||||
isNative
|
||||
defaultValue="connect"
|
||||
options={[
|
||||
{
|
||||
value: 'openDetails',
|
||||
label: _t('settings.defaultActions.connectionClick.openDetails', {
|
||||
defaultMessage: 'Edit / open details',
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: 'connect',
|
||||
label: _t('settings.defaultActions.connectionClick.connect', { defaultMessage: 'Connect' }),
|
||||
},
|
||||
{
|
||||
value: 'none',
|
||||
label: _t('settings.defaultActions.connectionClick.none', { defaultMessage: 'Do nothing' }),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<FormSelectField
|
||||
label={_t('settings.defaultActions.databaseClick', { defaultMessage: 'Database click' })}
|
||||
name="defaultAction.databaseClick"
|
||||
isNative
|
||||
defaultValue="switch"
|
||||
options={[
|
||||
{
|
||||
value: 'switch',
|
||||
label: _t('settings.defaultActions.databaseClick.switch', { defaultMessage: 'Switch database' }),
|
||||
},
|
||||
{
|
||||
value: 'none',
|
||||
label: _t('settings.defaultActions.databaseClick.none', { defaultMessage: 'Do nothing' }),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="defaultAction.useLastUsedAction"
|
||||
label={_t('settings.defaultActions.useLastUsedAction', { defaultMessage: 'Use last used action' })}
|
||||
defaultValue={true}
|
||||
/>
|
||||
|
||||
<FormDefaultActionField
|
||||
label={_t('settings.defaultActions.tableClick', { defaultMessage: 'Table click' })}
|
||||
objectTypeField="tables"
|
||||
disabled={values['defaultAction.useLastUsedAction'] !== false}
|
||||
/>
|
||||
<FormDefaultActionField
|
||||
label={_t('settings.defaultActions.viewClick', { defaultMessage: 'View click' })}
|
||||
objectTypeField="views"
|
||||
disabled={values['defaultAction.useLastUsedAction'] !== false}
|
||||
/>
|
||||
<FormDefaultActionField
|
||||
label={_t('settings.defaultActions.materializedViewClick', { defaultMessage: 'Materialized view click' })}
|
||||
objectTypeField="matviews"
|
||||
disabled={values['defaultAction.useLastUsedAction'] !== false}
|
||||
/>
|
||||
<FormDefaultActionField
|
||||
label={_t('settings.defaultActions.procedureClick', { defaultMessage: 'Procedure click' })}
|
||||
objectTypeField="procedures"
|
||||
disabled={values['defaultAction.useLastUsedAction'] !== false}
|
||||
/>
|
||||
<FormDefaultActionField
|
||||
label={_t('settings.defaultActions.functionClick', { defaultMessage: 'Function click' })}
|
||||
objectTypeField="functions"
|
||||
disabled={values['defaultAction.useLastUsedAction'] !== false}
|
||||
/>
|
||||
<FormDefaultActionField
|
||||
label={_t('settings.defaultActions.collectionClick', { defaultMessage: 'NoSQL collection click' })}
|
||||
objectTypeField="collections"
|
||||
disabled={values['defaultAction.useLastUsedAction'] !== false}
|
||||
/>
|
||||
</FormValues>
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
.heading {
|
||||
font-size: 20px;
|
||||
margin: 5px;
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,50 @@
|
||||
<script lang="ts">
|
||||
import FormTextField from "../forms/FormTextField.svelte";
|
||||
import { _t } from "../translations";
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="heading">{_t('settings.externalTools', { defaultMessage: 'External tools' })}</div>
|
||||
<FormTextField
|
||||
name="externalTools.mysqldump"
|
||||
label={_t('settings.other.externalTools.mysqldump', {
|
||||
defaultMessage: 'mysqldump (backup MySQL database)',
|
||||
})}
|
||||
defaultValue="mysqldump"
|
||||
/>
|
||||
<FormTextField
|
||||
name="externalTools.mysql"
|
||||
label={_t('settings.other.externalTools.mysql', { defaultMessage: 'mysql (restore MySQL database)' })}
|
||||
defaultValue="mysql"
|
||||
/>
|
||||
<FormTextField
|
||||
name="externalTools.mysqlPlugins"
|
||||
label={_t('settings.other.externalTools.mysqlPlugins', {
|
||||
defaultMessage:
|
||||
'Folder with mysql plugins (for example for authentication). Set only in case of problems',
|
||||
})}
|
||||
defaultValue=""
|
||||
/>
|
||||
<FormTextField
|
||||
name="externalTools.pg_dump"
|
||||
label={_t('settings.other.externalTools.pg_dump', {
|
||||
defaultMessage: 'pg_dump (backup PostgreSQL database)',
|
||||
})}
|
||||
defaultValue="pg_dump"
|
||||
/>
|
||||
<FormTextField
|
||||
name="externalTools.psql"
|
||||
label={_t('settings.other.externalTools.psql', { defaultMessage: 'psql (restore PostgreSQL database)' })}
|
||||
defaultValue="psql"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.heading {
|
||||
font-size: 20px;
|
||||
margin: 5px;
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -4,6 +4,7 @@
|
||||
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||
import SelectField from '../forms/SelectField.svelte';
|
||||
import { lastUsedDefaultActions } from '../stores';
|
||||
import { _tval } from '../translations';
|
||||
|
||||
export let label;
|
||||
export let objectTypeField;
|
||||
@@ -18,7 +19,7 @@
|
||||
defaultValue={defaultDatabaseObjectAppObjectActions[objectTypeField][0]?.defaultActionId}
|
||||
options={defaultDatabaseObjectAppObjectActions[objectTypeField].map(x => ({
|
||||
value: x.defaultActionId,
|
||||
label: x.label,
|
||||
label: _tval(x.label),
|
||||
}))}
|
||||
value={$lastUsedDefaultActions[objectTypeField]}
|
||||
on:change={e => {
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
<script lang="ts">
|
||||
import { internalRedirectTo } from '../clientAuth';
|
||||
import CheckboxField from '../forms/CheckboxField.svelte';
|
||||
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
|
||||
import FormFieldTemplateLarge from '../forms/FormFieldTemplateLarge.svelte';
|
||||
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||
import FormTextField from '../forms/FormTextField.svelte';
|
||||
import SelectField from '../forms/SelectField.svelte';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import { EDITOR_KEYBINDINGS_MODES } from '../query/AceEditor.svelte';
|
||||
import { currentEditorKeybindigMode, currentEditorWrapEnabled } from '../stores';
|
||||
import { _t, getSelectedLanguage, setSelectedLanguage } from '../translations';
|
||||
import { isMac } from '../utility/common';
|
||||
import getElectron from '../utility/getElectron';
|
||||
import { isProApp } from '../utility/proTools';
|
||||
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
||||
|
||||
const electron = getElectron();
|
||||
let restartWarning = false;
|
||||
</script>
|
||||
<div class="wrapper">
|
||||
<div class="heading">{_t('settings.general', { defaultMessage: 'General' })}</div>
|
||||
{#if electron}
|
||||
<div class="heading">{_t('settings.appearance', { defaultMessage: 'Appearance' })}</div>
|
||||
<FormCheckboxField
|
||||
name="app.useNativeMenu"
|
||||
label={isMac()
|
||||
? _t('settings.useNativeWindowTitle', { defaultMessage: 'Use native window title' })
|
||||
: _t('settings.useSystemNativeMenu', { defaultMessage: 'Use system native menu' })}
|
||||
on:change={() => {
|
||||
restartWarning = true;
|
||||
}}
|
||||
/>
|
||||
{#if restartWarning}
|
||||
<div class="ml-5 mb-3">
|
||||
<FontIcon icon="img warn" />
|
||||
{_t('settings.nativeMenuRestartWarning', {
|
||||
defaultMessage: 'Native menu settings will be applied after app restart',
|
||||
})}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<FormCheckboxField
|
||||
name="tabGroup.showServerName"
|
||||
label={_t('settings.tabGroup.showServerName', {
|
||||
defaultMessage: 'Show server name alongside database name in title of the tab group',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
<div class="heading">{_t('settings.localization', { defaultMessage: 'Localization' })}</div>
|
||||
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.localization.language', { defaultMessage: 'Language' })}
|
||||
type="combo"
|
||||
>
|
||||
<SelectField
|
||||
isNative
|
||||
data-testid="SettingsModal_languageSelect"
|
||||
options={[
|
||||
{ value: 'cs', label: 'Čeština' },
|
||||
{ value: 'de', label: 'Deutsch' },
|
||||
{ value: 'en', label: 'English' },
|
||||
{ value: 'es', label: 'Español' },
|
||||
{ value: 'fr', label: 'Français' },
|
||||
{ value: 'it', label: 'Italiano' },
|
||||
{ value: 'pt', label: 'Português (Brasil)' },
|
||||
{ value: 'sk', label: 'Slovenčina' },
|
||||
{ value: 'ja', label: '日本語' },
|
||||
{ value: 'zh', label: '中文' },
|
||||
]}
|
||||
defaultValue={getSelectedLanguage()}
|
||||
value={getSelectedLanguage()}
|
||||
on:change={e => {
|
||||
setSelectedLanguage(e.detail);
|
||||
showModal(ConfirmModal, {
|
||||
message: _t('settings.localization.reloadWarning', {
|
||||
defaultMessage: 'Application will be reloaded to apply new language settings',
|
||||
}),
|
||||
onConfirm: () => {
|
||||
setTimeout(() => {
|
||||
internalRedirectTo(electron ? '/index.html' : '/');
|
||||
}, 100);
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.heading {
|
||||
font-size: 20px;
|
||||
margin: 5px;
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,91 @@
|
||||
<script lang="ts">
|
||||
import { safeFormatDate } from "dbgate-tools";
|
||||
import FormStyledButton from "../buttons/FormStyledButton.svelte";
|
||||
import FormTextAreaField from "../forms/FormTextAreaField.svelte";
|
||||
import FontIcon from "../icons/FontIcon.svelte";
|
||||
import { _t } from "../translations";
|
||||
import { apiCall } from "../utility/api";
|
||||
import { useSettings } from "../utility/metadataLoaders";
|
||||
import { derived } from "svelte/store";
|
||||
|
||||
const settings = useSettings();
|
||||
const settingsValues = derived(settings, $settings => {
|
||||
if (!$settings) {
|
||||
return {};
|
||||
}
|
||||
return $settings;
|
||||
});
|
||||
let licenseKeyCheckResult = null;
|
||||
|
||||
$: licenseKey = $settingsValues['other.licenseKey'];
|
||||
|
||||
</script>
|
||||
|
||||
<div class="heading">{_t('settings.other.license', { defaultMessage: 'License' })}</div>
|
||||
<FormTextAreaField
|
||||
name="other.licenseKey"
|
||||
label={_t('settings.other.licenseKey', { defaultMessage: 'License key' })}
|
||||
rows={7}
|
||||
onChange={async value => {
|
||||
licenseKeyCheckResult = await apiCall('config/check-license', { licenseKey: value });
|
||||
}}
|
||||
/>
|
||||
{#if licenseKeyCheckResult}
|
||||
<div class="m-3 ml-5">
|
||||
{#if licenseKeyCheckResult.status == 'ok'}
|
||||
<div>
|
||||
<FontIcon icon="img ok" />
|
||||
{_t('settings.other.licenseKey.valid', { defaultMessage: 'License key is valid' })}
|
||||
</div>
|
||||
{#if licenseKeyCheckResult.validTo}
|
||||
<div>
|
||||
{_t('settings.other.licenseKey.validTo', { defaultMessage: 'License valid to:' })}
|
||||
{licenseKeyCheckResult.validTo}
|
||||
</div>
|
||||
{/if}
|
||||
{#if licenseKeyCheckResult.expiration}
|
||||
<div>
|
||||
{_t('settings.other.licenseKey.expiration', { defaultMessage: 'License key expiration:' })}
|
||||
<b>{safeFormatDate(licenseKeyCheckResult.expiration)}</b>
|
||||
</div>
|
||||
{/if}
|
||||
{:else if licenseKeyCheckResult.status == 'error'}
|
||||
<div>
|
||||
<FontIcon icon="img error" />
|
||||
{licenseKeyCheckResult.errorMessage ??
|
||||
_t('settings.other.licenseKey.invalid', { defaultMessage: 'License key is invalid' })}
|
||||
{#if licenseKeyCheckResult.expiration}
|
||||
<div>
|
||||
{_t('settings.other.licenseKey.expiration', { defaultMessage: 'License key expiration:' })}
|
||||
<b>{safeFormatDate(licenseKeyCheckResult.expiration)}</b>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if licenseKeyCheckResult.isExpired}
|
||||
<div class="mt-2">
|
||||
<FormStyledButton
|
||||
value={_t('settings.other.licenseKey.checkForNew', {
|
||||
defaultMessage: 'Check for new license key',
|
||||
})}
|
||||
skipWidth
|
||||
on:click={async () => {
|
||||
licenseKeyCheckResult = await apiCall('config/get-new-license', { oldLicenseKey: licenseKey });
|
||||
if (licenseKeyCheckResult.licenseKey) {
|
||||
apiCall('config/update-settings', { 'other.licenseKey': licenseKeyCheckResult.licenseKey });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.heading {
|
||||
font-size: 20px;
|
||||
margin: 5px;
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,64 @@
|
||||
<script lang="ts">
|
||||
import FormCheckboxField from "../forms/FormCheckboxField.svelte";
|
||||
import FormSelectField from "../forms/FormSelectField.svelte";
|
||||
import FormTextField from "../forms/FormTextField.svelte";
|
||||
import { _t } from "../translations";
|
||||
import { isProApp } from "../utility/proTools";
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="heading">{_t('settings.other', { defaultMessage: 'Other' })}</div>
|
||||
|
||||
<FormTextField
|
||||
name="other.gistCreateToken"
|
||||
label={_t('settings.other.gistCreateToken', { defaultMessage: 'API token for creating error gists' })}
|
||||
defaultValue=""
|
||||
/>
|
||||
|
||||
<FormSelectField
|
||||
label={_t('settings.other.autoUpdateApplication', { defaultMessage: 'Auto update application' })}
|
||||
name="app.autoUpdateMode"
|
||||
isNative
|
||||
defaultValue=""
|
||||
options={[
|
||||
{
|
||||
value: 'skip',
|
||||
label: _t('settings.other.autoUpdateApplication.skip', {
|
||||
defaultMessage: 'Do not check for new versions',
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
label: _t('settings.other.autoUpdateApplication.check', { defaultMessage: 'Check for new versions' }),
|
||||
},
|
||||
{
|
||||
value: 'download',
|
||||
label: _t('settings.other.autoUpdateApplication.download', {
|
||||
defaultMessage: 'Check and download new versions',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{#if isProApp()}
|
||||
<FormCheckboxField
|
||||
name="ai.allowSendModels"
|
||||
label={_t('settings.other.ai.allowSendModels', {
|
||||
defaultMessage: 'Allow to send DB models and query snippets to AI service',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
.heading {
|
||||
font-size: 20px;
|
||||
margin: 5px;
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,100 @@
|
||||
<script lang="ts">
|
||||
import CheckboxField from "../forms/CheckboxField.svelte";
|
||||
import FormCheckboxField from "../forms/FormCheckboxField.svelte";
|
||||
import FormFieldTemplateLarge from "../forms/FormFieldTemplateLarge.svelte";
|
||||
import FormSelectField from "../forms/FormSelectField.svelte";
|
||||
import FormTextField from "../forms/FormTextField.svelte";
|
||||
import SelectField from "../forms/SelectField.svelte";
|
||||
import { EDITOR_KEYBINDINGS_MODES } from "../query/AceEditor.svelte";
|
||||
import { currentEditorKeybindigMode, currentEditorWrapEnabled } from "../stores";
|
||||
import { _t } from "../translations";
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="heading">{_t('settings.sqlEditor', { defaultMessage: 'SQL editor' })}</div>
|
||||
|
||||
<div class="flex">
|
||||
<div class="col-3">
|
||||
<FormSelectField
|
||||
label={_t('settings.sqlEditor.sqlCommandsCase', { defaultMessage: 'SQL commands case' })}
|
||||
name="sqlEditor.sqlCommandsCase"
|
||||
isNative
|
||||
defaultValue="upperCase"
|
||||
options={[
|
||||
{ value: 'upperCase', label: 'UPPER CASE' },
|
||||
{ value: 'lowerCase', label: 'lower case' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.editor.keybinds', { defaultMessage: 'Editor keybinds' })}
|
||||
type="combo"
|
||||
>
|
||||
<SelectField
|
||||
isNative
|
||||
defaultValue="default"
|
||||
options={EDITOR_KEYBINDINGS_MODES.map(mode => ({ label: mode.label, value: mode.value }))}
|
||||
value={$currentEditorKeybindigMode}
|
||||
on:change={e => ($currentEditorKeybindigMode = e.detail)}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.editor.wordWrap', { defaultMessage: 'Enable word wrap' })}
|
||||
type="combo"
|
||||
>
|
||||
<CheckboxField
|
||||
checked={$currentEditorWrapEnabled}
|
||||
on:change={e => ($currentEditorWrapEnabled = e.target.checked)}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FormTextField
|
||||
name="sqlEditor.limitRows"
|
||||
label={_t('settings.sqlEditor.limitRows', { defaultMessage: 'Return only N rows from query' })}
|
||||
placeholder={_t('settings.sqlEditor.limitRowsPlaceholder', { defaultMessage: '(No rows limit)' })}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="sqlEditor.showTableAliasesInCodeCompletion"
|
||||
label={_t('settings.sqlEditor.showTableAliasesInCodeCompletion', {
|
||||
defaultMessage: 'Show table aliases in code completion',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="sqlEditor.disableSplitByEmptyLine"
|
||||
label={_t('settings.sqlEditor.disableSplitByEmptyLine', { defaultMessage: 'Disable split by empty line' })}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="sqlEditor.disableExecuteCurrentLine"
|
||||
label={_t('settings.sqlEditor.disableExecuteCurrentLine', {
|
||||
defaultMessage: 'Disable current line execution (Execute current)',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="sqlEditor.hideColumnsPanel"
|
||||
label={_t('settings.sqlEditor.hideColumnsPanel', { defaultMessage: 'Hide Columns/Filters panel by default' })}
|
||||
defaultValue={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.heading {
|
||||
font-size: 20px;
|
||||
margin: 5px;
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
</style>
|
||||
@@ -42,7 +42,7 @@
|
||||
import { safeFormatDate } from 'dbgate-tools';
|
||||
import FormDefaultActionField from './FormDefaultActionField.svelte';
|
||||
import AiSettingsTab from './AiSettingsTab.svelte';
|
||||
import { _t } from '../translations';
|
||||
import { _t, setSelectedLanguage } from '../translations';
|
||||
import hasPermission from '../utility/hasPermission';
|
||||
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
@@ -110,14 +110,43 @@ ORDER BY
|
||||
maxHeight100
|
||||
flex1
|
||||
tabs={[
|
||||
hasPermission('settings/change') && { identifier: 'general', label: _t('settings.general', { defaultMessage: 'General' }), slot: 1 },
|
||||
isProApp() && electron && { identifier: 'license', label: _t('settings.license', { defaultMessage: 'License' }), slot: 7 },
|
||||
hasPermission('settings/change') && { identifier: 'connection', label: _t('settings.connection', { defaultMessage: 'Connection' }), slot: 2 },
|
||||
hasPermission('settings/change') && {
|
||||
identifier: 'general',
|
||||
label: _t('settings.general', { defaultMessage: 'General' }),
|
||||
slot: 1,
|
||||
},
|
||||
isProApp() &&
|
||||
electron && {
|
||||
identifier: 'license',
|
||||
label: _t('settings.license', { defaultMessage: 'License' }),
|
||||
slot: 7,
|
||||
},
|
||||
hasPermission('settings/change') && {
|
||||
identifier: 'connection',
|
||||
label: _t('settings.connection', { defaultMessage: 'Connection' }),
|
||||
slot: 2,
|
||||
},
|
||||
{ identifier: 'theme', label: _t('settings.theme', { defaultMessage: 'Themes' }), slot: 3 },
|
||||
hasPermission('settings/change') && { identifier: 'default-actions', label: _t('settings.defaultActions', { defaultMessage: 'Default Actions' }), slot: 4 },
|
||||
hasPermission('settings/change') && { identifier: 'behaviour', label: _t('settings.behaviour', { defaultMessage: 'Behaviour' }), slot: 5 },
|
||||
hasPermission('settings/change') && { identifier: 'external-tools', label: _t('settings.externalTools', { defaultMessage: 'External tools' }), slot: 8 },
|
||||
hasPermission('settings/change') && { identifier: 'other', label: _t('settings.other', { defaultMessage: 'Other' }), slot: 6 },
|
||||
hasPermission('settings/change') && {
|
||||
identifier: 'default-actions',
|
||||
label: _t('settings.defaultActions', { defaultMessage: 'Default Actions' }),
|
||||
slot: 4,
|
||||
},
|
||||
hasPermission('settings/change') && {
|
||||
identifier: 'behaviour',
|
||||
label: _t('settings.behaviour', { defaultMessage: 'Behaviour' }),
|
||||
slot: 5,
|
||||
},
|
||||
hasPermission('settings/change') && {
|
||||
identifier: 'external-tools',
|
||||
label: _t('settings.externalTools', { defaultMessage: 'External tools' }),
|
||||
slot: 8,
|
||||
},
|
||||
hasPermission('settings/change') && {
|
||||
identifier: 'other',
|
||||
label: _t('settings.other', { defaultMessage: 'Other' }),
|
||||
slot: 6,
|
||||
},
|
||||
isProApp() && hasPermission('settings/change') && { identifier: 'ai', label: 'AI', slot: 9 },
|
||||
]}
|
||||
>
|
||||
@@ -126,68 +155,112 @@ ORDER BY
|
||||
<div class="heading">{_t('settings.appearance', { defaultMessage: 'Appearance' })}</div>
|
||||
<FormCheckboxField
|
||||
name="app.useNativeMenu"
|
||||
label={isMac() ? 'Use native window title' : 'Use system native menu'}
|
||||
label={isMac()
|
||||
? _t('settings.useNativeWindowTitle', { defaultMessage: 'Use native window title' })
|
||||
: _t('settings.useSystemNativeMenu', { defaultMessage: 'Use system native menu' })}
|
||||
on:change={() => {
|
||||
restartWarning = true;
|
||||
}}
|
||||
/>
|
||||
{#if restartWarning}
|
||||
<div class="ml-5 mb-3">
|
||||
<FontIcon icon="img warn" /> {_t('settings.nativeMenuRestartWarning', { defaultMessage: 'Native menu settings will be applied after app restart' })}
|
||||
<FontIcon icon="img warn" />
|
||||
{_t('settings.nativeMenuRestartWarning', {
|
||||
defaultMessage: 'Native menu settings will be applied after app restart',
|
||||
})}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<FormCheckboxField
|
||||
name="tabGroup.showServerName"
|
||||
label={_t('settings.tabGroup.showServerName', { defaultMessage: 'Show server name alongside database name in title of the tab group' })}
|
||||
label={_t('settings.tabGroup.showServerName', {
|
||||
defaultMessage: 'Show server name alongside database name in title of the tab group',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
<div class="heading">{_t('settings.localization', { defaultMessage: 'Localization' })}</div>
|
||||
<FormSelectField
|
||||
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.localization.language', { defaultMessage: 'Language' })}
|
||||
name="localization.language"
|
||||
defaultValue={getSelectedLanguage()}
|
||||
isNative
|
||||
options={[
|
||||
{ value: 'en', label: 'English' },
|
||||
{ value: 'cs', label: 'Čeština' },
|
||||
{ value: 'sk', label: 'Slovenčina' },
|
||||
]}
|
||||
on:change={() => {
|
||||
showModal(ConfirmModal, {
|
||||
message: _t('settings.localization.reloadWarning', { defaultMessage: 'Application will be reloaded to apply new language settings' }),
|
||||
onConfirm: () => {
|
||||
setTimeout(() => {
|
||||
internalRedirectTo('/');
|
||||
}, 100);
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
type="combo"
|
||||
>
|
||||
<SelectField
|
||||
isNative
|
||||
data-testid="SettingsModal_languageSelect"
|
||||
options={[
|
||||
{ value: 'cs', label: 'Čeština' },
|
||||
{ value: 'de', label: 'Deutsch' },
|
||||
{ value: 'en', label: 'English' },
|
||||
{ value: 'es', label: 'Español' },
|
||||
{ value: 'fr', label: 'Français' },
|
||||
{ value: 'it', label: 'Italiano' },
|
||||
{ value: 'pt', label: 'Português (Brasil)' },
|
||||
{ value: 'sk', label: 'Slovenčina' },
|
||||
{ value: 'ja', label: '日本語' },
|
||||
{ value: 'zh', label: '中文' },
|
||||
]}
|
||||
defaultValue={getSelectedLanguage()}
|
||||
value={getSelectedLanguage()}
|
||||
on:change={e => {
|
||||
setSelectedLanguage(e.detail);
|
||||
showModal(ConfirmModal, {
|
||||
message: _t('settings.localization.reloadWarning', {
|
||||
defaultMessage: 'Application will be reloaded to apply new language settings',
|
||||
}),
|
||||
onConfirm: () => {
|
||||
setTimeout(() => {
|
||||
internalRedirectTo(electron ? '/index.html' : '/');
|
||||
}, 100);
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
|
||||
<div class="heading">{_t('settings.dataGrid.title', { defaultMessage: 'Data grid' })}</div>
|
||||
<FormTextField
|
||||
name="dataGrid.pageSize"
|
||||
label={_t('settings.dataGrid.pageSize', { defaultMessage: 'Page size (number of rows for incremental loading, must be between 5 and 50000)' })}
|
||||
label={_t('settings.dataGrid.pageSize', {
|
||||
defaultMessage: 'Page size (number of rows for incremental loading, must be between 5 and 50000)',
|
||||
})}
|
||||
defaultValue="100"
|
||||
/>
|
||||
<FormCheckboxField name="dataGrid.showHintColumns" label={_t('settings.dataGrid.showHintColumns', { defaultMessage: 'Show foreign key hints' })} defaultValue={true} />
|
||||
{#if isProApp()}
|
||||
<FormCheckboxField
|
||||
name="dataGrid.showHintColumns"
|
||||
label={_t('settings.dataGrid.showHintColumns', { defaultMessage: 'Show foreign key hints' })}
|
||||
defaultValue={true}
|
||||
/>
|
||||
{/if}
|
||||
<!-- <FormCheckboxField name="dataGrid.showHintColumns" label="Show foreign key hints" defaultValue={true} /> -->
|
||||
|
||||
<FormCheckboxField name="dataGrid.thousandsSeparator" label={_t('settings.dataGrid.thousandsSeparator', { defaultMessage: 'Use thousands separator for numbers' })} />
|
||||
<FormCheckboxField
|
||||
name="dataGrid.thousandsSeparator"
|
||||
label={_t('settings.dataGrid.thousandsSeparator', {
|
||||
defaultMessage: 'Use thousands separator for numbers',
|
||||
})}
|
||||
/>
|
||||
|
||||
<FormTextField
|
||||
name="dataGrid.defaultAutoRefreshInterval"
|
||||
label={_t('settings.dataGrid.defaultAutoRefreshInterval', { defaultMessage: 'Default grid auto refresh interval in seconds' })}
|
||||
label={_t('settings.dataGrid.defaultAutoRefreshInterval', {
|
||||
defaultMessage: 'Default grid auto refresh interval in seconds',
|
||||
})}
|
||||
defaultValue="10"
|
||||
/>
|
||||
|
||||
<FormCheckboxField name="dataGrid.alignNumbersRight" label={_t('settings.dataGrid.alignNumbersRight', { defaultMessage: 'Align numbers to right' })} defaultValue={false} />
|
||||
<FormCheckboxField
|
||||
name="dataGrid.alignNumbersRight"
|
||||
label={_t('settings.dataGrid.alignNumbersRight', { defaultMessage: 'Align numbers to right' })}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
<FormTextField
|
||||
name="dataGrid.collectionPageSize"
|
||||
label={_t('settings.dataGrid.collectionPageSize', { defaultMessage: 'Collection page size (for MongoDB JSON view, must be between 5 and 1000)' })}
|
||||
label={_t('settings.dataGrid.collectionPageSize', {
|
||||
defaultMessage: 'Collection page size (for MongoDB JSON view, must be between 5 and 1000)',
|
||||
})}
|
||||
defaultValue="50"
|
||||
/>
|
||||
|
||||
@@ -197,16 +270,31 @@ ORDER BY
|
||||
isNative
|
||||
defaultValue="36"
|
||||
options={[
|
||||
{ value: '36', label: _t('settings.dataGrid.coloringMode.36', { defaultMessage: 'Every 3rd and 6th row' }) },
|
||||
{ value: '2-primary', label: _t('settings.dataGrid.coloringMode.2-primary', { defaultMessage: 'Every 2-nd row, primary color' }) },
|
||||
{ value: '2-secondary', label: _t('settings.dataGrid.coloringMode.2-secondary', { defaultMessage: 'Every 2-nd row, secondary color' }) },
|
||||
{
|
||||
value: '36',
|
||||
label: _t('settings.dataGrid.coloringMode.36', { defaultMessage: 'Every 3rd and 6th row' }),
|
||||
},
|
||||
{
|
||||
value: '2-primary',
|
||||
label: _t('settings.dataGrid.coloringMode.2-primary', {
|
||||
defaultMessage: 'Every 2-nd row, primary color',
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: '2-secondary',
|
||||
label: _t('settings.dataGrid.coloringMode.2-secondary', {
|
||||
defaultMessage: 'Every 2-nd row, secondary color',
|
||||
}),
|
||||
},
|
||||
{ value: 'none', label: _t('settings.dataGrid.coloringMode.none', { defaultMessage: 'None' }) },
|
||||
]}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="dataGrid.showAllColumnsWhenSearch"
|
||||
label={_t('settings.dataGrid.showAllColumnsWhenSearch', { defaultMessage: 'Show all columns when searching' })}
|
||||
label={_t('settings.dataGrid.showAllColumnsWhenSearch', {
|
||||
defaultMessage: 'Show all columns when searching',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
@@ -226,7 +314,10 @@ ORDER BY
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<FormFieldTemplateLarge label={_t('settings.editor.keybinds', { defaultMessage: 'Editor keybinds' })} type="combo">
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.editor.keybinds', { defaultMessage: 'Editor keybinds' })}
|
||||
type="combo"
|
||||
>
|
||||
<SelectField
|
||||
isNative
|
||||
defaultValue="default"
|
||||
@@ -237,7 +328,10 @@ ORDER BY
|
||||
</FormFieldTemplateLarge>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<FormFieldTemplateLarge label={_t('settings.editor.wordWrap', { defaultMessage: 'Enable word wrap' })} type="combo">
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.editor.wordWrap', { defaultMessage: 'Enable word wrap' })}
|
||||
type="combo"
|
||||
>
|
||||
<CheckboxField
|
||||
checked={$currentEditorWrapEnabled}
|
||||
on:change={e => ($currentEditorWrapEnabled = e.target.checked)}
|
||||
@@ -254,7 +348,9 @@ ORDER BY
|
||||
|
||||
<FormCheckboxField
|
||||
name="sqlEditor.showTableAliasesInCodeCompletion"
|
||||
label={_t('settings.sqlEditor.showTableAliasesInCodeCompletion', { defaultMessage: 'Show table aliases in code completion' })}
|
||||
label={_t('settings.sqlEditor.showTableAliasesInCodeCompletion', {
|
||||
defaultMessage: 'Show table aliases in code completion',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
@@ -266,15 +362,26 @@ ORDER BY
|
||||
|
||||
<FormCheckboxField
|
||||
name="sqlEditor.disableExecuteCurrentLine"
|
||||
label={_t('settings.sqlEditor.disableExecuteCurrentLine', { defaultMessage: 'Disable current line execution (Execute current)' })}
|
||||
label={_t('settings.sqlEditor.disableExecuteCurrentLine', {
|
||||
defaultMessage: 'Disable current line execution (Execute current)',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="sqlEditor.hideColumnsPanel"
|
||||
label={_t('settings.sqlEditor.hideColumnsPanel', { defaultMessage: 'Hide Columns/Filters panel by default' })}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="2">
|
||||
<div class="heading">{_t('settings.connection', { defaultMessage: 'Connection' })}</div>
|
||||
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.connection.showOnlyTabsFromSelectedDatabase', { defaultMessage: 'Show only tabs from selected database' })}
|
||||
label={_t('settings.connection.showOnlyTabsFromSelectedDatabase', {
|
||||
defaultMessage: 'Show only tabs from selected database',
|
||||
})}
|
||||
type="checkbox"
|
||||
labelProps={{
|
||||
onClick: () => {
|
||||
@@ -287,12 +394,16 @@ ORDER BY
|
||||
|
||||
<FormCheckboxField
|
||||
name="connection.autoRefresh"
|
||||
label={_t('settings.connection.autoRefresh', { defaultMessage: 'Automatic refresh of database model on background' })}
|
||||
label={_t('settings.connection.autoRefresh', {
|
||||
defaultMessage: 'Automatic refresh of database model on background',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
<FormTextField
|
||||
name="connection.autoRefreshInterval"
|
||||
label={_t('settings.connection.autoRefreshInterval', { defaultMessage: 'Interval between automatic DB structure reloads in seconds' })}
|
||||
label={_t('settings.connection.autoRefreshInterval', {
|
||||
defaultMessage: 'Interval between automatic DB structure reloads in seconds',
|
||||
})}
|
||||
defaultValue="30"
|
||||
disabled={values['connection.autoRefresh'] === false}
|
||||
/>
|
||||
@@ -311,12 +422,16 @@ ORDER BY
|
||||
<div class="heading">{_t('settings.session', { defaultMessage: 'Query sessions' })}</div>
|
||||
<FormCheckboxField
|
||||
name="session.autoClose"
|
||||
label={_t('settings.session.autoClose', { defaultMessage: 'Automatic close query sessions after period without any activity' })}
|
||||
label={_t('settings.session.autoClose', {
|
||||
defaultMessage: 'Automatic close query sessions after period without any activity',
|
||||
})}
|
||||
defaultValue={true}
|
||||
/>
|
||||
<FormTextField
|
||||
name="session.autoCloseTimeout"
|
||||
label={_t('settings.session.autoCloseTimeout', { defaultMessage: 'Interval, after which query session without activity is closed (in minutes)' })}
|
||||
label={_t('settings.session.autoCloseTimeout', {
|
||||
defaultMessage: 'Interval, after which query session without activity is closed (in minutes)',
|
||||
})}
|
||||
defaultValue="15"
|
||||
disabled={values['session.autoClose'] === false}
|
||||
/>
|
||||
@@ -357,16 +472,23 @@ ORDER BY
|
||||
</div>
|
||||
|
||||
<div class="m-5">
|
||||
{_t('settings.appearance.moreThemes', { defaultMessage: 'More themes are available as' })} <Link onClick={openThemePlugins}>plugins</Link>
|
||||
{_t('settings.appearance.moreThemes', { defaultMessage: 'More themes are available as' })}
|
||||
<Link onClick={openThemePlugins}>plugins</Link>
|
||||
<br />
|
||||
{_t('settings.appearance.afterInstalling', { defaultMessage: 'After installing theme plugin (try search "theme" in available extensions) new themes will be available here.' })}
|
||||
{_t('settings.appearance.afterInstalling', {
|
||||
defaultMessage:
|
||||
'After installing theme plugin (try search "theme" in available extensions) new themes will be available here.',
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div class="heading">{_t('settings.appearance.editorTheme', { defaultMessage: 'Editor theme' })}</div>
|
||||
|
||||
<div class="flex">
|
||||
<div class="col-3">
|
||||
<FormFieldTemplateLarge label={_t('settings.appearance.editorTheme', { defaultMessage: 'Theme' })} type="combo">
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.appearance.editorTheme', { defaultMessage: 'Theme' })}
|
||||
type="combo"
|
||||
>
|
||||
<SelectField
|
||||
isNative
|
||||
notSelected={_t('settings.appearance.editorTheme.default', { defaultMessage: '(use theme default)' })}
|
||||
@@ -378,7 +500,10 @@ ORDER BY
|
||||
</div>
|
||||
|
||||
<div class="col-3">
|
||||
<FormFieldTemplateLarge label={_t('settings.appearance.fontSize', { defaultMessage: 'Font size' })} type="combo">
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.appearance.fontSize', { defaultMessage: 'Font size' })}
|
||||
type="combo"
|
||||
>
|
||||
<SelectField
|
||||
isNative
|
||||
notSelected="(default)"
|
||||
@@ -390,7 +515,10 @@ ORDER BY
|
||||
</div>
|
||||
|
||||
<div class="col-3">
|
||||
<FormFieldTemplateLarge label={_t('settings.appearance.customSize', { defaultMessage: 'Custom size' })} type="text">
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.appearance.customSize', { defaultMessage: 'Custom size' })}
|
||||
type="text"
|
||||
>
|
||||
<TextField
|
||||
value={$currentEditorFontSize == 'custom' ? '' : $currentEditorFontSize}
|
||||
on:change={e => ($currentEditorFontSize = e.target['value'])}
|
||||
@@ -401,7 +529,10 @@ ORDER BY
|
||||
</div>
|
||||
|
||||
<div class="col-3">
|
||||
<FormTextField name="editor.fontFamily" label={_t('settings.appearance.fontFamily', { defaultMessage: 'Editor font family' })} />
|
||||
<FormTextField
|
||||
name="editor.fontFamily"
|
||||
label={_t('settings.appearance.fontFamily', { defaultMessage: 'Editor font family' })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -418,9 +549,20 @@ ORDER BY
|
||||
isNative
|
||||
defaultValue="connect"
|
||||
options={[
|
||||
{ value: 'openDetails', label: _t('settings.defaultActions.connectionClick.openDetails', { defaultMessage: 'Edit / open details' }) },
|
||||
{ value: 'connect', label: _t('settings.defaultActions.connectionClick.connect', { defaultMessage: 'Connect' }) },
|
||||
{ value: 'none', label: _t('settings.defaultActions.connectionClick.none', { defaultMessage: 'Do nothing' }) },
|
||||
{
|
||||
value: 'openDetails',
|
||||
label: _t('settings.defaultActions.connectionClick.openDetails', {
|
||||
defaultMessage: 'Edit / open details',
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: 'connect',
|
||||
label: _t('settings.defaultActions.connectionClick.connect', { defaultMessage: 'Connect' }),
|
||||
},
|
||||
{
|
||||
value: 'none',
|
||||
label: _t('settings.defaultActions.connectionClick.none', { defaultMessage: 'Do nothing' }),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -430,12 +572,22 @@ ORDER BY
|
||||
isNative
|
||||
defaultValue="switch"
|
||||
options={[
|
||||
{ value: 'switch', label: _t('settings.defaultActions.databaseClick.switch', { defaultMessage: 'Switch database' }) },
|
||||
{ value: 'none', label: _t('settings.defaultActions.databaseClick.none', { defaultMessage: 'Do nothing' }) },
|
||||
{
|
||||
value: 'switch',
|
||||
label: _t('settings.defaultActions.databaseClick.switch', { defaultMessage: 'Switch database' }),
|
||||
},
|
||||
{
|
||||
value: 'none',
|
||||
label: _t('settings.defaultActions.databaseClick.none', { defaultMessage: 'Do nothing' }),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<FormCheckboxField name="defaultAction.useLastUsedAction" label={_t('settings.defaultActions.useLastUsedAction', { defaultMessage: 'Use last used action' })} defaultValue={true} />
|
||||
<FormCheckboxField
|
||||
name="defaultAction.useLastUsedAction"
|
||||
label={_t('settings.defaultActions.useLastUsedAction', { defaultMessage: 'Use last used action' })}
|
||||
defaultValue={true}
|
||||
/>
|
||||
|
||||
<FormDefaultActionField
|
||||
label={_t('settings.defaultActions.tableClick', { defaultMessage: 'Table click' })}
|
||||
@@ -471,7 +623,11 @@ ORDER BY
|
||||
<svelte:fragment slot="5">
|
||||
<div class="heading">{_t('settings.behaviour', { defaultMessage: 'Behaviour' })}</div>
|
||||
|
||||
<FormCheckboxField name="behaviour.useTabPreviewMode" label={_t('settings.behaviour.useTabPreviewMode', { defaultMessage: 'Use tab preview mode' })} defaultValue={true} />
|
||||
<FormCheckboxField
|
||||
name="behaviour.useTabPreviewMode"
|
||||
label={_t('settings.behaviour.useTabPreviewMode', { defaultMessage: 'Use tab preview mode' })}
|
||||
defaultValue={true}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="behaviour.jsonPreviewWrap"
|
||||
@@ -480,28 +636,45 @@ ORDER BY
|
||||
/>
|
||||
|
||||
<div class="tip">
|
||||
<FontIcon icon="img tip" /> {_t('settings.behaviour.singleClickPreview', { defaultMessage: 'When you single-click or select a file in the "Tables, Views, Functions" view, it is shown in a preview mode and reuses an existing tab (preview tab). This is useful if you are quickly browsing tables and don\'t want every visited table to have its own tab. When you start editing the table or use double-click to open the table from the "Tables" view, a new tab is dedicated to that table.' })}
|
||||
<FontIcon icon="img tip" />
|
||||
{_t('settings.behaviour.singleClickPreview', {
|
||||
defaultMessage:
|
||||
'When you single-click or select a file in the "Tables, Views, Functions" view, it is shown in a preview mode and reuses an existing tab (preview tab). This is useful if you are quickly browsing tables and don\'t want every visited table to have its own tab. When you start editing the table or use double-click to open the table from the "Tables" view, a new tab is dedicated to that table.',
|
||||
})}
|
||||
</div>
|
||||
|
||||
<FormCheckboxField
|
||||
name="behaviour.openDetailOnArrows"
|
||||
label={_t('settings.behaviour.openDetailOnArrows', { defaultMessage: 'Open detail on keyboard navigation' })}
|
||||
label={_t('settings.behaviour.openDetailOnArrows', {
|
||||
defaultMessage: 'Open detail on keyboard navigation',
|
||||
})}
|
||||
defaultValue={true}
|
||||
disabled={values['behaviour.useTabPreviewMode'] === false}
|
||||
/>
|
||||
|
||||
<div class="heading">{_t('settings.confirmations', { defaultMessage: 'Confirmations' })}</div>
|
||||
|
||||
<FormCheckboxField name="skipConfirm.tableDataSave" label={_t('settings.confirmations.skipConfirm.tableDataSave', { defaultMessage: 'Skip confirmation when saving table data (SQL)' })} />
|
||||
<FormCheckboxField
|
||||
name="skipConfirm.tableDataSave"
|
||||
label={_t('settings.confirmations.skipConfirm.tableDataSave', {
|
||||
defaultMessage: 'Skip confirmation when saving table data (SQL)',
|
||||
})}
|
||||
/>
|
||||
<FormCheckboxField
|
||||
name="skipConfirm.collectionDataSave"
|
||||
label={_t('settings.confirmations.skipConfirm.collectionDataSave', { defaultMessage: 'Skip confirmation when saving collection data (NoSQL)' })}
|
||||
label={_t('settings.confirmations.skipConfirm.collectionDataSave', {
|
||||
defaultMessage: 'Skip confirmation when saving collection data (NoSQL)',
|
||||
})}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="6">
|
||||
<div class="heading">{_t('settings.other', { defaultMessage: 'Other' })}</div>
|
||||
|
||||
<FormTextField name="other.gistCreateToken" label={_t('settings.other.gistCreateToken', { defaultMessage: 'API token for creating error gists' })} defaultValue="" />
|
||||
<FormTextField
|
||||
name="other.gistCreateToken"
|
||||
label={_t('settings.other.gistCreateToken', { defaultMessage: 'API token for creating error gists' })}
|
||||
defaultValue=""
|
||||
/>
|
||||
|
||||
<FormSelectField
|
||||
label={_t('settings.other.autoUpdateApplication', { defaultMessage: 'Auto update application' })}
|
||||
@@ -509,16 +682,31 @@ ORDER BY
|
||||
isNative
|
||||
defaultValue=""
|
||||
options={[
|
||||
{ value: 'skip', label: _t('settings.other.autoUpdateApplication.skip', { defaultMessage: 'Do not check for new versions' }) },
|
||||
{ value: '', label: _t('settings.other.autoUpdateApplication.check', { defaultMessage: 'Check for new versions' }) },
|
||||
{ value: 'download', label: _t('settings.other.autoUpdateApplication.download', { defaultMessage: 'Check and download new versions' }) },
|
||||
{
|
||||
value: 'skip',
|
||||
label: _t('settings.other.autoUpdateApplication.skip', {
|
||||
defaultMessage: 'Do not check for new versions',
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
label: _t('settings.other.autoUpdateApplication.check', { defaultMessage: 'Check for new versions' }),
|
||||
},
|
||||
{
|
||||
value: 'download',
|
||||
label: _t('settings.other.autoUpdateApplication.download', {
|
||||
defaultMessage: 'Check and download new versions',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{#if isProApp()}
|
||||
<FormCheckboxField
|
||||
name="ai.allowSendModels"
|
||||
label={_t('settings.other.ai.allowSendModels', { defaultMessage: 'Allow to send DB models and query snippets to AI service' })}
|
||||
label={_t('settings.other.ai.allowSendModels', {
|
||||
defaultMessage: 'Allow to send DB models and query snippets to AI service',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
{/if}
|
||||
@@ -538,28 +726,39 @@ ORDER BY
|
||||
<div class="m-3 ml-5">
|
||||
{#if licenseKeyCheckResult.status == 'ok'}
|
||||
<div>
|
||||
<FontIcon icon="img ok" /> { _t('settings.other.licenseKey.valid', { defaultMessage: 'License key is valid' }) }
|
||||
<FontIcon icon="img ok" />
|
||||
{_t('settings.other.licenseKey.valid', { defaultMessage: 'License key is valid' })}
|
||||
</div>
|
||||
{#if licenseKeyCheckResult.validTo}
|
||||
<div>
|
||||
{ _t('settings.other.licenseKey.validTo', { defaultMessage: 'License valid to:' }) } {licenseKeyCheckResult.validTo}
|
||||
{_t('settings.other.licenseKey.validTo', { defaultMessage: 'License valid to:' })}
|
||||
{licenseKeyCheckResult.validTo}
|
||||
</div>
|
||||
{/if}
|
||||
{#if licenseKeyCheckResult.expiration}
|
||||
<div>{ _t('settings.other.licenseKey.expiration', { defaultMessage: 'License key expiration:' }) } <b>{safeFormatDate(licenseKeyCheckResult.expiration)}</b></div>
|
||||
<div>
|
||||
{_t('settings.other.licenseKey.expiration', { defaultMessage: 'License key expiration:' })}
|
||||
<b>{safeFormatDate(licenseKeyCheckResult.expiration)}</b>
|
||||
</div>
|
||||
{/if}
|
||||
{:else if licenseKeyCheckResult.status == 'error'}
|
||||
<div>
|
||||
<FontIcon icon="img error" />
|
||||
{licenseKeyCheckResult.errorMessage ?? _t('settings.other.licenseKey.invalid', { defaultMessage: 'License key is invalid' })}
|
||||
{licenseKeyCheckResult.errorMessage ??
|
||||
_t('settings.other.licenseKey.invalid', { defaultMessage: 'License key is invalid' })}
|
||||
{#if licenseKeyCheckResult.expiration}
|
||||
<div>{ _t('settings.other.licenseKey.expiration', { defaultMessage: 'License key expiration:' }) } <b>{safeFormatDate(licenseKeyCheckResult.expiration)}</b></div>
|
||||
<div>
|
||||
{_t('settings.other.licenseKey.expiration', { defaultMessage: 'License key expiration:' })}
|
||||
<b>{safeFormatDate(licenseKeyCheckResult.expiration)}</b>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if licenseKeyCheckResult.isExpired}
|
||||
<div class="mt-2">
|
||||
<FormStyledButton
|
||||
value={_t('settings.other.licenseKey.checkForNew', { defaultMessage: 'Check for new license key' })}
|
||||
value={_t('settings.other.licenseKey.checkForNew', {
|
||||
defaultMessage: 'Check for new license key',
|
||||
})}
|
||||
skipWidth
|
||||
on:click={async () => {
|
||||
licenseKeyCheckResult = await apiCall('config/get-new-license', { oldLicenseKey: licenseKey });
|
||||
@@ -579,7 +778,9 @@ ORDER BY
|
||||
<div class="heading">{_t('settings.externalTools', { defaultMessage: 'External tools' })}</div>
|
||||
<FormTextField
|
||||
name="externalTools.mysqldump"
|
||||
label={_t('settings.other.externalTools.mysqldump', { defaultMessage: 'mysqldump (backup MySQL database)' })}
|
||||
label={_t('settings.other.externalTools.mysqldump', {
|
||||
defaultMessage: 'mysqldump (backup MySQL database)',
|
||||
})}
|
||||
defaultValue="mysqldump"
|
||||
/>
|
||||
<FormTextField
|
||||
@@ -589,12 +790,17 @@ ORDER BY
|
||||
/>
|
||||
<FormTextField
|
||||
name="externalTools.mysqlPlugins"
|
||||
label={_t('settings.other.externalTools.mysqlPlugins', { defaultMessage: 'Folder with mysql plugins (for example for authentication). Set only in case of problems' })}
|
||||
label={_t('settings.other.externalTools.mysqlPlugins', {
|
||||
defaultMessage:
|
||||
'Folder with mysql plugins (for example for authentication). Set only in case of problems',
|
||||
})}
|
||||
defaultValue=""
|
||||
/>
|
||||
<FormTextField
|
||||
name="externalTools.pg_dump"
|
||||
label={_t('settings.other.externalTools.pg_dump', { defaultMessage: 'pg_dump (backup PostgreSQL database)' })}
|
||||
label={_t('settings.other.externalTools.pg_dump', {
|
||||
defaultMessage: 'pg_dump (backup PostgreSQL database)',
|
||||
})}
|
||||
defaultValue="pg_dump"
|
||||
/>
|
||||
<FormTextField
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
<script lang="ts">
|
||||
import Link from "../elements/Link.svelte";
|
||||
import CheckboxField from "../forms/CheckboxField.svelte";
|
||||
import FormFieldTemplateLarge from "../forms/FormFieldTemplateLarge.svelte";
|
||||
import SelectField from "../forms/SelectField.svelte";
|
||||
import { currentEditorFontSize, currentEditorTheme, currentTheme, extensions, getSystemTheme, selectedWidget, visibleWidgetSideBar } from "../stores";
|
||||
import { _t } from "../translations";
|
||||
import ThemeSkeleton from "./ThemeSkeleton.svelte";
|
||||
import { EDITOR_THEMES, FONT_SIZES } from '../query/AceEditor.svelte';
|
||||
import { closeCurrentModal } from "../modals/modalTools";
|
||||
import TextField from "../forms/TextField.svelte";
|
||||
import FormTextField from "../forms/FormTextField.svelte";
|
||||
import SqlEditor from "../query/SqlEditor.svelte";
|
||||
|
||||
function openThemePlugins() {
|
||||
closeCurrentModal();
|
||||
$selectedWidget = 'plugins';
|
||||
$visibleWidgetSideBar = true;
|
||||
}
|
||||
|
||||
const sqlPreview = `-- example query
|
||||
SELECT
|
||||
MAX(Album.AlbumId) AS max_album,
|
||||
MAX(Album.Title) AS max_title,
|
||||
Artist.ArtistId,
|
||||
'album' AS test_string,
|
||||
123 AS test_number
|
||||
FROM
|
||||
Album
|
||||
INNER JOIN Artist ON Album.ArtistId = Artist.ArtistId
|
||||
GROUP BY
|
||||
Artist.ArtistId
|
||||
ORDER BY
|
||||
Artist.Name ASC
|
||||
`;
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="heading">{_t('settings.appearance', { defaultMessage: 'Application theme' })}</div>
|
||||
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.appearance.useSystemTheme', { defaultMessage: 'Use system theme' })}
|
||||
type="checkbox"
|
||||
labelProps={{
|
||||
onClick: () => {
|
||||
if ($currentTheme) {
|
||||
$currentTheme = null;
|
||||
} else {
|
||||
$currentTheme = getSystemTheme();
|
||||
}
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CheckboxField
|
||||
checked={!$currentTheme}
|
||||
on:change={e => {
|
||||
if (e.target['checked']) {
|
||||
$currentTheme = null;
|
||||
} else {
|
||||
$currentTheme = getSystemTheme();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
|
||||
<div class="themes">
|
||||
{#each $extensions.themes as theme}
|
||||
<ThemeSkeleton {theme} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="m-5">
|
||||
{_t('settings.appearance.moreThemes', { defaultMessage: 'More themes are available as' })}
|
||||
<Link onClick={openThemePlugins}>plugins</Link>
|
||||
<br />
|
||||
{_t('settings.appearance.afterInstalling', {
|
||||
defaultMessage:
|
||||
'After installing theme plugin (try search "theme" in available extensions) new themes will be available here.',
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div class="heading">{_t('settings.appearance.editorTheme', { defaultMessage: 'Editor theme' })}</div>
|
||||
|
||||
<div class="flex">
|
||||
<div class="col-3">
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.appearance.editorTheme', { defaultMessage: 'Theme' })}
|
||||
type="combo"
|
||||
>
|
||||
<SelectField
|
||||
isNative
|
||||
notSelected={_t('settings.appearance.editorTheme.default', { defaultMessage: '(use theme default)' })}
|
||||
options={EDITOR_THEMES.map(theme => ({ label: theme, value: theme }))}
|
||||
value={$currentEditorTheme}
|
||||
on:change={e => ($currentEditorTheme = e.detail)}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
</div>
|
||||
|
||||
<div class="col-3">
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.appearance.fontSize', { defaultMessage: 'Font size' })}
|
||||
type="combo"
|
||||
>
|
||||
<SelectField
|
||||
isNative
|
||||
notSelected="(default)"
|
||||
options={FONT_SIZES}
|
||||
value={FONT_SIZES.find(x => x.value == $currentEditorFontSize) ? $currentEditorFontSize : 'custom'}
|
||||
on:change={e => ($currentEditorFontSize = e.detail)}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
</div>
|
||||
|
||||
<div class="col-3">
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.appearance.customSize', { defaultMessage: 'Custom size' })}
|
||||
type="text"
|
||||
>
|
||||
<TextField
|
||||
value={$currentEditorFontSize == 'custom' ? '' : $currentEditorFontSize}
|
||||
on:change={e => ($currentEditorFontSize = e.target['value'])}
|
||||
disabled={!!FONT_SIZES.find(x => x.value == $currentEditorFontSize) &&
|
||||
$currentEditorFontSize != 'custom'}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
</div>
|
||||
|
||||
<div class="col-3">
|
||||
<FormTextField
|
||||
name="editor.fontFamily"
|
||||
label={_t('settings.appearance.fontFamily', { defaultMessage: 'Editor font family' })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor">
|
||||
<SqlEditor value={sqlPreview} readOnly />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.heading {
|
||||
font-size: 20px;
|
||||
margin: 5px;
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
|
||||
.themes {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
}
|
||||
|
||||
.editor {
|
||||
position: relative;
|
||||
height: 250px;
|
||||
width: 400px;
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
margin-bottom: var(--dim-large-form-margin);
|
||||
}
|
||||
</style>
|
||||
+38
-18
@@ -9,6 +9,7 @@ import { safeJsonParse } from 'dbgate-tools';
|
||||
import { apiCall } from './utility/api';
|
||||
import { getOpenedTabsStorageName, isAdminPage } from './utility/pageDefs';
|
||||
import { switchCurrentDatabase } from './utility/common';
|
||||
import { tick } from 'svelte';
|
||||
|
||||
export interface TabDefinition {
|
||||
title: string;
|
||||
@@ -189,7 +190,6 @@ export const cloudConnectionsStore = writable({});
|
||||
|
||||
export const promoWidgetPreview = writable(null);
|
||||
|
||||
|
||||
export const DEFAULT_OBJECT_SEARCH_SETTINGS = {
|
||||
pureName: true,
|
||||
schemaName: false,
|
||||
@@ -200,6 +200,7 @@ export const DEFAULT_OBJECT_SEARCH_SETTINGS = {
|
||||
sqlObjectText: false,
|
||||
tableEngine: false,
|
||||
tablesWithRows: false,
|
||||
sortBy: undefined as string
|
||||
};
|
||||
|
||||
export const DEFAULT_CONNECTION_SEARCH_SETTINGS = {
|
||||
@@ -228,6 +229,12 @@ currentTheme.subscribe(value => {
|
||||
});
|
||||
export const getCurrentTheme = () => currentThemeValue;
|
||||
|
||||
let extensionsValue: ExtensionsDirectory = null;
|
||||
extensions.subscribe(value => {
|
||||
extensionsValue = value;
|
||||
});
|
||||
export const getExtensions = () => extensionsValue;
|
||||
|
||||
export const currentThemeDefinition = derived(
|
||||
[currentTheme, extensions, systemThemeStore],
|
||||
([$currentTheme, $extensions, $systemTheme]) => {
|
||||
@@ -239,7 +246,9 @@ currentThemeDefinition.subscribe(value => {
|
||||
if (value?.themeType && getCurrentTheme()) {
|
||||
localStorage.setItem('currentThemeType', value?.themeType);
|
||||
} else {
|
||||
localStorage.removeItem('currentThemeType');
|
||||
if (extensionsValue?.themes?.length > 0) {
|
||||
localStorage.removeItem('currentThemeType');
|
||||
}
|
||||
}
|
||||
});
|
||||
export const openedConnectionsWithTemporary = derived(
|
||||
@@ -310,17 +319,40 @@ openedTabs.subscribe(value => {
|
||||
});
|
||||
export const getOpenedTabs = () => openedTabsValue;
|
||||
|
||||
let openedModalsValue = [];
|
||||
openedModals.subscribe(value => {
|
||||
openedModalsValue = value;
|
||||
|
||||
tick().then(() => {
|
||||
dispatchUpdateCommands();
|
||||
});
|
||||
});
|
||||
export const getOpenedModals = () => openedModalsValue;
|
||||
|
||||
let commandsValue = null;
|
||||
commands.subscribe(value => {
|
||||
commandsValue = value;
|
||||
|
||||
const electron = getElectron();
|
||||
if (electron) {
|
||||
electron.send('update-commands', JSON.stringify(value));
|
||||
}
|
||||
tick().then(() => {
|
||||
dispatchUpdateCommands();
|
||||
});
|
||||
});
|
||||
export const getCommands = () => commandsValue;
|
||||
|
||||
function dispatchUpdateCommands() {
|
||||
const electron = getElectron();
|
||||
if (electron) {
|
||||
electron.send(
|
||||
'update-commands',
|
||||
JSON.stringify({
|
||||
isModalOpened: openedModalsValue?.length > 0,
|
||||
commands: commandsValue,
|
||||
dbgatePage: window['dbgate_page'],
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let activeTabValue = null;
|
||||
activeTab.subscribe(value => {
|
||||
activeTabValue = value;
|
||||
@@ -366,12 +398,6 @@ export const getCurrentDatabase = () => currentDatabaseValue;
|
||||
let currentSettingsValue = null;
|
||||
export const getCurrentSettings = () => currentSettingsValue || {};
|
||||
|
||||
let extensionsValue: ExtensionsDirectory = null;
|
||||
extensions.subscribe(value => {
|
||||
extensionsValue = value;
|
||||
});
|
||||
export const getExtensions = () => extensionsValue;
|
||||
|
||||
let openedConnectionsValue = null;
|
||||
openedConnections.subscribe(value => {
|
||||
openedConnectionsValue = value;
|
||||
@@ -418,12 +444,6 @@ selectedDatabaseObjectAppObject.subscribe(value => {
|
||||
});
|
||||
export const getSelectedDatabaseObjectAppObject = () => selectedDatabaseObjectAppObjectValue;
|
||||
|
||||
let openedModalsValue = [];
|
||||
openedModals.subscribe(value => {
|
||||
openedModalsValue = value;
|
||||
});
|
||||
export const getOpenedModals = () => openedModalsValue;
|
||||
|
||||
let focusedConnectionOrDatabaseValue = null;
|
||||
focusedConnectionOrDatabase.subscribe(value => {
|
||||
focusedConnectionOrDatabaseValue = value;
|
||||
|
||||
@@ -60,7 +60,11 @@
|
||||
|
||||
<FormProvider>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">{constraintInfo ? _t('foreignKeyEditor.editForeignKey', { defaultMessage: 'Edit foreign key' }) : _t('foreignKeyEditor.addForeignKey', { defaultMessage: 'Add foreign key' })}</svelte:fragment>
|
||||
<svelte:fragment slot="header"
|
||||
>{constraintInfo
|
||||
? _t('foreignKeyEditor.editForeignKey', { defaultMessage: 'Edit foreign key' })
|
||||
: _t('foreignKeyEditor.addForeignKey', { defaultMessage: 'Add foreign key' })}</svelte:fragment
|
||||
>
|
||||
|
||||
<div class="largeFormMarker">
|
||||
<div class="row">
|
||||
@@ -92,6 +96,19 @@
|
||||
const name = fullNameFromString(e.detail);
|
||||
refTableName = name.pureName;
|
||||
refSchemaName = name.schemaName;
|
||||
|
||||
if (!columns?.find(x => x.columnName)) {
|
||||
const refTable = dbInfo?.tables?.find(
|
||||
x => x.pureName == refTableName && x.schemaName == refSchemaName
|
||||
);
|
||||
if (refTable?.primaryKey) {
|
||||
columns = refTable.primaryKey.columns.map(col => ({
|
||||
refColumnName: col.columnName,
|
||||
}));
|
||||
} else {
|
||||
columns = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@@ -135,7 +152,8 @@
|
||||
{_t('foreignKeyEditor.baseColumn', { defaultMessage: 'Base column - ' })}{tableInfo.pureName}
|
||||
</div>
|
||||
<div class="col-5 ml-1">
|
||||
{_t('foreignKeyEditor.refColumn', { defaultMessage: 'Ref column - ' })}{refTableName || _t('foreignKeyEditor.tableNotSet', { defaultMessage: '(table not set)' })}
|
||||
{_t('foreignKeyEditor.refColumn', { defaultMessage: 'Ref column - ' })}{refTableName ||
|
||||
_t('foreignKeyEditor.tableNotSet', { defaultMessage: '(table not set)' })}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -217,7 +235,11 @@
|
||||
}}
|
||||
/>
|
||||
|
||||
<FormStyledButton type="button" value={_t('common.close', { defaultMessage: 'Close' })} on:click={closeCurrentModal} />
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value={_t('common.close', { defaultMessage: 'Close' })}
|
||||
on:click={closeCurrentModal}
|
||||
/>
|
||||
{#if constraintInfo}
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
registerCommand({
|
||||
id: 'tableEditor.addColumn',
|
||||
category: __t('tableEditor', { defaultMessage: 'Table editor'}),
|
||||
name: __t('tableEditor.addColumn', { defaultMessage: 'Add column'}),
|
||||
category: __t('tableEditor', { defaultMessage: 'Table editor' }),
|
||||
name: __t('tableEditor.addColumn', { defaultMessage: 'Add column' }),
|
||||
icon: 'icon add-column',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
registerCommand({
|
||||
id: 'tableEditor.addPrimaryKey',
|
||||
category: __t('tableEditor', { defaultMessage: 'Table editor'}),
|
||||
name: __t('tableEditor.addPrimaryKey', { defaultMessage: 'Add primary key'}),
|
||||
category: __t('tableEditor', { defaultMessage: 'Table editor' }),
|
||||
name: __t('tableEditor.addPrimaryKey', { defaultMessage: 'Add primary key' }),
|
||||
icon: 'icon add-key',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
registerCommand({
|
||||
id: 'tableEditor.addForeignKey',
|
||||
category: __t('tableEditor', { defaultMessage: 'Table editor'}),
|
||||
name: __t('tableEditor.addForeignKey', { defaultMessage: 'Add foreign key'}),
|
||||
category: __t('tableEditor', { defaultMessage: 'Table editor' }),
|
||||
name: __t('tableEditor.addForeignKey', { defaultMessage: 'Add foreign key' }),
|
||||
icon: 'icon add-key',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
@@ -36,8 +36,8 @@
|
||||
|
||||
registerCommand({
|
||||
id: 'tableEditor.addIndex',
|
||||
category: __t('tableEditor', { defaultMessage: 'Table editor'}),
|
||||
name: __t('tableEditor.addIndex', { defaultMessage: 'Add index'}),
|
||||
category: __t('tableEditor', { defaultMessage: 'Table editor' }),
|
||||
name: __t('tableEditor.addIndex', { defaultMessage: 'Add index' }),
|
||||
icon: 'icon add-key',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
@@ -47,8 +47,8 @@
|
||||
|
||||
registerCommand({
|
||||
id: 'tableEditor.addUnique',
|
||||
category: __t('tableEditor', { defaultMessage: 'Table editor'}),
|
||||
name: __t('tableEditor.addUnique', { defaultMessage: 'Add unique'}),
|
||||
category: __t('tableEditor', { defaultMessage: 'Table editor' }),
|
||||
name: __t('tableEditor.addUnique', { defaultMessage: 'Add unique' }),
|
||||
icon: 'icon add-key',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
@@ -188,7 +188,10 @@
|
||||
|
||||
<ObjectListControl
|
||||
collection={columns?.map((x, index) => ({ ...x, ordinal: index + 1 }))}
|
||||
title={_t('tableEditor.columns', { defaultMessage: 'Columns ({columnCount})', values: { columnCount: columns?.length || 0 } })}
|
||||
title={_t('tableEditor.columns', {
|
||||
defaultMessage: 'Columns ({columnCount})',
|
||||
values: { columnCount: columns?.length || 0 },
|
||||
})}
|
||||
emptyMessage={_t('tableEditor.nocolumnsdefined', { defaultMessage: 'No columns defined' })}
|
||||
clickable
|
||||
on:clickrow={e => showModal(ColumnEditorModal, { columnInfo: e.detail, tableInfo, setTableInfo, driver })}
|
||||
@@ -217,9 +220,7 @@
|
||||
text: _t('tableEditor.copydefinitions', { defaultMessage: 'Copy definitions' }),
|
||||
icon: 'icon copy',
|
||||
onClick: selected => {
|
||||
const names = selected
|
||||
.map(x => `${x.columnName} ${x.dataType}${x.notNull ? ' NOT NULL' : ''}`)
|
||||
.join(',\n');
|
||||
const names = selected.map(x => `${x.columnName} ${x.dataType}${x.notNull ? ' NOT NULL' : ''}`).join(',\n');
|
||||
navigator.clipboard.writeText(names);
|
||||
},
|
||||
},
|
||||
@@ -288,9 +289,21 @@
|
||||
: null,
|
||||
]}
|
||||
>
|
||||
<svelte:fragment slot="0" let:row>{row?.notNull ? _t('tableEditor.notnull', { defaultMessage: 'NOT NULL' }) : _t('tableEditor.null', { defaultMessage: 'NULL' })}</svelte:fragment>
|
||||
<svelte:fragment slot="1" let:row>{row?.isSparse ? _t('tableEditor.yes', { defaultMessage: 'YES' }) : _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment>
|
||||
<svelte:fragment slot="2" let:row>{row?.isPersisted ? _t('tableEditor.yes', { defaultMessage: 'YES' }) : _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment>
|
||||
<svelte:fragment slot="0" let:row
|
||||
>{row?.notNull
|
||||
? _t('tableEditor.notnull', { defaultMessage: 'NOT NULL' })
|
||||
: _t('tableEditor.null', { defaultMessage: 'NULL' })}</svelte:fragment
|
||||
>
|
||||
<svelte:fragment slot="1" let:row
|
||||
>{row?.isSparse
|
||||
? _t('tableEditor.yes', { defaultMessage: 'YES' })
|
||||
: _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment
|
||||
>
|
||||
<svelte:fragment slot="2" let:row
|
||||
>{row?.isPersisted
|
||||
? _t('tableEditor.yes', { defaultMessage: 'YES' })
|
||||
: _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment
|
||||
>
|
||||
<svelte:fragment slot="3" let:row
|
||||
><Link
|
||||
onClick={e => {
|
||||
@@ -299,8 +312,16 @@
|
||||
}}>{_t('tableEditor.remove', { defaultMessage: 'Remove' })}</Link
|
||||
></svelte:fragment
|
||||
>
|
||||
<svelte:fragment slot="4" let:row>{row?.isUnsigned ? _t('tableEditor.yes', { defaultMessage: 'YES' }) : _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment>
|
||||
<svelte:fragment slot="5" let:row>{row?.isZerofill ? _t('tableEditor.yes', { defaultMessage: 'YES' }) : _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment>
|
||||
<svelte:fragment slot="4" let:row
|
||||
>{row?.isUnsigned
|
||||
? _t('tableEditor.yes', { defaultMessage: 'YES' })
|
||||
: _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment
|
||||
>
|
||||
<svelte:fragment slot="5" let:row
|
||||
>{row?.isZerofill
|
||||
? _t('tableEditor.yes', { defaultMessage: 'YES' })
|
||||
: _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment
|
||||
>
|
||||
<svelte:fragment slot="name" let:row><ColumnLabel {...row} forceIcon /></svelte:fragment>
|
||||
</ObjectListControl>
|
||||
|
||||
@@ -321,7 +342,10 @@
|
||||
<ObjectListControl
|
||||
collection={indexes}
|
||||
onAddNew={isWritable && columns?.length > 0 ? addIndex : null}
|
||||
title={_t('tableEditor.indexes', { defaultMessage: 'Indexes ({indexCount})', values: { indexCount: indexes?.length || 0 } })}
|
||||
title={_t('tableEditor.indexes', {
|
||||
defaultMessage: 'Indexes ({indexCount})',
|
||||
values: { indexCount: indexes?.length || 0 },
|
||||
})}
|
||||
emptyMessage={isWritable ? _t('tableEditor.noindexdefined', { defaultMessage: 'No index defined' }) : null}
|
||||
clickable
|
||||
on:clickrow={e => showModal(IndexEditorModal, { constraintInfo: e.detail, tableInfo, setTableInfo, driver })}
|
||||
@@ -348,7 +372,11 @@
|
||||
>
|
||||
<svelte:fragment slot="name" let:row><ConstraintLabel {...row} /></svelte:fragment>
|
||||
<svelte:fragment slot="0" let:row>{row?.columns.map(x => x.columnName).join(', ')}</svelte:fragment>
|
||||
<svelte:fragment slot="1" let:row>{row?.isUnique ? _t('tableEditor.yes', { defaultMessage: 'YES' }) : _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment>
|
||||
<svelte:fragment slot="1" let:row
|
||||
>{row?.isUnique
|
||||
? _t('tableEditor.yes', { defaultMessage: 'YES' })
|
||||
: _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment
|
||||
>
|
||||
<svelte:fragment slot="2" let:row
|
||||
><Link
|
||||
onClick={e => {
|
||||
@@ -364,7 +392,10 @@
|
||||
<ObjectListControl
|
||||
collection={uniques}
|
||||
onAddNew={isWritable && columns?.length > 0 ? addUnique : null}
|
||||
title={_t('tableEditor.uniqueConstraints', { defaultMessage: 'Unique constraints ({constraintCount})', values: { constraintCount: uniques?.length || 0 } })}
|
||||
title={_t('tableEditor.uniqueConstraints', {
|
||||
defaultMessage: 'Unique constraints ({constraintCount})',
|
||||
values: { constraintCount: uniques?.length || 0 },
|
||||
})}
|
||||
emptyMessage={isWritable ? _t('tableEditor.nouniquedefined', { defaultMessage: 'No unique defined' }) : null}
|
||||
clickable
|
||||
on:clickrow={e => showModal(UniqueEditorModal, { constraintInfo: e.detail, tableInfo, setTableInfo })}
|
||||
@@ -401,13 +432,21 @@
|
||||
<ForeignKeyObjectListControl
|
||||
collection={foreignKeys}
|
||||
onAddNew={isWritable && columns?.length > 0 ? addForeignKey : null}
|
||||
title={_t('tableEditor.foreignKeys', { defaultMessage: 'Foreign keys ({foreignKeyCount})', values: { foreignKeyCount: foreignKeys?.length || 0 } })}
|
||||
emptyMessage={isWritable ? _t('tableEditor.noforeignkeydefined', { defaultMessage: 'No foreign key defined' }) : null}
|
||||
title={_t('tableEditor.foreignKeys', {
|
||||
defaultMessage: 'Foreign keys ({foreignKeyCount})',
|
||||
values: { foreignKeyCount: foreignKeys?.length || 0 },
|
||||
})}
|
||||
emptyMessage={isWritable
|
||||
? _t('tableEditor.noforeignkeydefined', { defaultMessage: 'No foreign key defined' })
|
||||
: null}
|
||||
clickable
|
||||
onRemove={row => setTableInfo(tbl => editorDeleteConstraint(tbl, row))}
|
||||
on:clickrow={e => showModal(ForeignKeyEditorModal, { constraintInfo: e.detail, tableInfo, setTableInfo, dbInfo })}
|
||||
/>
|
||||
<ForeignKeyObjectListControl collection={dependencies} title={_t('tableEditor.dependencies', { defaultMessage: 'Dependencies' })} />
|
||||
<ForeignKeyObjectListControl
|
||||
collection={dependencies}
|
||||
title={_t('tableEditor.dependencies', { defaultMessage: 'Dependencies' })}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<script lang="ts" context="module">
|
||||
import { _t } from '../translations';
|
||||
|
||||
const getCurrentValueMarker: any = {};
|
||||
|
||||
export function shouldShowTab(tab, lockedDbModeArg = getCurrentValueMarker, currentDbArg = getCurrentValueMarker) {
|
||||
@@ -360,7 +358,7 @@
|
||||
import NewObjectModal from '../modals/NewObjectModal.svelte';
|
||||
import { isProApp } from '../utility/proTools';
|
||||
import { openWebLink } from '../utility/simpleTools';
|
||||
import { __t } from '../translations';
|
||||
import { __t, _t } from '../translations';
|
||||
|
||||
export let multiTabIndex;
|
||||
export let shownTab;
|
||||
@@ -437,27 +435,27 @@
|
||||
|
||||
return [
|
||||
tab.tabPreviewMode && {
|
||||
text: 'Pin tab',
|
||||
text: _t('tabsPanel.pinTab', { defaultMessage: 'Pin tab' }),
|
||||
onClick: () => pinTab(tabid),
|
||||
},
|
||||
{
|
||||
text: 'Close',
|
||||
text: _t('common.close', { defaultMessage: 'Close' }),
|
||||
onClick: () => closeTab(tabid),
|
||||
},
|
||||
{
|
||||
text: 'Close all',
|
||||
text: _t('tabsPanel.closeAll', { defaultMessage: 'Close all' }),
|
||||
onClick: () => closeAll(multiTabIndex),
|
||||
},
|
||||
{
|
||||
text: 'Close others',
|
||||
text: _t('tabsPanel.closeOthers', { defaultMessage: 'Close others' }),
|
||||
onClick: () => closeOthersInMultiTab(multiTabIndex)(tabid),
|
||||
},
|
||||
{
|
||||
text: 'Close to the right',
|
||||
text: _t('tabsPanel.closeToTheRight', { defaultMessage: 'Close to the right' }),
|
||||
onClick: () => closeRightTabs(multiTabIndex)(tabid),
|
||||
},
|
||||
{
|
||||
text: 'Duplicate',
|
||||
text: _t('tabsPanel.duplicate', { defaultMessage: 'Duplicate' }),
|
||||
onClick: () => duplicateTab(tab),
|
||||
},
|
||||
tabComponent &&
|
||||
@@ -466,7 +464,7 @@
|
||||
tabs[tabComponent].allowAddToFavorites(props) && [
|
||||
{ divider: true },
|
||||
{
|
||||
text: 'Add to favorites',
|
||||
text: _t('tabsPanel.addToFavorites', { defaultMessage: 'Add to favorites' }),
|
||||
onClick: () => showModal(FavoriteModal, { savingTab: tab }),
|
||||
},
|
||||
],
|
||||
@@ -476,7 +474,7 @@
|
||||
tabs[tabComponent].allowSwitchDatabase(props) && [
|
||||
{ divider: true },
|
||||
{
|
||||
text: 'Switch database',
|
||||
text: _t('tabsPanel.switchDatabase', { defaultMessage: 'Switch database' }),
|
||||
onClick: () => showModal(SwitchDatabaseModal, { callingTab: tab }),
|
||||
},
|
||||
],
|
||||
@@ -499,11 +497,17 @@
|
||||
conid &&
|
||||
database && [
|
||||
{
|
||||
text: `Close tabs with DB ${database}`,
|
||||
text: _t('tabsPanel.closeTabsWithDb', {
|
||||
defaultMessage: 'Close tabs with DB {database}',
|
||||
values: { database },
|
||||
}),
|
||||
onClick: () => closeWithSameDb(tabid),
|
||||
},
|
||||
{
|
||||
text: `Close tabs with other DB than ${database}`,
|
||||
text: _t('tabsPanel.closeTabsWithOtherDb', {
|
||||
defaultMessage: `Close tabs with other DB than {database}`,
|
||||
values: { database },
|
||||
}),
|
||||
onClick: () => closeWithOtherDb(tabid),
|
||||
},
|
||||
],
|
||||
@@ -578,9 +582,19 @@
|
||||
let domTabs;
|
||||
|
||||
function handleTabsWheel(e) {
|
||||
if (!e.shiftKey) {
|
||||
// if (!e.shiftKey) {
|
||||
// e.preventDefault();
|
||||
// domTabs.scrollBy({ top: 0, left: e.deltaY < 0 ? -150 : 150, behavior: 'smooth' });
|
||||
// }
|
||||
|
||||
// Handle horizontal scrolling from trackpad gestures (deltaX)
|
||||
// or vertical scrolling converted to horizontal (deltaY with Shift)
|
||||
|
||||
const scrollAmount = Math.abs(e.deltaX) > Math.abs(e.deltaY) ? e.deltaX : e.shiftKey ? 0 : e.deltaY;
|
||||
|
||||
if (scrollAmount !== 0) {
|
||||
e.preventDefault();
|
||||
domTabs.scrollBy({ top: 0, left: e.deltaY < 0 ? -150 : 150, behavior: 'smooth' });
|
||||
domTabs.scrollBy({ left: scrollAmount, behavior: 'auto' });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -744,7 +758,7 @@
|
||||
title="Upgrade to Premium"
|
||||
data-testid="TabsPanel_buttonUpgrade"
|
||||
>
|
||||
<FontIcon icon="icon premium" padRight /> Upgrade
|
||||
<FontIcon icon="icon premium" /> Upgrade
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -789,6 +803,9 @@
|
||||
cursor: pointer;
|
||||
font-size: 10pt;
|
||||
padding: 5px;
|
||||
margin-top: 3px;
|
||||
margin-right: 3px;
|
||||
font-size: 8pt;
|
||||
}
|
||||
.upgrade-button:hover {
|
||||
background: linear-gradient(135deg, #0f5a85, #5c1870);
|
||||
@@ -808,7 +825,7 @@
|
||||
}
|
||||
|
||||
.tabs-upgrade-button {
|
||||
right: 120px;
|
||||
right: 110px;
|
||||
}
|
||||
.tabs.can-split {
|
||||
right: 60px;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
createQuickExportHandlerRef,
|
||||
registerQuickExportHandler,
|
||||
} from '../buttons/ToolStripExportButton.svelte';
|
||||
import { _t } from '../translations';
|
||||
|
||||
let loadedRows = [];
|
||||
let loadedAll = false;
|
||||
@@ -191,8 +192,8 @@
|
||||
<SelectField
|
||||
isNative
|
||||
options={[
|
||||
{ label: 'Recent logs', value: 'recent' },
|
||||
{ label: 'Choose date', value: 'date' },
|
||||
{ label: _t('logs.recentLogs', { defaultMessage: 'Recent logs' }), value: 'recent' },
|
||||
{ label: _t('logs.chooseDate', { defaultMessage: 'Choose date' }), value: 'date' },
|
||||
]}
|
||||
value={mode}
|
||||
on:change={e => {
|
||||
@@ -202,7 +203,7 @@
|
||||
/>
|
||||
|
||||
{#if mode === 'recent'}
|
||||
<div class="filter-label ml-2">Auto-scroll</div>
|
||||
<div class="filter-label ml-2">{_t('logs.autoScroll', { defaultMessage: 'Auto-scroll' })}</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={autoScroll}
|
||||
@@ -213,7 +214,7 @@
|
||||
{/if}
|
||||
|
||||
{#if mode === 'date'}
|
||||
<div class="filter-label">Date:</div>
|
||||
<div class="filter-label">{_t('logs.date', { defaultMessage: 'Date:' })}</div>
|
||||
<DateRangeSelector
|
||||
onChange={value => {
|
||||
dateFilter = value;
|
||||
@@ -225,12 +226,12 @@
|
||||
data-testid="AdminAuditLogTab_addFilter"
|
||||
icon="icon filter"
|
||||
menu={[
|
||||
{ text: 'Connection ID', onClick: () => filterBy('conid') },
|
||||
{ text: 'Database', onClick: () => filterBy('database') },
|
||||
{ text: 'Engine', onClick: () => filterBy('engine') },
|
||||
{ text: 'Message code', onClick: () => filterBy('msgcode') },
|
||||
{ text: 'Caller', onClick: () => filterBy('caller') },
|
||||
{ text: 'Name', onClick: () => filterBy('name') },
|
||||
{ text: _t('logs.connectionId', { defaultMessage: 'Connection ID' }), onClick: () => filterBy('conid') },
|
||||
{ text: _t('logs.database', { defaultMessage: 'Database' }), onClick: () => filterBy('database') },
|
||||
{ text: _t('logs.engine', { defaultMessage: 'Engine' }), onClick: () => filterBy('engine') },
|
||||
{ text: _t('logs.messageCode', { defaultMessage: 'Message code' }), onClick: () => filterBy('msgcode') },
|
||||
{ text: _t('logs.caller', { defaultMessage: 'Caller' }), onClick: () => filterBy('caller') },
|
||||
{ text: _t('logs.name', { defaultMessage: 'Name' }), onClick: () => filterBy('name') },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
@@ -259,15 +260,15 @@
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:80px">Date</th>
|
||||
<th>Time</th>
|
||||
<th>Code</th>
|
||||
<th>Message</th>
|
||||
<th>Connection</th>
|
||||
<th>Database</th>
|
||||
<th>Engine</th>
|
||||
<th>Caller</th>
|
||||
<th>Name</th>
|
||||
<th style="width:80px">{_t('logs.dateTab', { defaultMessage: 'Date' })}</th>
|
||||
<th>{_t('logs.timeTab', { defaultMessage: 'Time' })}</th>
|
||||
<th>{_t('logs.codeTab', { defaultMessage: 'Code' })}</th>
|
||||
<th>{_t('logs.messageTab', { defaultMessage: 'Message' })}</th>
|
||||
<th>{_t('logs.connectionTab', { defaultMessage: 'Connection' })}</th>
|
||||
<th>{_t('logs.databaseTab', { defaultMessage: 'Database' })}</th>
|
||||
<th>{_t('logs.engineTab', { defaultMessage: 'Engine' })}</th>
|
||||
<th>{_t('logs.callerTab', { defaultMessage: 'Caller' })}</th>
|
||||
<th>{_t('logs.nameTab', { defaultMessage: 'Name' })}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -299,14 +300,14 @@
|
||||
<TabControl
|
||||
isInline
|
||||
tabs={_.compact([
|
||||
{ label: 'Details', slot: 1 },
|
||||
{ label: _t('logs.details', { defaultMessage: 'Details' }), slot: 1 },
|
||||
{ label: 'JSON', slot: 2 },
|
||||
])}
|
||||
>
|
||||
<svelte:fragment slot="1">
|
||||
<div class="details-wrap">
|
||||
<div class="row">
|
||||
<div>Message code:</div>
|
||||
<div>{_t('logs.messageCode', { defaultMessage: 'Message code:' })}</div>
|
||||
{#if mode == 'date'}
|
||||
<Link onClick={() => doSetFilter('msgcode', [row.msgcode])}>{row.msgcode || 'N/A'}</Link>
|
||||
{:else}
|
||||
@@ -314,15 +315,15 @@
|
||||
{/if}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>Message:</div>
|
||||
<div>{_t('logs.message', { defaultMessage: 'Message:' })}</div>
|
||||
{row.msg}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>Time:</div>
|
||||
<div>{_t('logs.time', { defaultMessage: 'Time:' })}</div>
|
||||
<b>{row.time ? format(new Date(parseInt(row.time)), 'yyyy-MM-dd HH:mm:ss') : ''}</b>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>Caller:</div>
|
||||
<div>{_t('logs.caller', { defaultMessage: 'Caller:' })}</div>
|
||||
{#if mode == 'date'}
|
||||
<Link onClick={() => doSetFilter('caller', [row.caller])}>{row.caller || 'N/A'}</Link>
|
||||
{:else}
|
||||
@@ -330,7 +331,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>Name:</div>
|
||||
<div>{_t('logs.name', { defaultMessage: 'Name:' })}</div>
|
||||
{#if mode == 'date'}
|
||||
<Link onClick={() => doSetFilter('name', [row.name])}>{row.name || 'N/A'}</Link>
|
||||
{:else}
|
||||
@@ -339,7 +340,7 @@
|
||||
</div>
|
||||
{#if row.conid}
|
||||
<div class="row">
|
||||
<div>Connection ID:</div>
|
||||
<div>{_t('logs.connectionId', { defaultMessage: 'Connection ID:' })}</div>
|
||||
{#if mode == 'date'}
|
||||
<Link onClick={() => doSetFilter('conid', [row.conid])}
|
||||
>{formatPossibleUuid(row.conid)}</Link
|
||||
@@ -351,7 +352,7 @@
|
||||
{/if}
|
||||
{#if row.database}
|
||||
<div class="row">
|
||||
<div>Database:</div>
|
||||
<div>{_t('logs.database', { defaultMessage: 'Database:' })}</div>
|
||||
{#if mode == 'date'}
|
||||
<Link onClick={() => doSetFilter('database', [row.database])}>{row.database}</Link>
|
||||
{:else}
|
||||
@@ -361,7 +362,7 @@
|
||||
{/if}
|
||||
{#if row.engine}
|
||||
<div class="row">
|
||||
<div>Engine:</div>
|
||||
<div>{_t('logs.engine', { defaultMessage: 'Engine:' })}</div>
|
||||
{#if mode == 'date'}
|
||||
<Link onClick={() => doSetFilter('engine', [row.engine])}>{row.engine}</Link>
|
||||
{:else}
|
||||
@@ -381,13 +382,13 @@
|
||||
{/each}
|
||||
{#if !loadedRows?.length && mode === 'date'}
|
||||
<tr>
|
||||
<td colspan="6">No data for selected date</td>
|
||||
<td colspan="6">{_t('logs.noDataForSelectedDate', { defaultMessage: "No data for selected date" })}</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{#if !loadedAll && mode === 'date'}
|
||||
{#key loadedRows}
|
||||
<tr>
|
||||
<td colspan="6" bind:this={domLoadNext}>Loading next rows... </td>
|
||||
<td colspan="6" bind:this={domLoadNext}>{_t('logs.loadingNextRows', { defaultMessage: "Loading next rows..." })}</td>
|
||||
</tr>
|
||||
{/key}
|
||||
{/if}
|
||||
@@ -402,7 +403,7 @@
|
||||
data-testid="AdminAuditLogTab_refreshButton"
|
||||
on:click={() => {
|
||||
reloadData();
|
||||
}}>Refresh</ToolStripButton
|
||||
}}>{_t('logs.refresh', { defaultMessage: 'Refresh' })}</ToolStripButton
|
||||
>
|
||||
<ToolStripExportButton {quickExportHandlerRef} />
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import CommandModal from '../modals/CommandModal.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import { commandsCustomized } from '../stores';
|
||||
import { _tval } from '../translations';
|
||||
|
||||
$: commandList = _.sortBy(_.values($commandsCustomized), ['category', 'name']);
|
||||
let filter;
|
||||
@@ -27,7 +28,9 @@
|
||||
<div class="table-wrapper">
|
||||
<TableControl
|
||||
clickable
|
||||
rows={commandList.filter(cmd => filterName(filter, cmd['category'], cmd['name'], cmd['keyText'], cmd['id']))}
|
||||
rows={commandList.filter(cmd =>
|
||||
filterName(filter, _tval(cmd['category']), _tval(cmd['name']), _tval(cmd['keyText']), cmd['id'])
|
||||
)}
|
||||
columns={[
|
||||
{ header: 'Category', fieldName: 'category' },
|
||||
{ header: 'Name', fieldName: 'name' },
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
folder: 'diagrams',
|
||||
format: 'json',
|
||||
fileExtension: 'diagram',
|
||||
defaultTeamFolder: true,
|
||||
|
||||
undoRedo: true,
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user