diff --git a/packages/web/src/App.svelte b/packages/web/src/App.svelte
index 2461869a7..8d5e288db 100644
--- a/packages/web/src/App.svelte
+++ b/packages/web/src/App.svelte
@@ -1,6 +1,7 @@
+
diff --git a/packages/web/src/plugins/PluginsList.js b/packages/web/src/plugins/PluginsList.js
new file mode 100644
index 000000000..5fb4e197a
--- /dev/null
+++ b/packages/web/src/plugins/PluginsList.js
@@ -0,0 +1,87 @@
+// import React from 'react';
+// import styled from 'styled-components';
+// import useTheme from '../theme/useTheme';
+// import { useSetOpenedTabs } from '../utility/globalState';
+// import { extractPluginIcon, extractPluginAuthor } from '../plugins/manifestExtractors';
+// import useOpenNewTab from '../utility/useOpenNewTab';
+
+// const Wrapper = styled.div`
+// margin: 1px 3px 10px 5px;
+// display: flex;
+// align-items: center;
+// &:hover {
+// background-color: ${props => props.theme.left_background_blue[1]};
+// }
+// `;
+
+// const Texts = styled.div`
+// margin-left: 10px;
+// `;
+
+// const Name = styled.div`
+// font-weight: bold;
+// `;
+
+// const Line = styled.div`
+// display: flex;
+// `;
+
+// const Icon = styled.img`
+// width: 50px;
+// height: 50px;
+// `;
+
+// const Description = styled.div`
+// font-style: italic;
+// `;
+
+// const Author = styled.div`
+// font-weight: bold;
+// `;
+
+// const Version = styled.div`
+// margin-left: 5px;
+// `;
+
+// function openPlugin(openNewTab, packageManifest) {
+// openNewTab({
+// title: packageManifest.name,
+// icon: 'icon plugin',
+// tabComponent: 'PluginTab',
+// props: {
+// packageName: packageManifest.name,
+// },
+// });
+// }
+
+// function PluginsListItem({ packageManifest }) {
+// const openNewTab = useOpenNewTab();
+// const theme = useTheme();
+// return (
+// openPlugin(openNewTab, packageManifest)} theme={theme}>
+//
+//
+//
+// {packageManifest.name}
+// {packageManifest.version}
+//
+//
+// {packageManifest.description}
+//
+//
+// {extractPluginAuthor(packageManifest)}
+//
+//
+//
+// );
+// }
+
+// export default function PluginsList({ plugins }) {
+// return (
+// <>
+// {plugins.map(packageManifest => (
+//
+// ))}
+// >
+// );
+// }
diff --git a/packages/web/src/plugins/PluginsProvider.svelte b/packages/web/src/plugins/PluginsProvider.svelte
new file mode 100644
index 000000000..ba7cf2345
--- /dev/null
+++ b/packages/web/src/plugins/PluginsProvider.svelte
@@ -0,0 +1,74 @@
+
+
+
diff --git a/packages/web/src/plugins/fileformats.ts b/packages/web/src/plugins/fileformats.ts
new file mode 100644
index 000000000..2f8fddf86
--- /dev/null
+++ b/packages/web/src/plugins/fileformats.ts
@@ -0,0 +1,34 @@
+import { FileFormatDefinition } from 'dbgate-types';
+
+const jsonlFormat = {
+ storageType: 'jsonl',
+ extension: 'jsonl',
+ name: 'JSON lines',
+ readerFunc: 'jsonLinesReader',
+ writerFunc: 'jsonLinesWriter',
+};
+
+export function buildFileFormats(plugins): FileFormatDefinition[] {
+ const res = [jsonlFormat];
+ for (const { content } of plugins) {
+ const { fileFormats } = content;
+ if (fileFormats) res.push(...fileFormats);
+ }
+ return res;
+}
+
+export function findFileFormat(extensions, storageType) {
+ return extensions.fileFormats.find(x => x.storageType == storageType);
+}
+
+export function getFileFormatDirections(format) {
+ if (!format) return [];
+ const res = [];
+ if (format.readerFunc) res.push('source');
+ if (format.writerFunc) res.push('target');
+ return res;
+}
+
+export function getDefaultFileFormat(extensions) {
+ return extensions.fileFormats.find(x => x.storageType == 'csv') || jsonlFormat;
+}
diff --git a/packages/web/src/plugins/manifestExtractors.ts b/packages/web/src/plugins/manifestExtractors.ts
new file mode 100644
index 000000000..32f62ef65
--- /dev/null
+++ b/packages/web/src/plugins/manifestExtractors.ts
@@ -0,0 +1,21 @@
+import _ from 'lodash';
+
+export function extractPluginIcon(packageManifest) {
+ const { links } = packageManifest || {};
+ const { repository } = links || {};
+ const homepage = (links && links.homepage) || packageManifest.homepage;
+ const tested = repository || homepage || packageManifest.homepage;
+
+ if (tested) {
+ const match = tested.match(/https:\/\/github.com\/([^/]*)\/([^/]*)/);
+ if (match) {
+ return `https://raw.githubusercontent.com/${match[1]}/${match[2]}/master/icon.svg`;
+ }
+ }
+ // eslint-disable-next-line no-undef
+ return `${process.env.PUBLIC_URL}/unknown.svg`;
+}
+
+export function extractPluginAuthor(packageManifest) {
+ return _.isPlainObject(packageManifest.author) ? packageManifest.author.name : packageManifest.author;
+}
diff --git a/packages/web/src/stores.ts b/packages/web/src/stores.ts
index d73400b98..15f9e542f 100644
--- a/packages/web/src/stores.ts
+++ b/packages/web/src/stores.ts
@@ -1,4 +1,5 @@
import { writable } from 'svelte/store';
+import { ExtensionsDirectory } from 'dbgate-types';
interface TabDefinition {
title: string;
@@ -23,5 +24,6 @@ export const selectedWidget = writable('database');
export const openedConnections = writable([]);
export const currentDatabase = writable(null);
export const openedTabs = writableWithStorage([], 'openedTabs');
+export const extensions = writable(null);
// export const leftPanelWidth = writable(300);
diff --git a/packages/web/src/utility/metadataLoaders.ts b/packages/web/src/utility/metadataLoaders.ts
index 195a71c36..b187f2091 100644
--- a/packages/web/src/utility/metadataLoaders.ts
+++ b/packages/web/src/utility/metadataLoaders.ts
@@ -350,11 +350,11 @@ export function useArchiveFolders(args) {
return useCore(archiveFoldersLoader, args);
}
-export function getInstalledPlugins(args) {
+export function getInstalledPlugins(args = {}) {
return getCore(installedPluginsLoader, args) || [];
}
-export function useInstalledPlugins(args) {
- return useCore(installedPluginsLoader, args) || [];
+export function useInstalledPlugins(args = {}) {
+ return useCore(installedPluginsLoader, args);
}
export function getFiles(args) {