Skip to content

Commit

Permalink
fix(vscode): chat panel display issues (#2276)
Browse files Browse the repository at this point in the history
* fix(vscode): chat panel display issues

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

* fix

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

* fix

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* fix

* update

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
  • Loading branch information
wwayne and autofix-ci[bot] authored May 29, 2024
1 parent d4494e8 commit fcdfc8a
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 41 deletions.
1 change: 1 addition & 0 deletions clients/vscode/.gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.png filter=lfs diff=lfs merge=lfs -text
*.svg filter=lfs diff=lfs merge=lfs -text
31 changes: 31 additions & 0 deletions clients/vscode/assets/chat-panel.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,34 @@ iframe {
width: 100%;
height: 100vh;
}

/* Static content page */
.static-content {
padding: 0.65rem 1.2rem;
}

.static-content .avatar {
display: flex;
align-items: center;
}

.static-content .avatar img {
width: 1rem;
height: 1rem;
object-fit: contain;
border-radius: 100%;
margin-right: 0.4rem;
padding: 0.2rem;
border: 1px solid var(--vscode-editorWidget-border);
background-color: rgb(232, 226, 210);
}

.static-content .title {
margin: 0.45rem 0 0;
font-size: 0.85rem;
}

.static-content p {
line-height: 1.45;
margin: 0.45rem 0;
}
4 changes: 3 additions & 1 deletion clients/vscode/assets/side-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions clients/vscode/assets/tabby.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
128 changes: 88 additions & 40 deletions clients/vscode/src/chat/ChatViewProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export class ChatViewProvider implements WebviewViewProvider {
webview?: WebviewView;
client?: ServerApi;
private pendingMessages: ChatMessage[] = [];
private isReady = false;

constructor(
private readonly context: ExtensionContext,
Expand All @@ -19,27 +20,19 @@ export class ChatViewProvider implements WebviewViewProvider {

public async resolveWebviewView(webviewView: WebviewView) {
this.webview = webviewView;
this.isReady = this.agent.status === "ready";
const extensionUri = this.context.extensionUri;

webviewView.webview.options = {
enableScripts: true,
localResourceRoots: [extensionUri],
};
// FIXME: we need to wait for the server to be ready, consider rendering a loading indicator
if (this.agent.status !== "ready") {
await new Promise<void>((resolve) => {
this.agent.on("didChangeStatus", (status) => {
if (status === "ready") {
resolve();
}
});
});

if (this.isReady) {
await this.renderChatPage();
} else {
webviewView.webview.html = this.getWelcomeContent();
}
const serverInfo = await this.agent.fetchServerInfo();
webviewView.webview.html = this.getWebviewContent(serverInfo);
this.agent.on("didUpdateServerInfo", (serverInfo: ServerInfo) => {
webviewView.webview.html = this.getWebviewContent(serverInfo);
});

this.client = createClient(webviewView, {
navigate: async (context: Context) => {
Expand All @@ -50,18 +43,27 @@ export class ChatViewProvider implements WebviewViewProvider {
},
});

this.agent.on("didChangeStatus", async (status) => {
if (status === "ready" && !this.isReady) {
this.isReady = true;
await this.renderChatPage();
}
});

this.agent.on("didUpdateServerInfo", async (serverInfo: ServerInfo) => {
await this.renderChatPage(serverInfo);
});

// The event will not be triggered during the initial rendering.
webviewView.onDidChangeVisibility(async () => {
if (webviewView.visible) {
await this.initChatPage();
}
});

webviewView.webview.onDidReceiveMessage(async (message) => {
if (message.action === "rendered") {
this.webview?.webview.postMessage({ action: "sync-theme" });
this.pendingMessages.forEach((message) => this.client?.sendMessage(message));
const serverInfo = await this.agent.fetchServerInfo();
if (serverInfo.config.token) {
this.client?.init({
fetcherOptions: {
authorization: serverInfo.config.token,
},
});
}
await this.initChatPage();
}
});

Expand All @@ -72,6 +74,15 @@ export class ChatViewProvider implements WebviewViewProvider {
});
}

private async renderChatPage(serverInfo?: ServerInfo) {
if (!serverInfo) {
serverInfo = await this.agent.fetchServerInfo();
}
if (this.webview) {
this.webview.webview.html = this.getWebviewContent(serverInfo);
}
}

private isChatPanelAvailable(serverInfo: ServerInfo): boolean {
if (!serverInfo.health || !serverInfo.health["webserver"] || !serverInfo.health["chat_model"]) {
return false;
Expand All @@ -94,24 +105,28 @@ export class ChatViewProvider implements WebviewViewProvider {
return true;
}

private async initChatPage() {
this.webview?.webview.postMessage({ action: "sync-theme" });
this.pendingMessages.forEach((message) => this.client?.sendMessage(message));
const serverInfo = await this.agent.fetchServerInfo();
if (serverInfo.config.token) {
this.client?.init({
fetcherOptions: {
authorization: serverInfo.config.token,
},
});
}
}

private getWebviewContent(serverInfo: ServerInfo) {
if (!this.isChatPanelAvailable(serverInfo)) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
</head>
<body>
<h2>Tabby is not available</h2>
<ul>
<li>Please update to <a href="https://github.com/TabbyML/tabby/releases" target="_blank">the latest version</a> of the Tabby server.</li>
<li>You also need to launch the server with the chat model enabled; for example, use <code>--chat-model Mistral-7B</code>.</li>
</ul>
</body>
</html>
`;
return this.getStaticContent(`
<h4 class='title'>Tabby is not available</h4>
<p>Please update to <a href="https://github.com/TabbyML/tabby/releases" target="_blank">the latest version</a> of the Tabby server.</p>
<p>You also need to launch the server with the chat model enabled; for example, use <code>--chat-model Mistral-7B</code>.</p>
`);
}

const endpoint = serverInfo.config.endpoint;
const styleUri = this.webview?.webview.asWebviewUri(
Uri.joinPath(this.context.extensionUri, "assets", "chat-panel.css"),
Expand All @@ -123,7 +138,6 @@ export class ChatViewProvider implements WebviewViewProvider {
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tabby</title>
<link href="${endpoint}" rel="preconnect">
<link href="${styleUri}" rel="stylesheet">
<script defer>
Expand Down Expand Up @@ -172,6 +186,40 @@ export class ChatViewProvider implements WebviewViewProvider {
`;
}

// The content is displayed before the server is ready
private getWelcomeContent() {
return this.getStaticContent(`
<h4 class='title'>Welcome to Tabby Chat!</h4>
<p>Before you can start chatting, please take a moment to set up your credentials to connect to the Tabby server.</p>
`);
}

private getStaticContent(htmlContent: string) {
const logoUri = this.webview?.webview.asWebviewUri(Uri.joinPath(this.context.extensionUri, "assets", "tabby.png"));
const styleUri = this.webview?.webview.asWebviewUri(
Uri.joinPath(this.context.extensionUri, "assets", "chat-panel.css"),
);
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="${styleUri}" rel="stylesheet">
</head>
<body>
<main class='static-content'>
<div class='avatar'>
<img src="${logoUri}" />
<p>Tabby</p>
</div>
${htmlContent}
</main>
</body>
</html>
`;
}

public getWebview() {
return this.webview;
}
Expand Down

0 comments on commit fcdfc8a

Please sign in to comment.