mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
feat(plugin-sql-editor): CB-1083 upload and download script option
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>export</title>
|
||||
<g id="export" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Page-1" transform="translate(3.000000, 2.000000)" fill="#338ECC">
|
||||
<polygon id="Fill-1" points="12.7422 -0.0003 5.4502 7.2927 9.6172 7.2927 9.6172 20.4797 15.8672 20.4797 15.8672 7.2927 20.0342 7.2927"></polygon>
|
||||
<polygon id="Fill-2" points="3.641 28.6602 21.844 28.6602 21.844 25.0192 3.641 25.0192"></polygon>
|
||||
<polygon id="Fill-3" points="0 25.0192 3.641 25.0192 3.641 18.7792 0 18.7792"></polygon>
|
||||
<polygon id="Fill-4" points="0 28.6602 3.641 28.6602 3.641 25.0192 0 25.0192"></polygon>
|
||||
<polygon id="Fill-5" points="21.844 25.0192 25.485 25.0192 25.485 18.7792 21.844 18.7792"></polygon>
|
||||
<polygon id="Fill-6" points="21.844 28.6602 25.485 28.6602 25.485 25.0192 21.844 25.0192"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="import" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Page-1" transform="translate(3.000000, 2.000000)" fill="#338ECC">
|
||||
<polygon id="Fill-1" points="9.6174 -0.0003 9.6174 13.1887 5.4504 13.1887 12.7424 20.4797 20.0344 13.1887 15.8684 13.1887 15.8684 -0.0003"></polygon>
|
||||
<polygon id="Fill-2" points="21.8439 18.78 21.8439 25.02 3.6409 25.02 3.6409 18.78 -0.0001 18.78 -0.0001 25.02 -0.0001 28.66 3.6409 28.66 21.8439 28.66 25.4849 28.66 25.4849 18.78"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 737 B |
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { uuid } from '@cloudbeaver/core-utils';
|
||||
|
||||
interface Props extends React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const styles = css`
|
||||
label {
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
export const UploadButton: React.FC<Props> = function UploadButton({ id, className, children, ...rest }) {
|
||||
const _id = id ?? uuid();
|
||||
|
||||
return styled(styles)(
|
||||
<>
|
||||
<input {...rest} type='file' id={_id} hidden />
|
||||
<label htmlFor={_id} className={className} title={rest.title}>
|
||||
{children}
|
||||
</label>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -125,6 +125,7 @@ export * from './FormControls/SubmittingForm';
|
||||
export * from './FormControls/Textarea';
|
||||
export * from './Link';
|
||||
export * from './Cell';
|
||||
export * from './UploadButton';
|
||||
export * from './ErrorMessage';
|
||||
export * from './StatusMessage';
|
||||
export * from './ExceptionMessage';
|
||||
|
||||
@@ -52,6 +52,7 @@ export const defaultENLocale = [
|
||||
['ui_set', 'Set'],
|
||||
['ui_are_you_sure', 'Are you sure?'],
|
||||
['ui_options', 'Options'],
|
||||
['ui_changes_might_be_lost', 'Your changes might be lost'],
|
||||
|
||||
['root_permission_denied', "You don't have permissions"],
|
||||
['root_permission_no_permission', "You don't have permission for this action"],
|
||||
|
||||
@@ -50,6 +50,7 @@ export const defaultRULocale = [
|
||||
['ui_set', 'Установить'],
|
||||
['ui_are_you_sure', 'Вы уверены?'],
|
||||
['ui_options', 'Параметры'],
|
||||
['ui_changes_might_be_lost', 'Ваши изменения могут быть потеряны'],
|
||||
|
||||
['root_permission_denied', 'Отказано в доступе'],
|
||||
['root_permission_no_permission', 'У вас нет разрешения на это действие'],
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
export function download(blob: Blob, fileName: string): void {
|
||||
const saveLink = document.createElement('a');
|
||||
|
||||
saveLink.download = fileName;
|
||||
saveLink.style.display = 'none';
|
||||
document.body.appendChild(saveLink);
|
||||
|
||||
try {
|
||||
const url = URL.createObjectURL(blob);
|
||||
saveLink.href = url;
|
||||
saveLink.onclick = () => requestAnimationFrame(() => URL.revokeObjectURL(url));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
console.warn('Error while getting object URL.');
|
||||
}
|
||||
|
||||
saveLink.click();
|
||||
document.body.removeChild(saveLink);
|
||||
}
|
||||
@@ -27,3 +27,5 @@ export * from './getCookies';
|
||||
export * from './getUniqueName';
|
||||
export * from './isMapsEqual';
|
||||
export * from './openCenteredPopup';
|
||||
export * from './download';
|
||||
export * from './uploadTextFiles';
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
interface UploadProcess {
|
||||
promise: Promise<string>;
|
||||
reader: FileReader;
|
||||
}
|
||||
|
||||
type UploadData = Map<File, UploadProcess>;
|
||||
|
||||
export function uploadTextFiles(files: File[]): UploadData {
|
||||
const result: UploadData = new Map();
|
||||
|
||||
for (const file of files) {
|
||||
const reader = new FileReader();
|
||||
|
||||
const promise: Promise<string> = new Promise((resolve, reject) => {
|
||||
reader.readAsText(file);
|
||||
|
||||
reader.onload = event => {
|
||||
const data = event.target?.result;
|
||||
|
||||
if (data) {
|
||||
resolve(data as string);
|
||||
} else {
|
||||
reject(new Error(`No data for the file: "${file.name}"`));
|
||||
}
|
||||
};
|
||||
|
||||
reader.onerror = event => {
|
||||
reject(new Error(`Error occurred reading file: "${file.name}"`));
|
||||
if (event.target?.error) {
|
||||
console.error(event.target.error);
|
||||
}
|
||||
};
|
||||
|
||||
reader.onabort = () => reject(new Error(`Reading "${file.name}" is aborted`));
|
||||
});
|
||||
|
||||
result.set(file, {
|
||||
promise,
|
||||
reader,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import { observer } from 'mobx-react-lite';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { StaticImage, useTab as useBaseTab } from '@cloudbeaver/core-blocks';
|
||||
import { StaticImage, UploadButton, useTab as useBaseTab } from '@cloudbeaver/core-blocks';
|
||||
import { useController } from '@cloudbeaver/core-di';
|
||||
import { useTranslate } from '@cloudbeaver/core-localization';
|
||||
import { composes, useStyles } from '@cloudbeaver/core-theming';
|
||||
@@ -19,10 +19,11 @@ import type { ISqlEditorProps } from './ISqlEditorProps';
|
||||
import type { SQLCodeEditorController } from './SQLCodeEditor/SQLCodeEditorController';
|
||||
import { SQLCodeEditorLoader } from './SQLCodeEditor/SQLCodeEditorLoader';
|
||||
import { SqlEditorController } from './SqlEditorController';
|
||||
import { useTools } from './useTools';
|
||||
|
||||
const styles = composes(
|
||||
css`
|
||||
button {
|
||||
button, upload {
|
||||
composes: theme-ripple from global;
|
||||
}
|
||||
`,
|
||||
@@ -35,6 +36,12 @@ const styles = composes(
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
actions {
|
||||
width: 32px;
|
||||
@@ -44,8 +51,15 @@ const styles = composes(
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
tools {
|
||||
width: 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
button {
|
||||
button, upload {
|
||||
background: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
@@ -76,6 +90,7 @@ export const SqlEditor = observer<ISqlEditorProps>(function SqlEditor({ tab, cla
|
||||
const editor = useRef<SQLCodeEditorController>(null);
|
||||
const baseTab = useBaseTab(tab.id);
|
||||
const controller = useController(SqlEditorController, tab);
|
||||
const tools = useTools(controller, tab);
|
||||
|
||||
useEffect(() => {
|
||||
if (!baseTab.selected) {
|
||||
@@ -95,38 +110,58 @@ export const SqlEditor = observer<ISqlEditorProps>(function SqlEditor({ tab, cla
|
||||
|
||||
return styled(style)(
|
||||
<sql-editor className={className}>
|
||||
<actions onMouseDown={preventFocus}>
|
||||
<button
|
||||
disabled={controller.isLineScriptEmpty || controller.isDisabled}
|
||||
title={translate('sql_editor_sql_execution_button_tooltip')}
|
||||
onClick={controller.executeQuery}
|
||||
>
|
||||
<StaticImage icon="/icons/sql_exec.svg" />
|
||||
</button>
|
||||
<button
|
||||
disabled={controller.isLineScriptEmpty || controller.isDisabled}
|
||||
title={translate('sql_editor_sql_execution_new_tab_button_tooltip')}
|
||||
onClick={controller.executeQueryNewTab}
|
||||
>
|
||||
<StaticImage icon="/icons/sql_exec_new.svg" />
|
||||
</button>
|
||||
<button
|
||||
disabled={controller.isDisabled || controller.isScriptEmpty}
|
||||
title={translate('sql_editor_sql_execution_script_button_tooltip')}
|
||||
onClick={controller.executeScript}
|
||||
>
|
||||
<StaticImage icon="/icons/sql_script_exec.svg" />
|
||||
</button>
|
||||
{controller.dialect?.supportsExplainExecutionPlan && (
|
||||
<container>
|
||||
<actions onMouseDown={preventFocus}>
|
||||
<button
|
||||
disabled={controller.isLineScriptEmpty || controller.isDisabled}
|
||||
title={translate('sql_editor_execution_plan_button_tooltip')}
|
||||
onClick={controller.showExecutionPlan}
|
||||
title={translate('sql_editor_sql_execution_button_tooltip')}
|
||||
onClick={controller.executeQuery}
|
||||
>
|
||||
<StaticImage icon="/icons/sql_execution_plan.svg" />
|
||||
<StaticImage icon="/icons/sql_exec.svg" />
|
||||
</button>
|
||||
)}
|
||||
</actions>
|
||||
<button
|
||||
disabled={controller.isLineScriptEmpty || controller.isDisabled}
|
||||
title={translate('sql_editor_sql_execution_new_tab_button_tooltip')}
|
||||
onClick={controller.executeQueryNewTab}
|
||||
>
|
||||
<StaticImage icon="/icons/sql_exec_new.svg" />
|
||||
</button>
|
||||
<button
|
||||
disabled={controller.isDisabled || controller.isScriptEmpty}
|
||||
title={translate('sql_editor_sql_execution_script_button_tooltip')}
|
||||
onClick={controller.executeScript}
|
||||
>
|
||||
<StaticImage icon="/icons/sql_script_exec.svg" />
|
||||
</button>
|
||||
{controller.dialect?.supportsExplainExecutionPlan && (
|
||||
<button
|
||||
disabled={controller.isLineScriptEmpty || controller.isDisabled}
|
||||
title={translate('sql_editor_execution_plan_button_tooltip')}
|
||||
onClick={controller.showExecutionPlan}
|
||||
>
|
||||
<StaticImage icon="/icons/sql_execution_plan.svg" />
|
||||
</button>
|
||||
)}
|
||||
</actions>
|
||||
<tools onMouseDown={preventFocus}>
|
||||
<button
|
||||
disabled={controller.isLineScriptEmpty}
|
||||
title={translate('sql_editor_download_script_tooltip')}
|
||||
onClick={tools.downloadScript}
|
||||
>
|
||||
<StaticImage icon='/icons/export.svg' />
|
||||
</button>
|
||||
<UploadButton
|
||||
accept='.sql'
|
||||
title={translate('sql_editor_upload_script_tooltip')}
|
||||
onChange={event => tools.uploadScript(event.target.files)}
|
||||
>
|
||||
<upload>
|
||||
<StaticImage icon='/icons/import.svg' />
|
||||
</upload>
|
||||
</UploadButton>
|
||||
</tools>
|
||||
</container>
|
||||
<SQLCodeEditorLoader
|
||||
ref={editor}
|
||||
readonly={controller.readonly}
|
||||
|
||||
@@ -234,6 +234,11 @@ export class SqlEditorController implements IInitializableController, IDestructi
|
||||
}
|
||||
};
|
||||
|
||||
setQuery(query: string): void {
|
||||
this.tab.handlerState.query = query;
|
||||
this.parser.setScript(query);
|
||||
}
|
||||
|
||||
private async executeQueryAction(
|
||||
query: ISQLScriptSegment | undefined,
|
||||
action: (query: ISQLScriptSegment) => Promise<void>
|
||||
@@ -515,8 +520,8 @@ export class SqlEditorController implements IInitializableController, IDestructi
|
||||
(data as any).cancel(); // seems it doesn't works, after disabling read-only mode, typings appears
|
||||
return;
|
||||
}
|
||||
this.tab.handlerState.query = query;
|
||||
this.parser.setScript(query);
|
||||
|
||||
this.setQuery(query);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { action } from 'mobx';
|
||||
|
||||
import type { ITab } from '@cloudbeaver/core-app';
|
||||
import { useObservableRef } from '@cloudbeaver/core-blocks';
|
||||
import { ConnectionInfoResource } from '@cloudbeaver/core-connections';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { CommonDialogService, ConfirmationDialog, DialogueStateResult } from '@cloudbeaver/core-dialogs';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
import { download, generateFileName, uploadTextFiles } from '@cloudbeaver/core-utils';
|
||||
|
||||
import type { ISqlEditorTabState } from '../ISqlEditorTabState';
|
||||
import { SqlEditorSettingsService } from '../SqlEditorSettingsService';
|
||||
import type { SqlEditorController } from './SqlEditorController';
|
||||
|
||||
interface State {
|
||||
uploadScript: (files: FileList | null) => Promise<void>;
|
||||
downloadScript: () => void;
|
||||
}
|
||||
|
||||
export function useTools(controller: SqlEditorController, tab: ITab<ISqlEditorTabState>): Readonly<State> {
|
||||
const commonDialogService = useService(CommonDialogService);
|
||||
const notificationService = useService(NotificationService);
|
||||
const connectionInfoResource = useService(ConnectionInfoResource);
|
||||
const sqlEditorSettingsService = useService(SqlEditorSettingsService);
|
||||
|
||||
return useObservableRef(() => ({
|
||||
async uploadScript(fileList: FileList | null) {
|
||||
if (!fileList) {
|
||||
throw new Error('No files found');
|
||||
}
|
||||
|
||||
if (!fileList.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.controller.value.trim()) {
|
||||
const state = await this.commonDialogService.open(ConfirmationDialog, {
|
||||
title: 'ui_changes_might_be_lost',
|
||||
message: 'sql_editor_upload_script_unsaved_changes_dialog_message',
|
||||
confirmActionText: 'ui_processing_save',
|
||||
});
|
||||
|
||||
if (state !== DialogueStateResult.Rejected) {
|
||||
this.downloadScript();
|
||||
}
|
||||
}
|
||||
|
||||
const maxSize = this.sqlEditorSettingsService.settings.getValue('maxFileSize');
|
||||
const aboveMaxSize: File[] = [];
|
||||
|
||||
const files = Array.from(fileList).filter(file => {
|
||||
const size = Math.round(file.size / 1000); // kilobyte
|
||||
const above = size > maxSize;
|
||||
|
||||
if (above) {
|
||||
aboveMaxSize.push(file);
|
||||
}
|
||||
|
||||
return !above;
|
||||
});
|
||||
|
||||
if (aboveMaxSize.length > 0) {
|
||||
const nameList = aboveMaxSize.map(file => `${file.name} (${Math.round(file.size / 1000)}KB)`);
|
||||
const message = `Max size: ${maxSize}KB\nFollowing files exceed max size: "${nameList.join(', ')}"`;
|
||||
|
||||
this.notificationService.logInfo({
|
||||
title: 'sql_editor_upload_script_max_size_title',
|
||||
message,
|
||||
persistent: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (!files.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = uploadTextFiles(files);
|
||||
const process = Array.from(data.values())[0];
|
||||
|
||||
try {
|
||||
const script = await process.promise;
|
||||
controller.setQuery(script);
|
||||
} catch (exception) {
|
||||
this.notificationService.logException(exception, 'Uploading file error');
|
||||
}
|
||||
},
|
||||
|
||||
downloadScript() {
|
||||
const script = this.controller.value;
|
||||
|
||||
if (!script.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const blob = new Blob([script], {
|
||||
type: 'application/sql',
|
||||
});
|
||||
|
||||
let name = 'script';
|
||||
const connectionId = this.tab.handlerState.executionContext?.connectionId;
|
||||
|
||||
if (connectionId) {
|
||||
const connection = this.connectionInfoResource.get(connectionId);
|
||||
name = connection ? `${connection.name}-script` : name;
|
||||
}
|
||||
|
||||
download(blob, generateFileName(name, '.sql'));
|
||||
},
|
||||
}),
|
||||
{ uploadScript: action.bound, downloadScript: action.bound },
|
||||
{ controller, commonDialogService, connectionInfoResource, notificationService, sqlEditorSettingsService, tab });
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2019-2021 DBeaver Corp
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* NOTICE: All information contained herein is, and remains
|
||||
* the property of DBeaver Corp and its suppliers, if any.
|
||||
* The intellectual and technical concepts contained
|
||||
* herein are proprietary to DBeaver Corp and its suppliers
|
||||
* and may be covered by U.S. and Foreign Patents,
|
||||
* patents in process, and are protected by trade secret or copyright law.
|
||||
* Dissemination of this information or reproduction of this material
|
||||
* is strictly forbidden unless prior written permission is obtained
|
||||
* from DBeaver Corp.
|
||||
*/
|
||||
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { PluginManagerService } from '@cloudbeaver/core-plugin';
|
||||
|
||||
const defaultSettings = {
|
||||
maxFileSize: 100, // kilobyte
|
||||
};
|
||||
|
||||
@injectable()
|
||||
export class SqlEditorSettingsService {
|
||||
readonly settings = this.pluginManagerService.getPluginSettings('core.app.sqlEditor', defaultSettings);
|
||||
|
||||
constructor(private pluginManagerService: PluginManagerService) { }
|
||||
}
|
||||
@@ -14,4 +14,8 @@ export default [
|
||||
['sql_execution_plan_properties_panel_value', 'Value'],
|
||||
['sql_execution_plan_properties_panel_general', 'General'],
|
||||
['sql_execution_plan_properties_panel_details', 'Details'],
|
||||
['sql_editor_upload_script_tooltip', 'Upload sql script'],
|
||||
['sql_editor_download_script_tooltip', 'Download sql script'],
|
||||
['sql_editor_upload_script_unsaved_changes_dialog_message', 'Your current script will be overwritten by the uploaded script. Do you want to save it first?'],
|
||||
['sql_editor_upload_script_max_size_title', 'File size exceeds max size'],
|
||||
];
|
||||
|
||||
@@ -14,4 +14,8 @@ export default [
|
||||
['sql_execution_plan_properties_panel_value', 'Значение'],
|
||||
['sql_execution_plan_properties_panel_general', 'Общие'],
|
||||
['sql_execution_plan_properties_panel_details', 'Дополнительные'],
|
||||
['sql_editor_upload_script_tooltip', 'Загрузить sql скрипт'],
|
||||
['sql_editor_download_script_tooltip', 'Скачать sql скрипт'],
|
||||
['sql_editor_upload_script_unsaved_changes_dialog_message', 'Текущий скрипт будет перезаписан загруженным. Сохранить текущий?'],
|
||||
['sql_editor_upload_script_max_size_title', 'Размер файла превышает максимально допустимый'],
|
||||
];
|
||||
|
||||
@@ -13,6 +13,7 @@ import { SqlDialectInfoService } from './SqlDialectInfoService';
|
||||
import { SqlEditorBootstrap } from './SqlEditorBootstrap';
|
||||
import { SqlEditorNavigatorService } from './SqlEditorNavigatorService';
|
||||
import { SqlEditorService } from './SqlEditorService';
|
||||
import { SqlEditorSettingsService } from './SqlEditorSettingsService';
|
||||
import { SqlEditorTabService } from './SqlEditorTabService';
|
||||
import { SqlExecutionPlanService } from './SqlResultTabs/ExecutionPlan/SqlExecutionPlanService';
|
||||
import { SqlQueryResultService } from './SqlResultTabs/SqlQueryResultService';
|
||||
@@ -35,5 +36,6 @@ export const sqlEditorPluginManifest: PluginManifest = {
|
||||
SqlEditorService,
|
||||
SqlEditorNavigatorService,
|
||||
LocaleService,
|
||||
SqlEditorSettingsService,
|
||||
],
|
||||
};
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
maxLogRecords: 2000,
|
||||
maxFailedRequests: 3
|
||||
},
|
||||
sqlEditor: {
|
||||
// max size of the file that can be uploaded to the editor (in kilobytes)
|
||||
maxFileSize: 100,
|
||||
}
|
||||
}
|
||||
},
|
||||
plugin_data_spreadsheet_new: {
|
||||
|
||||
Reference in New Issue
Block a user