refactor: Refactor VSCode extension using React.

This commit is contained in:
Rankin Zheng 2023-05-04 16:09:19 +08:00
parent cad699681b
commit 84222db4cc
27 changed files with 10558 additions and 402 deletions

View File

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 115 KiB

54
assets/chatPanel.html Normal file
View File

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Meta tags for responsiveness and charset -->
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Chat Panel</title>
<!-- Font Awesome icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.0/css/all.min.css" />
<link rel="stylesheet" href="<vscode-resource:/assets/chatPanel.css>" />
<script src="https://cdn.jsdelivr.net/npm/markdown-it/dist/markdown-it.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/prism.min.js"></script>
</head>
<body>
<div id="chat-container">
<div id="messages-container">
<!-- Chat messages will be added here dynamically -->
</div>
<div id="input-resize-handle" class="input-resize-handle"></div>
<div id="input-container">
<textarea id="message-input" placeholder="Type your message..."></textarea>
<button id="send-button">Send</button>
</div>
</div>
<div class="context-menu" id="context-menu">
<div class="context-menu-item" id="menu-item-1">Insert Code</div>
</div>
<!-- Load the chatPanel.js file -->
<script src="<vscode-resource:/assets/messageUtil.js>"></script>
<script src="<vscode-resource:/assets/codeBlock.js>"></script>
<script src="<vscode-resource:/assets/markdown.js>"></script>
<script src="<vscode-resource:/assets/contextMenu.js>"></script>
<script src="<vscode-resource:/assets/inputContainer.js>"></script>
<script src="<vscode-resource:/assets/messageContainer.js>"></script>
<script src="<vscode-resource:/assets/actionButton.js>"></script>
<script src="<vscode-resource:/assets/chatUI.js>"></script>
<script src="<vscode-resource:/assets/resizeInput.js>"></script>
<script src="<vscode-resource:/assets/clipboard.js>"></script>
<script src="<vscode-resource:/assets/main.js>"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
Prism.highlightAll();
});
</script>
</body>
</html>

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

3
babel.config.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
presets: ['@babel/preset-env', '@babel/preset-react'],
};

View File

@ -1,51 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Meta tags for responsiveness and charset -->
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Chat Panel</title>
<!-- Font Awesome icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.0/css/all.min.css" />
<link rel="stylesheet" href="<vscode-resource:/chatPanel.css>" />
<script src="https://cdn.jsdelivr.net/npm/markdown-it/dist/markdown-it.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/prism.min.js"></script>
</head>
<body>
<div id="chat-container">
<div id="messages-container">
<!-- Chat messages will be added here dynamically -->
</div>
<div id="input-resize-handle" class="input-resize-handle"></div>
<div id="input-container">
<textarea id="message-input" placeholder="Type your message..."></textarea>
<button id="send-button">Send</button>
</div>
</div>
<div class="context-menu" id="context-menu">
<div class="context-menu-item" id="menu-item-1">Insert Code</div>
</div>
<!-- Load the chatPanel.js file -->
<script src="<vscode-resource:/messageUtil.js>"></script>
<script src="<vscode-resource:/codeBlock.js>"></script>
<script src="<vscode-resource:/markdown.js>"></script>
<script src="<vscode-resource:/contextMenu.js>"></script>
<script src="<vscode-resource:/inputContainer.js>"></script>
<script src="<vscode-resource:/messageContainer.js>"></script>
<script src="<vscode-resource:/actionButton.js>"></script>
<script src="<vscode-resource:/chatUI.js>"></script>
<script src="<vscode-resource:/resizeInput.js>"></script>
<script src="<vscode-resource:/clipboard.js>"></script>
<script src="<vscode-resource:/main.js>"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
Prism.highlightAll();
});
</script>
</body>
</html>

