Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
wwayne committed May 21, 2024
1 parent db24984 commit 25fcc48
Show file tree
Hide file tree
Showing 5 changed files with 291 additions and 7 deletions.
24 changes: 22 additions & 2 deletions clients/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"Other"
],
"activationEvents": [
"onStartupFinished"
"onStartupFinished",
"onView:chatView"
],
"main": "./dist/node/extension.js",
"browser": "./dist/web/extension.js",
Expand Down Expand Up @@ -241,7 +242,26 @@
"key": "escape",
"when": "inlineSuggestionVisible"
}
]
],
"viewsContainers": {
"activitybar": [
{
"id": "chatView",
"title": "Tabby",
"icon": "assets/logo.png"
}
]
},
"views": {
"chatView": [
{
"type": "webview",
"id": "tabby.chatView",
"name": "",
"visibility": "visible"
}
]
}
},
"scripts": {
"prebuild": "cd ../tabby-agent && yarn build && cd ../tabby-chat-panel && yarn build:vscode",
Expand Down
206 changes: 206 additions & 0 deletions clients/vscode/src/ChatViewProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import { ExtensionContext, Disposable, Webview, WebviewPanel, window, Uri, ViewColumn, WebviewViewProvider, WebviewView, TextDocument } from "vscode";

Check failure on line 1 in clients/vscode/src/ChatViewProvider.ts

View workflow job for this annotation

GitHub Actions / tests

'Disposable' is defined but never used

Check failure on line 1 in clients/vscode/src/ChatViewProvider.ts

View workflow job for this annotation

GitHub Actions / tests

'WebviewPanel' is defined but never used

Check failure on line 1 in clients/vscode/src/ChatViewProvider.ts

View workflow job for this annotation

GitHub Actions / tests

'window' is defined but never used

Check failure on line 1 in clients/vscode/src/ChatViewProvider.ts

View workflow job for this annotation

GitHub Actions / tests

'ViewColumn' is defined but never used
import { getUri, getNonce } from "./utils";

Check failure on line 2 in clients/vscode/src/ChatViewProvider.ts

View workflow job for this annotation

GitHub Actions / tests

'getNonce' is defined but never used

import { createAgentInstance, disposeAgentInstance } from "./agent";

Check failure on line 4 in clients/vscode/src/ChatViewProvider.ts

View workflow job for this annotation

GitHub Actions / tests

'disposeAgentInstance' is defined but never used

/**
* This class manages the state and behavior of HelloWorld webview panels.
*
* It contains all the data and methods for:
*
* - Creating and rendering HelloWorld webview panels
* - Properly cleaning up and disposing of webview resources when the panel is closed
* - Setting the HTML (and by proxy CSS/JavaScript) content of the webview panel
* - Setting message listeners so data can be passed between the webview and extension
*/
export class ChatViewProvider implements WebviewViewProvider {
_view?: WebviewView;
_doc?: TextDocument;
private readonly _extensionUri: Uri
private readonly _context: ExtensionContext

// public static currentPanel: ChatViewPanel | undefined;
// private readonly _panel: WebviewPanel;
// private _disposables: Disposable[] = [];

/**
* The ChatViewPanel class private constructor (called only from the render method).
*
* @param panel A reference to the webview panel
* @param extensionUri The URI of the directory containing the extension
*/
// private constructor(panel: WebviewPanel, extensionUri: Uri) {
// this._panel = panel;

// // Set an event listener to listen for when the panel is disposed (i.e. when the user closes
// // the panel or when the panel is closed programmatically)
// this._panel.onDidDispose(() => this.dispose(), null, this._disposables);

// // Set the HTML content for the webview panel
// this._panel.webview.html = this._getWebviewContent(this._panel.webview, extensionUri);

// // Set an event listener to listen for messages passed from the webview context
// this._setWebviewMessageListener(this._panel.webview);
// }

constructor(context: ExtensionContext) {
this._extensionUri = context.extensionUri
this._context = context;
}

/**
* Renders the current webview panel if it exists otherwise a new webview panel
* will be created and displayed.
*
* @param extensionUri The URI of the directory containing the extension.
*/
// public static render(extensionUri: Uri) {
// if (ChatViewPanel.currentPanel) {
// // If the webview panel already exists reveal it
// ChatViewPanel.currentPanel._panel.reveal(ViewColumn.One);
// } else {
// // If a webview panel does not already exist create and show a new one
// const panel = window.createWebviewPanel(
// // Panel view type
// "showHelloWorld",
// // Panel title
// "Hello World",
// // The editor column the panel should be displayed in
// ViewColumn.One,
// // Extra panel configurations
// {
// // Enable JavaScript in the webview
// enableScripts: true,
// // Restrict the webview to only load resources from the `out` and `webview-ui/build` directories
// localResourceRoots: [Uri.joinPath(extensionUri, "out"), Uri.joinPath(extensionUri, "webview/build")],
// }
// );

// ChatViewPanel.currentPanel = new ChatViewPanel(panel, extensionUri);
// }
// }

public async resolveWebviewView(webviewView: WebviewView) {
this._view = webviewView;

webviewView.webview.options = {
enableScripts: true,
localResourceRoots: [this._extensionUri],
};
webviewView.webview.html = await this._getWebviewContent(webviewView.webview, this._extensionUri);
webviewView.webview.onDidReceiveMessage((data) => {
console.log('onDidReceiveMessage data', data)
switch (data.command) {
case 'explainThis': {
const { text, language } = data;
console.log('text', text)
console.log('language', language)
return;
}
}
});
console.log('resolveWebviewView this._view', this._view)

}

/**
* Cleans up and disposes of webview resources when the webview panel is closed.
*/
// public dispose() {
// ChatViewPanel.currentPanel = undefined;

// // Dispose of the current webview panel
// this._panel.dispose();

// // Dispose of all disposables (i.e. commands) for the current webview panel
// while (this._disposables.length) {
// const disposable = this._disposables.pop();
// if (disposable) {
// disposable.dispose();
// }
// }
// }

/**
* Defines and returns the HTML that should be rendered within the webview panel.
*
* @remarks This is also the place where references to the React webview build files
* are created and inserted into the webview HTML.
*
* @param webview A reference to the extension webview
* @param extensionUri The URI of the directory containing the extension
* @returns A template string literal containing the HTML that should be
* rendered within the webview panel
*/
private async _getWebviewContent(webview: Webview, extensionUri: Uri) {
const agent = await createAgentInstance(this._context);
const { server } = agent.getConfig()
const scriptUri = getUri(webview, extensionUri, ['webview', "index.js"]);
// Tip: Install the es6-string-html VS Code extension to enable code highlighting below

console.log(server.endpoint)
return /*html*/ `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tabby</title>
<style>
html, body, iframe {
padding: 0;
margin: 0;
box-sizing: border-box;
border-width: 0;
}
</style>
</head>
<body style="height:100vh">
<div id="root" style="height:100%;box-sizing:border-box;overflow:hidden"></div>
<script>window.endpoint="${server.endpoint}"</script>
<script>window.token="${server.token}"</script>
<script type="module" src="${scriptUri}"></script>
</body>
</html>
`;
}

/**
* Sets up an event listener to listen for messages passed from the webview context and
* executes code based on the message that is recieved.
*
* @param webview A reference to the extension webview
* @param context A reference to the extension context
*/
// private _setWebviewMessageListener(webview: Webview) {
// webview.onDidReceiveMessage(
// (message: any) => {
// const command = message.command;
// const text = message.text;

// switch (command) {
// case "hello":
// // Code that should run in response to the hello message command
// window.showInformationMessage(text);
// return;
// // Add more switch case statements here as more webview message commands
// // are created within the webview context (i.e. inside media/main.js)
// }
// },
// undefined,
// this._disposables
// );
// }

public revive(panel: WebviewView) {
this._view = panel;
}

public postMessage (message: any) {
if (this._view) {
console.log('this._view exist')
console.log('this._view?.webview.', this._view?.webview)
this._view?.webview.postMessage(message)
}
}
}
15 changes: 14 additions & 1 deletion clients/vscode/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import { ExtensionContext, commands, languages, workspace } from "vscode";
import { ExtensionContext, commands, languages, workspace, window } from "vscode";
import { getLogger } from "./logger";
import { createAgentInstance, disposeAgentInstance } from "./agent";
import { tabbyCommands } from "./commands";
import { TabbyCompletionProvider } from "./TabbyCompletionProvider";
import { TabbyStatusBarItem } from "./TabbyStatusBarItem";
import { RecentlyChangedCodeSearch } from "./RecentlyChangedCodeSearch";
import { ChatViewProvider } from "./ChatViewProvider";

const logger = getLogger();

Expand Down Expand Up @@ -39,6 +40,18 @@ export async function activate(context: ExtensionContext) {
const statusBarItem = new TabbyStatusBarItem(context, completionProvider);
context.subscriptions.push(statusBarItem.register());

// Register the Sidebar Panel
const chatViewProvider = new ChatViewProvider(context);
context.subscriptions.push(
window.registerWebviewViewProvider(
"tabby.chatView",
chatViewProvider,
{
webviewOptions: { retainContextWhenHidden: true },
},
)
);

context.subscriptions.push(...tabbyCommands(context, completionProvider, statusBarItem));

const updateIsChatEnabledContextVariable = () => {
Expand Down
22 changes: 21 additions & 1 deletion clients/vscode/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { commands, Position, Range, SemanticTokens, SemanticTokensLegend, TextDocument } from "vscode";
import { commands, Position, Range, SemanticTokens, SemanticTokensLegend, TextDocument, Uri, Webview } from "vscode";

export type SemanticSymbolInfo = {
position: Position;
Expand Down Expand Up @@ -133,3 +133,23 @@ export function extractNonReservedWordList(text: string): string {
...new Set(text.match(re)?.filter((symbol) => symbol.length > 2 && !reservedKeywords.includes(symbol))).values(),
].join(" ");
}

/**
* This function is primarily used to help enforce content security
* policies for resources/scripts being executed in a webview context.
*/
export function getNonce() {
let text = "";
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (let i = 0; i < 32; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}

/**
* A helper function which will get the webview URI of a given file or resource.
*/
export function getUri(webview: Webview, extensionUri: Uri, pathList: string[]) {
return webview.asWebviewUri(Uri.joinPath(extensionUri, ...pathList));
}
31 changes: 28 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3888,7 +3888,16 @@ stream-shift@^1.0.0:
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==

"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0:
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

string-width@^4.1.0, string-width@^4.2.0:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand Down Expand Up @@ -3920,7 +3929,14 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"

"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"

strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
Expand Down Expand Up @@ -4433,7 +4449,16 @@ [email protected]:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==

"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
Expand Down

0 comments on commit 25fcc48

Please sign in to comment.