removed charts & profiler

This commit is contained in:
SPRINX0\prochazka
2025-05-29 14:54:08 +02:00
parent 1794b86041
commit db6b7f52eb
7 changed files with 0 additions and 960 deletions

View File

@@ -1,87 +0,0 @@
<script lang="ts" context="module">
import Chart from 'chart.js/auto';
import 'chartjs-adapter-moment';
import zoomPlugin from 'chartjs-plugin-zoom';
const getCurrentEditor = () => getActiveComponent('ChartCore');
registerCommand({
id: 'chart.export',
category: 'Chart',
toolbarName: 'Export',
name: 'Export chart',
icon: 'icon report',
toolbar: true,
isRelatedToTab: true,
onClick: () => getCurrentEditor().exportChart(),
testEnabled: () => getCurrentEditor() != null,
});
Chart.register(zoomPlugin);
</script>
<script lang="ts">
import { onMount, afterUpdate, onDestroy } from 'svelte';
import _ from 'lodash';
import registerCommand from '../commands/registerCommand';
import { apiCall } from '../utility/api';
import contextMenu, { getContextMenu, registerMenu } from '../utility/contextMenu';
import createActivator, { getActiveComponent } from '../utility/createActivator';
import { saveFileToDisk } from '../utility/exportFileTools';
export let data;
export let title;
export let type = 'line';
export let options = {};
// export let plugins = {};
// export let menu;
export const activator = createActivator('ChartCore', true);
let chart = null;
let domChart;
onMount(() => {
chart = new Chart(domChart, {
type,
data: data,
// options must be cloned, because chartjs modifies options object, without cloning fails passing options to electron invoke
options: _.cloneDeep(options),
});
});
afterUpdate(() => {
if (!chart) return;
chart.data = data;
chart.type = type;
chart.options = _.cloneDeep(options);
// chart.plugins = plugins;
chart.update();
});
onDestroy(() => {
chart = null;
});
export async function exportChart() {
saveFileToDisk(async filePath => {
await apiCall('files/export-chart', {
title,
filePath,
config: {
type,
data,
options,
},
image: domChart.toDataURL(),
});
});
}
registerMenu({ command: 'chart.export', tag: 'export' });
const menu = getContextMenu();
</script>
<canvas bind:this={domChart} {...$$restProps} use:contextMenu={menu} />

View File