10659
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,12 @@
],
"activationEvents": [],
"main": "./dist/extension.js",
"files": [
"dist/*",
"assets/*",
"LICENSE",
"README.md"
],
"contributes": {
"views": {
"explorer": [
@ -71,31 +77,61 @@
"watch-tests": "tsc -p . -w --outDir out",
"pretest": "npm run compile-tests && npm run compile && npm run lint",
"lint": "eslint src --ext ts",
"test": "node ./out/test/runTest.js"
"test": "node ./out/test/runTest.js",
"build": "webpack --config webpack.config.js",
"dev": "webpack serve --config webpack.config.js --open"
},
"devDependencies": {
"@babel/core": "^7.21.8",
"@babel/preset-env": "^7.21.5",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.21.5",
"@types/glob": "^8.1.0",
"@types/jest": "^29.5.1",
"@types/mocha": "^10.0.1",
"@types/node": "16.x",
"@types/react-dom": "^18.2.3",
"@types/shell-escape": "^0.2.1",
"@types/vscode": "^1.77.0",
"@typescript-eslint/eslint-plugin": "^5.56.0",
"@typescript-eslint/parser": "^5.56.0",
"@vscode/test-electron": "^2.3.0",
"babel-loader": "^9.1.2",
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.3",
"dotenv": "^16.0.3",
"eslint": "^8.36.0",
"file-loader": "^6.2.0",
"glob": "^8.1.0",
"html-webpack-plugin": "^5.5.1",
"jest": "^29.5.0",
"json-loader": "^0.5.7",
"mocha": "^10.2.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.0.5",
"redux": "^4.2.1",
"style-loader": "^3.3.2",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.2",
"typescript": "^4.9.5",
"url-loader": "^4.1.1",
"webpack": "^5.76.3",
"webpack-cli": "^5.0.1"
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.13.3"
},
"dependencies": {
"@emotion/react": "^11.10.8",
"@mantine/core": "^6.0.10",
"@mantine/dropzone": "^6.0.10",
"@mantine/hooks": "^6.0.10",
"@mantine/prism": "^6.0.10",
"@mantine/tiptap": "^6.0.10",
"@tabler/icons-react": "^2.17.0",
"@tiptap/extension-link": "^2.0.3",
"@tiptap/react": "^2.0.3",
"@tiptap/starter-kit": "^2.0.3",
"axios": "^1.3.6",
"dotenv": "^16.0.3",
"node-fetch": "^3.3.1",
"nonce": "^1.0.4",
"openai": "^3.2.1",

View File

@ -37,7 +37,7 @@ export default class ChatPanel {
column || vscode.ViewColumn.One,
{
enableScripts: true,
localResourceRoots: [vscode.Uri.joinPath(extensionUri, 'media')],
localResourceRoots: [vscode.Uri.joinPath(extensionUri, 'dist')],
retainContextWhenHidden: true
}
);
@ -55,7 +55,7 @@ export default class ChatPanel {
private setWebviewOptions(extensionUri: vscode.Uri) {
this._panel.webview.options = {
enableScripts: true,
localResourceRoots: [vscode.Uri.joinPath(extensionUri, 'media')],
localResourceRoots: [vscode.Uri.joinPath(extensionUri, 'dist')],
};
}
@ -83,11 +83,12 @@ export default class ChatPanel {
// Get the HTML content for the panel
private _getHtmlContent(extensionUri: vscode.Uri): string {
const htmlPath = vscode.Uri.joinPath(extensionUri, 'media', 'chatPanel.html');
const htmlPath = vscode.Uri.joinPath(extensionUri, 'dist', 'assets', 'chatPanel.html');
// const htmlPath = vscode.Uri.joinPath(extensionUri, 'dist', 'index.html');
const htmlContent = fs.readFileSync(htmlPath.fsPath, 'utf8');
return htmlContent.replace(/<vscode-resource:(\/.+?)>/g, (_, resourcePath) => {
const resourceUri = vscode.Uri.joinPath(extensionUri, 'media', resourcePath);
const resourceUri = vscode.Uri.joinPath(extensionUri, 'dist', resourcePath);
return this._panel.webview.asWebviewUri(resourceUri).toString();
});
}

13
src/views/App.tsx Normal file
View File

@ -0,0 +1,13 @@
import React from 'react';
import { Button } from '@mantine/core';
const App = () => {
return (
<div>
<h1>Hello World!</h1>
<Button>Click me!</Button>
</div>
);
};
export default App;

0
src/views/index.css Normal file
View File

18
src/views/index.html Normal file
View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Extension</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css"
integrity="sha512-1lCvz8lRFF66HJzKpD6A+U6IVaUkV9U6xuUQVdqfdNV8YJ7zjKwJRYI95gUZ8JpWj1O9x6UuV2v0pz1yaYWX7w=="
crossorigin="anonymous" referrerpolicy="no-referrer" />
</head>
<body>
<div id="root"></div>
<script src="<vscode-resource:/index.js>"></script>
</body>
</html>

10
src/views/index.tsx Normal file
View File

@ -0,0 +1,10 @@
import * as React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { MantineProvider } from '@mantine/core';
ReactDOM.render(
<MantineProvider withGlobalStyles withNormalizeCSS>
<App />
</MantineProvider>,
document.getElementById('root'));

View File

@ -3,12 +3,15 @@
"module": "commonjs",
"target": "ES2020",
"lib": [
"ES2020"
"ES2020",
"es6",
"dom"
],
"outDir": "./dist",
"sourceMap": true,
"rootDir": "src",
"strict": true
"strict": true,
"jsx": "react",
"esModuleInterop": true
}
}

View File

@ -3,6 +3,9 @@
'use strict';
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { DefinePlugin } = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
//@ts-check
/** @typedef {import('webpack').Configuration} WebpackConfig **/
@ -12,11 +15,14 @@ const extensionConfig = {
target: 'node', // VS Code extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/
mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/
entry: {
extension: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/
index: './src/views/index.tsx'
},
output: {
// the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/
path: path.resolve(__dirname, 'dist'),
filename: 'extension.js',
filename: '[name].js',
libraryTarget: 'commonjs2'
},
externals: {
@ -25,18 +31,66 @@ const extensionConfig = {
},
resolve: {
// support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
extensions: ['.ts', '.js']
extensions: ['.ts', '.tsx', '.js', '.json']
},
module: {
rules: [
{
test: /\.ts$/,
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-react',
'@babel/preset-typescript'
]
}
},
{
loader: 'ts-loader'
}
]
},
{
test: /\.jsx?$/,
use: 'babel-loader',
exclude: /node_modules/,
},
{
test: /\.css$/i,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
options: {
modules: {
localIdentName: '[name]__[local]___[hash:base64:5]'
}
}
}
],
include: /views/
},
{
test: /\.json$/i,
use: 'json-loader',
type: 'asset/source'
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
},
},
],
}
]
},
@ -44,5 +98,23 @@ const extensionConfig = {
infrastructureLogging: {
level: "log", // enables logging required for problem matchers
},
plugins: [
// generate an HTML file that includes the extension's JavaScript file
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'src', 'views', 'index.html'),
filename: 'index.html',
chunks: ['index']
}),
new CopyWebpackPlugin({
patterns: [
{ from: 'assets', to: 'assets' },
],
}),
// define global variables
new DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development')
})
]
};
module.exports = [extensionConfig];