From d526d4529919f3f720856c0045f784f8f3fad5c1 Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Thu, 13 Feb 2025 09:28:30 +0100 Subject: [PATCH 1/7] add icon --- src/chat-handler.ts | 10 ++++++- src/icons.ts | 18 ++++++++++++ src/llm-models/svg.d.ts | 9 ++++++ style/icons/jupyternaut-lite.svg | 50 ++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 src/icons.ts create mode 100644 src/llm-models/svg.d.ts create mode 100644 style/icons/jupyternaut-lite.svg diff --git a/src/chat-handler.ts b/src/chat-handler.ts index c3e236e..3ef1da3 100644 --- a/src/chat-handler.ts +++ b/src/chat-handler.ts @@ -20,6 +20,14 @@ import { UUID } from '@lumino/coreutils'; import { getErrorMessage } from './llm-models'; import { chatSystemPrompt } from './provider'; import { IAIProvider } from './token'; +import { jupyternautLiteIcon } from './icons'; + +/** + * The base64 encoded SVG string of the jupyternaut lite icon. + * Encode so it can be passed as avatar_url to jupyter-chat. + */ +const AI_AVATAR_BASE64 = btoa(jupyternautLiteIcon.svgstr); +const AI_AVATAR = `data:image/svg+xml;base64,${AI_AVATAR_BASE64}`; export type ConnectionMessage = { type: 'connection'; @@ -87,7 +95,7 @@ export class ChatHandler extends ChatModel { }) ); - const sender = { username: 'AI' }; + const sender = { username: 'AI', avatar_url: AI_AVATAR }; this.updateWriters([sender]); // create an empty message to be filled by the AI provider diff --git a/src/icons.ts b/src/icons.ts new file mode 100644 index 0000000..bca46a2 --- /dev/null +++ b/src/icons.ts @@ -0,0 +1,18 @@ +/* + * Copyright (c) Jupyter Development Team. + * Distributed under the terms of the Modified BSD License. + */ + +import { LabIcon } from '@jupyterlab/ui-components'; + +/** + * This icon is based on the jupyternaut icon from Jupyter AI: + * https://github.com/jupyterlab/jupyter-ai/blob/main/packages/jupyter-ai/style/icons/jupyternaut.svg + * With a small tweak for the colors to match the JupyterLite icon. + */ +import jupyternautLiteSvg from '../style/icons/jupyternaut-lite.svg'; + +export const jupyternautLiteIcon = new LabIcon({ + name: 'jupyter-ai::jupyternaut-lite', + svgstr: jupyternautLiteSvg +}); diff --git a/src/llm-models/svg.d.ts b/src/llm-models/svg.d.ts new file mode 100644 index 0000000..dd61476 --- /dev/null +++ b/src/llm-models/svg.d.ts @@ -0,0 +1,9 @@ +/* + * Copyright (c) Jupyter Development Team. + * Distributed under the terms of the Modified BSD License. + */ + +declare module '*.svg' { + const value: string; + export default value; +} diff --git a/style/icons/jupyternaut-lite.svg b/style/icons/jupyternaut-lite.svg new file mode 100644 index 0000000..0da0575 --- /dev/null +++ b/style/icons/jupyternaut-lite.svg @@ -0,0 +1,50 @@ + + + + + + + + + From c03a8d00db87c46d31ddf352c9843d7c3c899724 Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Thu, 13 Feb 2025 09:30:08 +0100 Subject: [PATCH 2/7] optimize icon --- style/icons/jupyternaut-lite.svg | 55 ++++---------------------------- 1 file changed, 6 insertions(+), 49 deletions(-) diff --git a/style/icons/jupyternaut-lite.svg b/style/icons/jupyternaut-lite.svg index 0da0575..02f9d13 100644 --- a/style/icons/jupyternaut-lite.svg +++ b/style/icons/jupyternaut-lite.svg @@ -1,50 +1,7 @@ - - - - - - - - + + + + + + From b78c2373a6436295a7ebd8e536cceaed253d36de Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Thu, 13 Feb 2025 09:32:19 +0100 Subject: [PATCH 3/7] icon in the settings --- schema/ai-provider.json | 2 ++ src/icons.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/schema/ai-provider.json b/schema/ai-provider.json index e393663..02b0635 100644 --- a/schema/ai-provider.json +++ b/schema/ai-provider.json @@ -1,6 +1,8 @@ { "title": "AI provider", "description": "Provider settings", + "jupyter.lab.setting-icon": "@jupyterlite/ai:jupyternaut-lite", + "jupyter.lab.setting-icon-label": "JupyterLite AI Chat", "type": "object", "properties": { "provider": { diff --git a/src/icons.ts b/src/icons.ts index bca46a2..53995c0 100644 --- a/src/icons.ts +++ b/src/icons.ts @@ -13,6 +13,6 @@ import { LabIcon } from '@jupyterlab/ui-components'; import jupyternautLiteSvg from '../style/icons/jupyternaut-lite.svg'; export const jupyternautLiteIcon = new LabIcon({ - name: 'jupyter-ai::jupyternaut-lite', + name: '@jupyterlite/ai:jupyternaut-lite', svgstr: jupyternautLiteSvg }); From 7f29f8c6b31c0c2d468f819d7c176c2865d93000 Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Thu, 13 Feb 2025 09:56:45 +0100 Subject: [PATCH 4/7] Add persona name --- schema/chat.json | 6 ++++++ src/chat-handler.ts | 13 ++++++++++++- src/index.ts | 5 +++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/schema/chat.json b/schema/chat.json index 0a93a89..ad2ddd0 100644 --- a/schema/chat.json +++ b/schema/chat.json @@ -14,6 +14,12 @@ "type": "boolean", "default": true, "readOnly": false + }, + "personaName": { + "type": "string", + "title": "AI persona name", + "description": "The name of the AI persona", + "default": "Jupyternaut" } }, "additionalProperties": false diff --git a/src/chat-handler.ts b/src/chat-handler.ts index 3ef1da3..390a2bf 100644 --- a/src/chat-handler.ts +++ b/src/chat-handler.ts @@ -50,6 +50,16 @@ export class ChatHandler extends ChatModel { return this._aiProvider.chatModel; } + /** + * Getter and setter for the persona name. + */ + get personaName(): string { + return this._personaName; + } + set personaName(value: string) { + this._personaName = value; + } + /** * Getter and setter for the initial prompt. */ @@ -95,7 +105,7 @@ export class ChatHandler extends ChatModel { }) ); - const sender = { username: 'AI', avatar_url: AI_AVATAR }; + const sender = { username: this._personaName, avatar_url: AI_AVATAR }; this.updateWriters([sender]); // create an empty message to be filled by the AI provider @@ -148,6 +158,7 @@ export class ChatHandler extends ChatModel { } private _aiProvider: IAIProvider; + private _personaName = 'AI'; private _prompt: string; private _errorMessage: string = ''; private _history: IChatHistory = { messages: [] }; diff --git a/src/index.ts b/src/index.ts index 51ea5e3..36b6176 100644 --- a/src/index.ts +++ b/src/index.ts @@ -48,12 +48,17 @@ const chatPlugin: JupyterFrontEndPlugin = { let sendWithShiftEnter = false; let enableCodeToolbar = true; + let personaName = 'AI'; function loadSetting(setting: ISettingRegistry.ISettings): void { sendWithShiftEnter = setting.get('sendWithShiftEnter') .composite as boolean; enableCodeToolbar = setting.get('enableCodeToolbar').composite as boolean; + personaName = setting.get('personaName').composite as string; + + // set the properties chatHandler.config = { sendWithShiftEnter, enableCodeToolbar }; + chatHandler.personaName = personaName; } Promise.all([app.restored, settingsRegistry?.load(chatPlugin.id)]) From 2d0c5d4daf72b13045e91e355c26b589b38e3fcd Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Thu, 13 Feb 2025 19:04:10 +0100 Subject: [PATCH 5/7] Update src/chat-handler.ts Co-authored-by: Nicolas Brichet <32258950+brichet@users.noreply.github.com> --- src/chat-handler.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/chat-handler.ts b/src/chat-handler.ts index 390a2bf..63c17b8 100644 --- a/src/chat-handler.ts +++ b/src/chat-handler.ts @@ -57,6 +57,13 @@ export class ChatHandler extends ChatModel { return this._personaName; } set personaName(value: string) { + this.messages.forEach(message => { + if (message.sender.username === this._personaName) { + const updated: IChatMessage = { ...message }; + updated.sender.username = value; + this.messageAdded(updated); + } + }); this._personaName = value; } From e49f6229aef8fde88ecfd3ffde8c465dc86f565a Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Thu, 13 Feb 2025 20:33:15 +0100 Subject: [PATCH 6/7] lint --- src/chat-handler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chat-handler.ts b/src/chat-handler.ts index 63c17b8..a1570f0 100644 --- a/src/chat-handler.ts +++ b/src/chat-handler.ts @@ -57,7 +57,7 @@ export class ChatHandler extends ChatModel { return this._personaName; } set personaName(value: string) { - this.messages.forEach(message => { + this.messages.forEach(message => { if (message.sender.username === this._personaName) { const updated: IChatMessage = { ...message }; updated.sender.username = value; From 3b4813209b2e38024689b74ff0593854f7e4138a Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Thu, 13 Feb 2025 21:02:36 +0100 Subject: [PATCH 7/7] Reuse jupyter chat icon in the settings --- schema/chat.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/schema/chat.json b/schema/chat.json index ad2ddd0..536586f 100644 --- a/schema/chat.json +++ b/schema/chat.json @@ -1,6 +1,8 @@ { "title": "Chat configuration", "description": "Configuration for the chat panel", + "jupyter.lab.setting-icon": "jupyter-chat::chat", + "jupyter.lab.setting-icon-label": "Jupyter Chat", "type": "object", "properties": { "sendWithShiftEnter": {