@@ -1,170 +0,0 @@
<script lang="ts">
import FormProviderCore from '../forms/FormProviderCore.svelte';
import HorizontalSplitter from '../elements/HorizontalSplitter.svelte';
import WidgetColumnBar from '../widgets/WidgetColumnBar.svelte';
import WidgetColumnBarItem from '../widgets/WidgetColumnBarItem.svelte';
import ManagerInnerContainer from '../elements/ManagerInnerContainer.svelte';
import FormSelectField from '../forms/FormSelectField.svelte';
import FormTextField from '../forms/FormTextField.svelte';
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
import FormFieldTemplateTiny from '../forms/FormFieldTemplateTiny.svelte';
import { getConnectionInfo } from '../utility/metadataLoaders';
import { findEngineDriver } from 'dbgate-tools';
import { extensions } from '../stores';
import { loadChartData, loadChartStructure } from './chartDataLoader';
import DataChart from './DataChart.svelte';
import _ from 'lodash';
import ErrorInfo from '../elements/ErrorInfo.svelte';
import FormColorField from '../forms/FormColorField.svelte';
export let data;
export let configStore;
export let conid;
export let database;
export let sql;
// export let menu;
let availableColumnNames = [];
let errorLoadingColumns = null;
let errorLoadingData = null;
let loadedData = null;
$: config = $configStore;
const getDriver = async () => {
const conn = await getConnectionInfo({ conid });
if (!conn) return;
const driver = findEngineDriver(conn, $extensions);
return driver;
};
const handleLoadColumns = async () => {
const driver = await getDriver();
if (!driver) return;
try {
errorLoadingColumns = null;
const columns = await loadChartStructure(driver, conid, database, sql);
availableColumnNames = columns;
// configStore.update(x => ({ ...x, labelColumn: availableColumnNames[0] }));
} catch (err) {
console.error(err);
errorLoadingColumns = err.message;
}
};
const handleLoadData = async () => {
const driver = await getDriver();
if (!driver) return;
try {
errorLoadingData = null;
const loaded = await loadChartData(driver, conid, database, sql, config);
if (!loaded) return;
const { columns, rows } = loaded;
loadedData = {
structure: columns,
rows,
};
} catch (err) {
console.error(err);
errorLoadingData = err.message;
}
};
$: {
$extensions;
if (sql && conid && database) {
handleLoadColumns();
}
}
$: {
if (data) {
availableColumnNames = data.structure.columns.map(x => x.columnName);
}
}
$: {
$extensions;
if (config.labelColumn && sql && conid && database) {
handleLoadData();
}
}
let managerSize;
</script>
<FormProviderCore values={configStore} template={FormFieldTemplateTiny}>
<HorizontalSplitter initialValue="300px" bind:size={managerSize}>
<div class="left" slot="1">
<WidgetColumnBar>
<WidgetColumnBarItem title="Style" name="style" height="40%">
<ManagerInnerContainer width={managerSize}>
<FormSelectField
label="Chart type"
name="chartType"
isNative
options={[
{ value: 'bar', label: 'Bar' },
{ value: 'line', label: 'Line' },
{ value: 'pie', label: 'Pie' },
{ value: 'polarArea', label: 'Polar area' },
]}
/>
<FormTextField label="Chart title" name="chartTitle" />
<FormSelectField
label="Truncate from"
name="truncateFrom"
isNative
options={[
{ value: 'begin', label: 'Begin' },
{ value: 'end', label: 'End (most recent data for datetime)' },
]}
/>
<FormTextField label="Truncate limit" name="truncateLimit" />
<FormCheckboxField label="Show relative values" name="showRelativeValues" />
{#if $configStore.chartType == 'line'}
<FormCheckboxField label="Fill" name="fillLineChart" defaultValue={true} />
{/if}
<FormTextField label="Color set" name="colorSeed" />
</ManagerInnerContainer>
</WidgetColumnBarItem>
<WidgetColumnBarItem title="Data" name="data">
<ManagerInnerContainer width={managerSize}>
{#if availableColumnNames.length > 0}
<FormSelectField
label="Label column"
name="labelColumn"
isNative
options={availableColumnNames.map(col => ({ value: col, label: col }))}
/>
{/if}
{#each availableColumnNames as col (col)}
<FormCheckboxField label={col} name={`dataColumn_${col}`} />
{#if config[`dataColumn_${col}`]}
<FormColorField label="Color" name={`dataColumnColor_${col}`} emptyLabel="Random" />
<FormTextField label="Label" name={`dataColumnLabel_${col}`} />
{/if}
{/each}
</ManagerInnerContainer>
</WidgetColumnBarItem>
</WidgetColumnBar>
</div>
<svelte:fragment slot="2">
{#if errorLoadingColumns}
<ErrorInfo message={errorLoadingColumns} alignTop />
{:else if errorLoadingData}
<ErrorInfo message={errorLoadingData} alignTop />
{:else}
<DataChart data={data || loadedData} />
{/if}
</svelte:fragment>
</HorizontalSplitter>
</FormProviderCore>
<style>
.left {
background-color: var(--theme-bg-0);
display: flex;
flex: 1;
}
</style>

View File

@@ -1,198 +0,0 @@
<script lang="ts" context="module">
function getTimeAxis(labels) {
const res = [];
for (const label of labels) {
const parsed = moment(label);
if (!parsed.isValid()) return null;
const iso = parsed.toISOString();
if (iso < '1850-01-01T00:00:00' || iso > '2150-01-01T00:00:00') return null;
res.push(parsed);
}
return res;
}
function getLabels(labelValues, timeAxis, chartType) {
if (!timeAxis) return labelValues;
if (chartType === 'line') return timeAxis.map(x => x.toDate());
return timeAxis.map(x => x.format('D. M. YYYY'));
}
function getOptions(timeAxis, chartType) {
const res = {
scales: {},
};
if (timeAxis && chartType === 'line') {
res.scales = {
x: {
type: 'time',
distribution: 'linear',
time: {
tooltipFormat: 'D. M. YYYY HH:mm',
displayFormats: {
millisecond: 'HH:mm:ss.SSS',
second: 'HH:mm:ss',
minute: 'HH:mm',
hour: 'D.M hA',
day: 'D. M.',
week: 'D. M. YYYY',
month: 'MM-YYYY',
quarter: '[Q]Q - YYYY',
year: 'YYYY',
},
},
},
};
}
return res;
}
function getPlugins(chartTitle) {
const res = {};
if (chartTitle) {
res['title'] = {
display: true,
text: chartTitle,
};
}
res['zoom'] = {
zoom: {
wheel: {
enabled: true,
},
pinch: {
enabled: true,
},
drag: {
enabled: true,
modifierKey: 'shift',
},
mode: 'x',
},
pan: {
enabled: true,
mode: 'x',
},
};
return res;
}
function createChartData(
freeData,
labelColumn,
dataColumns,
colorSeed,
chartType,
chartTitle,
fillLineChart,
dataColumnColors,
dataColumnLabels,
themeDef
) {
if (!freeData || !labelColumn || !dataColumns || !freeData.rows || dataColumns.length == 0) return null;
const palettes = themeDef?.themeType == 'dark' ? presetDarkPalettes : presetPalettes;
const colors = randomcolor({
count: _.max([freeData.rows.length, dataColumns.length, 1]),
seed: colorSeed,
});
let backgroundColor = null;
let borderColor = null;
const labelValues = freeData.rows.map(x => x[labelColumn]);
const timeAxis = getTimeAxis(labelValues);
const labels = getLabels(labelValues, timeAxis, chartType);
const res = {
labels,
datasets: dataColumns.map((dataColumn, columnIndex) => {
const label = dataColumnLabels[dataColumn];
if (chartType == 'line' || chartType == 'bar') {
const color = dataColumnColors[dataColumn];
if (color) {
backgroundColor = palettes[color][4] + '80';
borderColor = palettes[color][7];
} else {
backgroundColor = colors[columnIndex] + '80';
borderColor = colors[columnIndex];
}
} else {
backgroundColor = colors;
}
return {
label: label || dataColumn,
data: freeData.rows.map(row => row[dataColumn]),
backgroundColor,
borderColor,
borderWidth: 1,
fill: fillLineChart == false ? false : true,
};
}),
};
const options = getOptions(timeAxis, chartType);
const plugins = getPlugins(chartTitle);
// console.log('RES', res);
// console.log('OPTIONS', options);
return [res, options, plugins];
}
</script>
<script lang="ts">
import _ from 'lodash';
import randomcolor from 'randomcolor';
import moment from 'moment';
import ChartCore from './ChartCore.svelte';
import { getFormContext } from '../forms/FormProviderCore.svelte';
import { generate, presetPalettes, presetDarkPalettes, presetPrimaryColors } from '@ant-design/colors';
import { extractDataColumnColors, extractDataColumnLabels, extractDataColumns } from './chartDataLoader';
import { currentThemeDefinition } from '../stores';
export let data;
// export let menu;
const { values } = getFormContext();
let clientWidth;
let clientHeight;
$: dataColumns = extractDataColumns($values);
$: dataColumnColors = extractDataColumnColors($values, dataColumns);
$: dataColumnLabels = extractDataColumnLabels($values, dataColumns);
$: chartData = createChartData(
data,
$values.labelColumn,
dataColumns,
$values.colorSeed || '5',
$values.chartType,
$values.chartTitle,
$values.fillLineChart,
dataColumnColors,
dataColumnLabels,
$currentThemeDefinition
);
</script>
<div class="wrapper" bind:clientWidth bind:clientHeight>
{#if chartData}
{#key `${$values.chartType}|${clientWidth}|${clientHeight}`}
<ChartCore
width={clientWidth}
height={clientHeight}
data={chartData[0]}
type={$values.chartType}
title={$values.chartTitle}
options={{ ...chartData[1], plugins: chartData[2] }}
/>
{/key}
{/if}
</div>
<style>
.wrapper {
flex: 1;
overflow: hidden;
}
</style>

View File

@@ -1,133 +0,0 @@
import type { Select } from 'dbgate-sqltree';
import type { EngineDriver } from 'dbgate-types';
import _ from 'lodash';
import { apiCall } from '../utility/api';
export async function loadChartStructure(driver: EngineDriver, conid, database, sql) {
const select: Select = {
commandType: 'select',
selectAll: true,
topRecords: 1,
from: {
subQueryString: sql,
alias: 'subq',
},
};
const resp = await apiCall('database-connections/sql-select', { conid, database, select });
if (resp.errorMessage) throw new Error(resp.errorMessage);
return resp.columns.map(x => x.columnName);
}
export async function loadChartData(driver: EngineDriver, conid, database, sql, config) {
const dataColumns = extractDataColumns(config);
const { labelColumn, truncateFrom, truncateLimit, showRelativeValues } = config;
if (!labelColumn || !dataColumns || dataColumns.length == 0) return null;
const select: Select = {
commandType: 'select',
columns: [
{
exprType: 'column',
source: { alias: 'subq' },
columnName: labelColumn,
alias: labelColumn,
},
// @ts-ignore
...dataColumns.map(columnName => ({
exprType: 'call',
func: 'SUM',
args: [
{
exprType: 'column',
columnName,
source: { alias: 'subq' },
},
],
alias: columnName,
})),
],
topRecords: truncateLimit || 100,
from: {
subQueryString: sql,
alias: 'subq',
},
groupBy: [
{
exprType: 'column',
source: { alias: 'subq' },
columnName: labelColumn,
},
],
orderBy: [
{
exprType: 'column',
source: { alias: 'subq' },
columnName: labelColumn,
direction: truncateFrom == 'end' ? 'DESC' : 'ASC',
},
],
};
const resp = await apiCall('database-connections/sql-select', { conid, database, select });
let { rows, columns, errorMessage } = resp;
if (errorMessage) {
throw new Error(errorMessage);
}
if (truncateFrom == 'end' && rows) {
rows = _.reverse([...rows]);
}
if (showRelativeValues) {
const maxValues = dataColumns.map(col => _.max(rows.map(row => row[col])));
for (const [col, max] of _.zip(dataColumns, maxValues)) {
if (!max) continue;
if (!_.isNumber(max)) continue;
if (!(max > 0)) continue;
rows = rows.map(row => ({
...row,
[col]: (row[col] / max) * 100,
}));
// columns = columns.map((x) => {
// if (x.columnName == col) {
// return { columnName: `${col} %` };
// }
// return x;
// });
}
}
console.log('Loaded chart data', { columns, rows });
return {
columns,
rows,
};
}
export function extractDataColumns(values) {
const dataColumns = [];
for (const key in values) {
if (key.startsWith('dataColumn_') && values[key]) {
dataColumns.push(key.substring('dataColumn_'.length));
}
}
return dataColumns;
}
export function extractDataColumnColors(values, dataColumns) {
const res = {};
for (const column of dataColumns) {
const color = values[`dataColumnColor_${column}`];
if (color) res[column] = color;
}
return res;
}
export function extractDataColumnLabels(values, dataColumns) {
const res = {};
for (const column of dataColumns) {
const label = values[`dataColumnLabel_${column}`];
if (label) res[column] = label;
}
return res;
}

View File

@@ -1,118 +0,0 @@
<script lang="ts" context="module">
const getCurrentEditor = () => getActiveComponent('ChartTab');
registerFileCommands({
idPrefix: 'chart',
category: 'Chart',
getCurrentEditor,
folder: 'charts',
format: 'json',
fileExtension: 'chart',
undoRedo: true,
});
</script>
<script lang="ts">
import _ from 'lodash';
import { derived } from 'svelte/store';
import ChartEditor from '../charts/ChartEditor.svelte';
import invalidateCommands from '../commands/invalidateCommands';
import registerCommand from '../commands/registerCommand';
import { registerFileCommands } from '../commands/stdCommands';
import ErrorInfo from '../elements/ErrorInfo.svelte';
import LoadingInfo from '../elements/LoadingInfo.svelte';
import useEditorData from '../query/useEditorData';
import { getContextMenu, registerMenu } from '../utility/contextMenu';
import createActivator, { getActiveComponent } from '../utility/createActivator';
import createUndoReducer from '../utility/createUndoReducer';
import resolveApi from '../utility/resolveApi';
export let tabid;
export let conid;
export let database;
export const activator = createActivator('ChartTab', true);
export function getData() {
return $editorState.value || '';
}
const { editorState, editorValue, setEditorData } = useEditorData({
tabid,
onInitialData: value => {
dispatchModel({ type: 'reset', value });
},
});
const [modelState, dispatchModel] = createUndoReducer({
tables: [],
references: [],
columns: [],
});
$: setEditorData($modelState.value);
$: {
$modelState;
invalidateCommands();
}
const setConfig = config =>
// @ts-ignore
dispatchModel({
type: 'compute',
compute: v => ({ ...v, config: _.isFunction(config) ? config(v.config) : config }),
});
const configDerivedStore = derived(modelState, ($modelState: any) =>
$modelState.value ? $modelState.value.config || {} : {}
);
const configStore = {
...configDerivedStore,
update: setConfig,
set: setConfig,
};
export function canUndo() {
return $modelState.canUndo;
}
export function undo() {
dispatchModel({ type: 'undo' });
}
export function canRedo() {
return $modelState.canRedo;
}
export function redo() {
dispatchModel({ type: 'redo' });
}
registerMenu(
{ command: 'chart.save' },
{ command: 'chart.saveAs' },
{ placeTag: 'export' },
{ divider: true },
{ command: 'chart.undo' },
{ command: 'chart.redo' }
);
</script>
{#if $editorState.isLoading}
<LoadingInfo wrapper message="Loading data" />
{:else if $editorState.errorMessage}
<ErrorInfo message={$editorState.errorMessage} />
{:else}
<ChartEditor
data={$modelState.value && $modelState.value.data}
{configStore}
sql={$modelState.value && $modelState.value.sql}
{conid}
{database}
/>
{/if}

View File

@@ -1,250 +0,0 @@
<script lang="ts" context="module">
const getCurrentEditor = () => getActiveComponent('ProfilerTab');
registerCommand({
id: 'profiler.start',
category: 'Profiler',
name: 'Start profiling',
icon: 'icon play',
testEnabled: () => getCurrentEditor()?.startProfilingEnabled(),
onClick: () => getCurrentEditor().startProfiling(),
});
registerCommand({
id: 'profiler.stop',
category: 'Profiler',
name: 'Stop profiling',
icon: 'icon play-stop',
testEnabled: () => getCurrentEditor()?.stopProfilingEnabled(),
onClick: () => getCurrentEditor().stopProfiling(),
});
registerCommand({
id: 'profiler.save',
category: 'Profiler',
name: 'Save',
icon: 'icon save',
testEnabled: () => getCurrentEditor()?.saveEnabled(),
onClick: () => getCurrentEditor().save(),
});
</script>
<script>
import { findEngineDriver } from 'dbgate-tools';
import { onDestroy, onMount } from 'svelte';
import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte';
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
import invalidateCommands from '../commands/invalidateCommands';
import registerCommand from '../commands/registerCommand';
import JslDataGrid from '../datagrid/JslDataGrid.svelte';
import ErrorInfo from '../elements/ErrorInfo.svelte';
import VerticalSplitter from '../elements/VerticalSplitter.svelte';
import { showModal } from '../modals/modalTools';
import SaveArchiveModal from '../modals/SaveArchiveModal.svelte';
import { currentArchive, selectedWidget } from '../stores';
import { apiCall } from '../utility/api';
import createActivator, { getActiveComponent } from '../utility/createActivator';
import { useConnectionInfo } from '../utility/metadataLoaders';
import { extensions } from '../stores';
import ChartCore from '../charts/ChartCore.svelte';
import LoadingInfo from '../elements/LoadingInfo.svelte';
import randomcolor from 'randomcolor';
export const activator = createActivator('ProfilerTab', true);
export let conid;
export let database;
export let engine;
export let jslidLoad;
let jslidSession;
let isProfiling = false;
let sessionId;
let isLoadingChart = false;
let intervalId;
let chartData;
$: connection = useConnectionInfo({ conid });
$: driver = findEngineDriver(engine || $connection, $extensions);
$: jslid = jslidSession || jslidLoad;
onMount(() => {
intervalId = setInterval(() => {
if (sessionId) {
apiCall('sessions/ping', {
sesid: sessionId,
});
}
}, 15 * 1000);
});
$: {
if (jslidLoad && driver) {
loadChart();
}
}
onDestroy(() => {
clearInterval(intervalId);
});
export async function startProfiling() {
isProfiling = true;
let sesid = sessionId;
if (!sesid) {
const resp = await apiCall('sessions/create', {
conid,
database,
});
sesid = resp.sesid;
sessionId = sesid;
}
const resp = await apiCall('sessions/start-profiler', {
sesid,
});
jslidSession = resp.jslid;
invalidateCommands();
}
export function startProfilingEnabled() {
return conid && database && !isProfiling;
}
async function loadChart() {
isLoadingChart = true;
const colors = randomcolor({
count: driver.profilerChartMeasures.length,
seed: 5,
});
const data = await apiCall('jsldata/extract-timeline-chart', {
jslid,
timestampFunction: driver.profilerTimestampFunction,
aggregateFunction: driver.profilerChartAggregateFunction,
measures: driver.profilerChartMeasures,
});
chartData = {
...data,
labels: data.labels.map(x => new Date(x)),
datasets: data.datasets.map((x, i) => ({
...x,
borderColor: colors[i],
})),
};
isLoadingChart = false;
}
export async function stopProfiling() {
isProfiling = false;
await apiCall('sessions/stop-profiler', { sesid: sessionId });
await apiCall('sessions/kill', { sesid: sessionId });
sessionId = null;
invalidateCommands();
loadChart();
}
export function stopProfilingEnabled() {
return conid && database && isProfiling;
}
export function saveEnabled() {
return !!jslidSession;
}
async function doSave(folder, file) {
await apiCall('archive/save-jsl-data', { folder, file, jslid });
currentArchive.set(folder);
selectedWidget.set('archive');
}
export function save() {
showModal(SaveArchiveModal, {
// folder: archiveFolder,
// file: archiveFile,
onSave: doSave,
});
}
// const data = [
// { year: 2010, count: 10 },
// { year: 2011, count: 20 },
// { year: 2012, count: 15 },
// { year: 2013, count: 25 },
// { year: 2014, count: 22 },
// { year: 2015, count: 30 },
// { year: 2016, count: 28 },
// ];
// {
// labels: data.map(row => row.year),
// datasets: [
// {
// label: 'Acquisitions by year',
// data: data.map(row => row.count),
// },
// ],
// }
</script>
<ToolStripContainer>
{#if jslid}
<VerticalSplitter allowCollapseChild1 allowCollapseChild2>
<svelte:fragment slot="1">
{#key jslid}
<JslDataGrid {jslid} listenInitializeFile formatterFunction={driver?.profilerFormatterFunction} />
{/key}
</svelte:fragment>
<svelte:fragment slot="2">
{#if isLoadingChart}
<LoadingInfo wrapper message="Loading chart" />
{:else}
<ChartCore
title="Profile data"
data={chartData}
options={{
maintainAspectRatio: false,
scales: {
x: {
type: 'time',
distribution: 'linear',
time: {
tooltipFormat: 'D. M. YYYY HH:mm',
displayFormats: {
millisecond: 'HH:mm:ss.SSS',
second: 'HH:mm:ss',
minute: 'HH:mm',
hour: 'D.M hA',
day: 'D. M.',
week: 'D. M. YYYY',
month: 'MM-YYYY',
quarter: '[Q]Q - YYYY',
year: 'YYYY',
},
},
},
},
}}
/>
{/if}
</svelte:fragment>
</VerticalSplitter>
{:else}
<ErrorInfo message="Profiler not yet started" alignTop />
{/if}
<svelte:fragment slot="toolstrip">
<ToolStripCommandButton command="profiler.start" />
<ToolStripCommandButton command="profiler.stop" />
<ToolStripCommandButton command="profiler.save" />
</svelte:fragment>
</ToolStripContainer>

View File

@@ -6,7 +6,6 @@ import * as QueryTab from './QueryTab.svelte';
import * as ShellTab from './ShellTab.svelte';
import * as ArchiveFileTab from './ArchiveFileTab.svelte';
import * as PluginTab from './PluginTab.svelte';
import * as ChartTab from './ChartTab.svelte';
import * as MarkdownEditorTab from './MarkdownEditorTab.svelte';
import * as MarkdownViewTab from './MarkdownViewTab.svelte';
import * as MarkdownPreviewTab from './MarkdownPreviewTab.svelte';
@@ -23,7 +22,6 @@ import * as QueryDataTab from './QueryDataTab.svelte';
import * as ConnectionTab from './ConnectionTab.svelte';
import * as MapTab from './MapTab.svelte';
import * as ServerSummaryTab from './ServerSummaryTab.svelte';
import * as ProfilerTab from './ProfilerTab.svelte';
import * as ImportExportTab from './ImportExportTab.svelte';
import * as SqlObjectTab from './SqlObjectTab.svelte';
@@ -38,7 +36,6 @@ export default {
ShellTab,
ArchiveFileTab,
PluginTab,
ChartTab,
MarkdownEditorTab,
MarkdownViewTab,
MarkdownPreviewTab,
@@ -55,7 +52,6 @@ export default {
ConnectionTab,
MapTab,
ServerSummaryTab,
ProfilerTab,
ImportExportTab,
SqlObjectTab,
...protabs,