diff --git a/erd-editor.code-workspace b/erd-editor.code-workspace index 2a4d0beb..47307109 100644 --- a/erd-editor.code-workspace +++ b/erd-editor.code-workspace @@ -24,6 +24,10 @@ "name": "erd-editor-vscode-bridge", "path": "packages/erd-editor-vscode-bridge" }, + { + "name": "erd-editor-jetbrains-webview", + "path": "packages/erd-editor-jetbrains-webview" + }, { "name": "erd-editor-app", "path": "packages/erd-editor-app" diff --git a/packages/erd-editor-jetbrains-webview/package.json b/packages/erd-editor-jetbrains-webview/package.json new file mode 100644 index 00000000..cbb2e58f --- /dev/null +++ b/packages/erd-editor-jetbrains-webview/package.json @@ -0,0 +1,33 @@ +{ + "name": "@dineug/erd-editor-jetbrains-webview", + "version": "0.1.0", + "private": true, + "description": "Entity-Relationship Diagram Editor jetbrains webview", + "author": "SeungHwan-Lee ", + "license": "MIT", + "scripts": { + "dev": "webpack serve --mode development", + "build": "webpack --mode production", + "build:analyzer": "webpack --mode production --env target=analyzer", + "build:webview": "webpack --mode production --env target=webview" + }, + "devDependencies": { + "@dineug/erd-editor": "workspace:*", + "@dineug/erd-editor-shiki-worker": "workspace:*", + "@dineug/erd-editor-vscode-bridge": "workspace:*", + "@swc/core": "^1.3.101", + "base64-arraybuffer": "^1.0.2", + "core-js": "^3.34.0", + "css-loader": "^6.8.1", + "html-webpack-plugin": "^5.6.0", + "mini-css-extract-plugin": "^2.7.6", + "style-loader": "^3.3.3", + "swc-loader": "^0.2.3", + "tsconfig-paths-webpack-plugin": "^4.1.0", + "typescript": "5.3.3", + "webpack": "^5.89.0", + "webpack-bundle-analyzer": "^4.10.1", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^4.15.1" + } +} diff --git a/packages/erd-editor-jetbrains-webview/public/index.html b/packages/erd-editor-jetbrains-webview/public/index.html new file mode 100644 index 00000000..aee6fa01 --- /dev/null +++ b/packages/erd-editor-jetbrains-webview/public/index.html @@ -0,0 +1,10 @@ + + + + + + + erd-editor + + + diff --git a/packages/erd-editor-jetbrains-webview/src/env.d.ts b/packages/erd-editor-jetbrains-webview/src/env.d.ts new file mode 100644 index 00000000..62b3aed2 --- /dev/null +++ b/packages/erd-editor-jetbrains-webview/src/env.d.ts @@ -0,0 +1,5 @@ +declare global { + interface Window { + cefQuery: (arg: any) => number; + } +} diff --git a/packages/erd-editor-jetbrains-webview/src/main.ts b/packages/erd-editor-jetbrains-webview/src/main.ts new file mode 100644 index 00000000..56e9c15b --- /dev/null +++ b/packages/erd-editor-jetbrains-webview/src/main.ts @@ -0,0 +1,99 @@ +import './webview.css'; + +import { + setExportFileCallback, + setGetShikiServiceCallback, + setImportFileCallback, +} from '@dineug/erd-editor'; +import { + AnyAction, + Emitter, + ThemeOptions, + vscodeExportFileAction, + vscodeImportFileAction, + vscodeInitialAction, + vscodeSaveReplicationAction, + vscodeSaveThemeAction, + vscodeSaveValueAction, +} from '@dineug/erd-editor-vscode-bridge'; +import { encode } from 'base64-arraybuffer'; + +const bridge = new Emitter(); +// const vscode = globalThis.acquireVsCodeApi(); +const editor = document.createElement('erd-editor'); +const sharedStore = editor.getSharedStore({ mouseTracker: false }); + +const dispatch = (action: AnyAction) => { + // vscode.postMessage(action); +}; + +import('@dineug/erd-editor-shiki-worker').then(({ getShikiService }) => { + setGetShikiServiceCallback(getShikiService); +}); +setImportFileCallback(options => { + dispatch(vscodeImportFileAction(options)); +}); +setExportFileCallback(async (blob, options) => { + const arrayBuffer = await blob.arrayBuffer(); + dispatch( + vscodeExportFileAction({ + value: encode(arrayBuffer), + fileName: options.fileName, + }) + ); +}); + +const getSystemTheme = () => + document.body.classList.contains('vscode-light') ? 'light' : 'dark'; + +const handleChange = () => { + dispatch( + vscodeSaveValueAction({ + value: editor.value, + }) + ); +}; + +const handleChangePresetTheme = (event: Event) => { + const e = event as CustomEvent; + dispatch(vscodeSaveThemeAction(e.detail)); +}; + +bridge.on({ + webviewImportFile: ({ payload: { type, value } }) => { + switch (type) { + case 'json': + editor.value = value; + break; + case 'sql': + editor.setSchemaSQL(value); + break; + } + }, + webviewInitialValue: ({ payload: { value } }) => { + // editor.addEventListener('change', handleChange); + editor.addEventListener('changePresetTheme', handleChangePresetTheme); + editor.setInitialValue(value); + editor.enableThemeBuilder = true; + sharedStore.subscribe(actions => { + dispatch(vscodeSaveReplicationAction({ actions })); + }); + document.body.appendChild(editor); + }, + webviewReplication: ({ payload: { actions } }) => { + sharedStore.dispatch(actions); + }, + webviewUpdateTheme: ({ payload }) => { + editor.setPresetTheme({ + ...payload, + appearance: + payload.appearance === 'auto' ? getSystemTheme() : payload.appearance, + }); + }, + webviewReadonly: ({ payload }) => { + editor.readonly = payload; + }, +}); + +globalThis.addEventListener('message', event => bridge.emit(event.data)); +dispatch(vscodeInitialAction()); diff --git a/packages/erd-editor-jetbrains-webview/src/webview.css b/packages/erd-editor-jetbrains-webview/src/webview.css new file mode 100644 index 00000000..0cd60724 --- /dev/null +++ b/packages/erd-editor-jetbrains-webview/src/webview.css @@ -0,0 +1,6 @@ +body { + padding: 0; + margin: 0; + width: 100%; + height: 100vh; +} diff --git a/packages/erd-editor-jetbrains-webview/tsconfig.json b/packages/erd-editor-jetbrains-webview/tsconfig.json new file mode 100644 index 00000000..5745f01e --- /dev/null +++ b/packages/erd-editor-jetbrains-webview/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src"] +} diff --git a/packages/erd-editor-jetbrains-webview/webpack.config.js b/packages/erd-editor-jetbrains-webview/webpack.config.js new file mode 100644 index 00000000..1688b800 --- /dev/null +++ b/packages/erd-editor-jetbrains-webview/webpack.config.js @@ -0,0 +1,119 @@ +const path = require('path'); +const { DefinePlugin } = require('webpack'); +const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); + +const resolvePath = value => path.resolve(__dirname, value); + +module.exports = (env, argv) => { + const isProduction = argv.mode === 'production'; + const isDevelopment = argv.mode !== 'production'; + const mode = isDevelopment ? 'development' : 'production'; + const isAnalyzer = env.target === 'analyzer'; + const isWebview = env.target === 'webview'; + + const config = { + mode, + devtool: isDevelopment ? 'eval-source-map' : 'source-map', + devServer: { + static: { + directory: resolvePath('public'), + }, + compress: true, + historyApiFallback: true, + open: true, + hot: true, + client: { + overlay: false, + }, + }, + entry: './src/main', + output: { + path: isWebview + ? resolvePath('../../../src/main/resources/assets') + : resolvePath('dist'), + publicPath: '/', + filename: isProduction + ? 'static/js/bundle.[contenthash:8].js' + : 'static/js/bundle.js', + chunkFilename: isProduction + ? 'static/js/[id].[contenthash:8].js' + : 'static/js/[name].js', + clean: true, + }, + resolve: { + extensions: ['.ts', '.ts', '.js'], + plugins: [new TsconfigPathsPlugin()], + }, + module: { + rules: [ + { + test: /\.[jt]s$/, + exclude: /node_modules/, + use: { + loader: 'swc-loader', + options: { + env: { + targets: 'defaults', + mode: 'entry', + coreJs: '3.34', + }, + }, + }, + }, + { + test: /\.css$/i, + use: [ + isProduction ? MiniCssExtractPlugin.loader : 'style-loader', + 'css-loader', + ], + }, + { + test: /\.(png|svg|jpg|jpeg|gif)$/i, + type: 'asset/resource', + }, + { + test: /\.(woff|woff2|eot|ttf|otf)$/i, + type: 'asset/resource', + }, + ], + }, + plugins: [ + new DefinePlugin({ + 'import.meta.env.MODE': JSON.stringify(mode), + }), + isProduction && + new MiniCssExtractPlugin({ + filename: 'static/css/bundle.[contenthash:8].css', + chunkFilename: 'static/css/[id].[contenthash:8].css', + }), + new HtmlWebpackPlugin({ + inject: true, + template: resolvePath('public/index.html'), + templateParameters: { + gtag: isProduction ? toGtag() : '', + }, + }), + isAnalyzer && new BundleAnalyzerPlugin(), + ].filter(Boolean), + performance: false, + }; + + return config; +}; + +function toGtag() { + // return /*html*/ ` + // + // `; + return ''; +} diff --git a/packages/erd-editor-vscode-webview/vite.config.ts b/packages/erd-editor-vscode-webview/vite.config.ts index 6ecf4ca4..607cd8a7 100644 --- a/packages/erd-editor-vscode-webview/vite.config.ts +++ b/packages/erd-editor-vscode-webview/vite.config.ts @@ -5,6 +5,7 @@ import { defineConfig, loadEnv } from 'vite'; export default defineConfig(({ command, mode }) => { const envDir = './environment'; const env = loadEnv(mode, envDir); + const target = env.VITE_TARGET; const targetMap = { modern: { @@ -37,11 +38,11 @@ export default defineConfig(({ command, mode }) => { }, }; - const entry = targetMap[env.VITE_TARGET].entry; - const name = targetMap[env.VITE_TARGET].name; - const fileName = targetMap[env.VITE_TARGET].fileName; - const outDir = targetMap[env.VITE_TARGET].outDir; - const emptyOutDir = targetMap[env.VITE_TARGET].emptyOutDir; + const entry = targetMap[target].entry; + const name = targetMap[target].name; + const fileName = targetMap[target].fileName; + const outDir = targetMap[target].outDir; + const emptyOutDir = targetMap[target].emptyOutDir; const isBuild = command === 'build'; return { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ff651aeb..a0b5ea5b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -565,6 +565,60 @@ importers: specifier: ^7.0.0 version: 7.0.0(webpack@5.89.0) + packages/erd-editor-jetbrains-webview: + devDependencies: + '@dineug/erd-editor': + specifier: workspace:* + version: link:../erd-editor + '@dineug/erd-editor-shiki-worker': + specifier: workspace:* + version: link:../erd-editor-shiki-worker + '@dineug/erd-editor-vscode-bridge': + specifier: workspace:* + version: link:../erd-editor-vscode-bridge + '@swc/core': + specifier: ^1.3.101 + version: 1.3.101 + base64-arraybuffer: + specifier: ^1.0.2 + version: 1.0.2 + core-js: + specifier: ^3.34.0 + version: 3.34.0 + css-loader: + specifier: ^6.8.1 + version: 6.8.1(webpack@5.89.0) + html-webpack-plugin: + specifier: ^5.6.0 + version: 5.6.0(webpack@5.89.0) + mini-css-extract-plugin: + specifier: ^2.7.6 + version: 2.7.6(webpack@5.89.0) + style-loader: + specifier: ^3.3.3 + version: 3.3.3(webpack@5.89.0) + swc-loader: + specifier: ^0.2.3 + version: 0.2.3(@swc/core@1.3.101)(webpack@5.89.0) + tsconfig-paths-webpack-plugin: + specifier: ^4.1.0 + version: 4.1.0 + typescript: + specifier: 5.3.3 + version: 5.3.3 + webpack: + specifier: ^5.89.0 + version: 5.89.0(@swc/core@1.3.101)(webpack-cli@5.1.4) + webpack-bundle-analyzer: + specifier: ^4.10.1 + version: 4.10.1 + webpack-cli: + specifier: ^5.1.4 + version: 5.1.4(webpack-bundle-analyzer@4.10.1)(webpack-dev-server@4.15.1)(webpack@5.89.0) + webpack-dev-server: + specifier: ^4.15.1 + version: 4.15.1(webpack-cli@5.1.4)(webpack@5.89.0) + packages/erd-editor-schema: devDependencies: '@dineug/shared':