diff --git a/src/__tests__/chat-settings.spec.ts b/src/__tests__/chat-settings.spec.ts deleted file mode 100644 index d265c77..0000000 --- a/src/__tests__/chat-settings.spec.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { minifyPatchObject } from '../components/settings/minify'; - -const COMPLEX_OBJECT = { - primitive: 0, - array: ['a'], - object: { nested: { field: 0 } } -}; - -describe('minifyPatchObject', () => { - test('returns empty object if patch is identical', () => { - const obj = COMPLEX_OBJECT; - const patch = JSON.parse(JSON.stringify(obj)); - - expect(minifyPatchObject(obj, patch)).toEqual({}); - }); - - test('returns empty object if patch is empty', () => { - expect(minifyPatchObject(COMPLEX_OBJECT, {})).toEqual({}); - }); - - test('returns patch if object is empty', () => { - expect(minifyPatchObject({}, COMPLEX_OBJECT)).toEqual(COMPLEX_OBJECT); - }); - - test('should remove unchanged props from patch', () => { - const obj = { - unchanged: 'foo', - changed: 'bar', - nested: { - unchanged: 'foo', - changed: 'bar' - } - }; - const patch = { - unchanged: 'foo', - changed: 'baz', - nested: { - unchanged: 'foo', - changed: 'baz' - } - }; - - expect(minifyPatchObject(obj, patch)).toEqual({ - changed: 'baz', - nested: { - changed: 'baz' - } - }); - }); - - test('defers to patch object when property types mismatch', () => { - const obj = { - api_keys: ['ANTHROPIC_API_KEY'] - }; - const patch = { - api_keys: { - OPENAI_API_KEY: 'foobar' - } - }; - - expect(minifyPatchObject(obj, patch)).toEqual(patch); - }); -}); diff --git a/src/components/chat-input.tsx b/src/components/chat-input.tsx index 22fe899..cd66ce6 100644 --- a/src/components/chat-input.tsx +++ b/src/components/chat-input.tsx @@ -10,15 +10,7 @@ import { } from '@mui/material'; import SendIcon from '@mui/icons-material/Send'; -type ChatInputProps = { - value: string; - onChange: (newValue: string) => unknown; - onSend: () => unknown; - sendWithShiftEnter: boolean; - sx?: SxProps; -}; - -export function ChatInput(props: ChatInputProps): JSX.Element { +export function ChatInput(props: ChatInput.IProps): JSX.Element { function handleKeyDown(event: React.KeyboardEvent) { if ( event.key === 'Enter' && @@ -77,3 +69,19 @@ export function ChatInput(props: ChatInputProps): JSX.Element { ); } + +/** + * The chat input namespace. + */ +export namespace ChatInput { + /** + * The properties of the react element. + */ + export interface IProps { + value: string; + onChange: (newValue: string) => unknown; + onSend: () => unknown; + sendWithShiftEnter: boolean; + sx?: SxProps; + } +} diff --git a/src/components/chat-messages.tsx b/src/components/chat-messages.tsx index 02953dc..880388f 100644 --- a/src/components/chat-messages.tsx +++ b/src/components/chat-messages.tsx @@ -4,14 +4,14 @@ import type { SxProps, Theme } from '@mui/material'; import React, { useState, useEffect } from 'react'; import { RendermimeMarkdown } from './rendermime-markdown'; -import { ChatService } from '../services'; +import { IChatMessage, IUser } from '../types'; type ChatMessagesProps = { rmRegistry: IRenderMimeRegistry; - messages: ChatService.IChatMessage[]; + messages: IChatMessage[]; }; -export type ChatMessageHeaderProps = ChatService.IUser & { +export type ChatMessageHeaderProps = IUser & { timestamp: string; sx?: SxProps; }; diff --git a/src/components/chat-settings.tsx b/src/components/chat-settings.tsx deleted file mode 100644 index 0949882..0000000 --- a/src/components/chat-settings.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import { Box } from '@mui/system'; -import { - Alert, - Button, - FormControl, - FormControlLabel, - FormLabel, - Radio, - RadioGroup, - CircularProgress -} from '@mui/material'; -import React, { useEffect, useState } from 'react'; - -import { useStackingAlert } from './mui-extras/stacking-alert'; -import { ServerInfoState, useServerInfo } from './settings/use-server-info'; -import { minifyUpdate } from './settings/minify'; -import { ChatService } from '../services'; - -/** - * Component that returns the settings view in the chat panel. - */ -export function ChatSettings(): JSX.Element { - // state fetched on initial render - const server = useServerInfo(); - - // initialize alert helper - const alert = useStackingAlert(); - - const [sendWse, setSendWse] = useState(false); - - // whether the form is currently saving - const [saving, setSaving] = useState(false); - - /** - * Effect: initialize inputs after fetching server info. - */ - useEffect(() => { - if (server.state !== ServerInfoState.Ready) { - return; - } - setSendWse(server.config.send_with_shift_enter); - }, [server]); - - const handleSave = async () => { - // compress fields with JSON values - if (server.state !== ServerInfoState.Ready) { - return; - } - - let updateRequest: ChatService.UpdateConfigRequest = { - send_with_shift_enter: sendWse - }; - updateRequest = minifyUpdate(server.config, updateRequest); - updateRequest.last_read = server.config.last_read; - - setSaving(true); - try { - await ChatService.updateConfig(updateRequest); - } catch (e) { - console.error(e); - const msg = - e instanceof Error || typeof e === 'string' - ? e.toString() - : 'An unknown error occurred. Check the console for more details.'; - alert.show('error', msg); - return; - } finally { - setSaving(false); - } - await server.refetchAll(); - alert.show('success', 'Settings saved successfully.'); - }; - - if (server.state === ServerInfoState.Loading) { - return ( - - - - ); - } - - if (server.state === ServerInfoState.Error) { - return ( - - - {server.error || - 'An unknown error occurred. Check the console for more details.'} - - - ); - } - - return ( - - {/* Input */} -

Input

- - - When writing a message, press Enter to: - - { - setSendWse(e.target.value === 'newline'); - }} - > - } - label="Send the message" - /> - } - label={ - <> - Start a new line (use Shift+Enter to send) - - } - /> - - - - - - {alert.jsx} -
- ); -} diff --git a/src/components/chat.tsx b/src/components/chat.tsx index e593f42..42a7ff6 100644 --- a/src/components/chat.tsx +++ b/src/components/chat.tsx @@ -1,4 +1,3 @@ -import type { IThemeManager } from '@jupyterlab/apputils'; import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import SettingsIcon from '@mui/icons-material/Settings'; @@ -9,10 +8,10 @@ import React, { useState, useEffect } from 'react'; import { JlThemeProvider } from './jl-theme-provider'; import { ChatMessages } from './chat-messages'; import { ChatInput } from './chat-input'; -import { ChatSettings } from './chat-settings'; import { ScrollContainer } from './scroll-container'; import { IChatModel } from '../model'; -import { ChatService } from '../services'; +import { IChatMessage, IMessage } from '../types'; +import { IThemeManager } from '@jupyterlab/apputils'; type ChatBodyProps = { chatModel: IChatModel; @@ -23,22 +22,13 @@ function ChatBody({ chatModel, rmRegistry: renderMimeRegistry }: ChatBodyProps): JSX.Element { - const [messages, setMessages] = useState([]); + const [messages, setMessages] = useState([]); const [input, setInput] = useState(''); - const [sendWithShiftEnter, setSendWithShiftEnter] = useState(true); /** * Effect: fetch history and config on initial render */ useEffect(() => { - async function getConfig() { - ChatService.getConfig() - .then(config => - setSendWithShiftEnter(config.send_with_shift_enter ?? false) - ) - .catch(e => console.error(e)); - } - async function fetchHistory() { if (!chatModel.getHistory) { return; @@ -49,7 +39,6 @@ function ChatBody({ .catch(e => console.error(e)); } - getConfig(); fetchHistory(); }, [chatModel]); @@ -57,14 +46,13 @@ function ChatBody({ * Effect: listen to chat messages */ useEffect(() => { - function handleChatEvents(_: IChatModel, message: ChatService.IMessage) { - if (message.type === 'connection') { - return; - } else if (message.type === 'clear') { + function handleChatEvents(_: IChatModel, message: IMessage) { + if (message.type === 'clear') { setMessages([]); return; + } else if (message.type === 'msg') { + setMessages(messageGroups => [...messageGroups, message]); } - setMessages(messageGroups => [...messageGroups, message]); } chatModel.incomingMessage.connect(handleChatEvents); @@ -98,26 +86,16 @@ function ChatBody({ paddingBottom: 0, borderTop: '1px solid var(--jp-border-color1)' }} - sendWithShiftEnter={sendWithShiftEnter} + sendWithShiftEnter={chatModel.config.sendWithShiftEnter ?? false} /> ); } -export type ChatProps = { - chatModel: IChatModel; - themeManager: IThemeManager | null; - rmRegistry: IRenderMimeRegistry; - chatView?: ChatView; -}; - -enum ChatView { - Chat, - Settings -} - -export function Chat(props: ChatProps): JSX.Element { - const [view, setView] = useState(props.chatView || ChatView.Chat); +export function Chat(props: Chat.IOptions): JSX.Element { + const [view, setView] = useState( + props.chatView || Chat.ChatView.Chat + ); console.log('Instantiate a chat'); return ( @@ -135,15 +113,15 @@ export function Chat(props: ChatProps): JSX.Element { > {/* top bar */} - {view !== ChatView.Chat ? ( - setView(ChatView.Chat)}> + {view !== Chat.ChatView.Chat ? ( + setView(Chat.ChatView.Chat)}> ) : ( )} - {view === ChatView.Chat ? ( - setView(ChatView.Settings)}> + {view === Chat.ChatView.Chat && props.settingsPanel ? ( + setView(Chat.ChatView.Settings)}> ) : ( @@ -151,11 +129,53 @@ export function Chat(props: ChatProps): JSX.Element { )} {/* body */} - {view === ChatView.Chat && ( + {view === Chat.ChatView.Chat && ( )} - {view === ChatView.Settings && } + {view === Chat.ChatView.Settings && props.settingsPanel && ( + + )} ); } + +/** + * The chat UI namespace + */ +export namespace Chat { + /** + * The options to build the Chat UI. + */ + export interface IOptions { + /** + * The chat model. + */ + chatModel: IChatModel; + /** + * The theme manager. + */ + themeManager: IThemeManager | null; + /** + * The rendermime registry. + */ + rmRegistry: IRenderMimeRegistry; + /** + * The view to render. + */ + chatView?: ChatView; + /** + * A settings panel that can be used for dedicated settings (e.g. jupyter-ai) + */ + settingsPanel?: () => JSX.Element; + } + + /** + * The view to render. + * The settings view is available only if the settings panel is provided in options. + */ + export enum ChatView { + Chat, + Settings + } +} diff --git a/src/components/settings/minify.ts b/src/components/settings/minify.ts deleted file mode 100644 index daff8f1..0000000 --- a/src/components/settings/minify.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { ChatService } from '../../services'; - -/** - * Function that minimizes the `UpdateConfigRequest` object prior to submission. - * Removes properties with values identical to those specified in the server - * configuration. - */ -export function minifyUpdate( - config: ChatService.DescribeConfigResponse, - update: ChatService.UpdateConfigRequest -): ChatService.UpdateConfigRequest { - return minifyPatchObject(config, update) as ChatService.UpdateConfigRequest; -} - -/** - * Function that removes all properties from `patch` that have identical values - * to `obj` recursively. - */ -export function minifyPatchObject( - obj: Record, - patch: Record -): Record { - const diffObj: Record = {}; - for (const key in patch) { - if (!(key in obj) || typeof obj[key] !== typeof patch[key]) { - // if key is not present in oldObj, or if the value types do not match, - // use the value of `patch`. - diffObj[key] = patch[key]; - continue; - } - - const objVal = obj[key]; - const patchVal = patch[key]; - if (Array.isArray(objVal) && Array.isArray(patchVal)) { - // if objects are both arrays but are not equal, then use the value - const areNotEqual = - objVal.length !== patchVal.length || - !objVal.every((objVal_i, i) => objVal_i === patchVal[i]); - if (areNotEqual) { - diffObj[key] = patchVal; - } - } else if (typeof patchVal === 'object') { - // if the value is an object, run `diffObjects` recursively. - const childPatch = minifyPatchObject(objVal, patchVal); - const isNonEmpty = !!Object.keys(childPatch)?.length; - if (isNonEmpty) { - diffObj[key] = childPatch; - } - } else if (objVal !== patchVal) { - // otherwise, use the value of `patch` only if it differs. - diffObj[key] = patchVal; - } - } - - return diffObj; -} diff --git a/src/components/settings/use-server-info.ts b/src/components/settings/use-server-info.ts deleted file mode 100644 index 204dcb6..0000000 --- a/src/components/settings/use-server-info.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { useState, useEffect, useMemo, useCallback } from 'react'; -import { ChatService } from '../../services'; - -type ServerInfoProperties = { - config: ChatService.DescribeConfigResponse; -}; - -type ServerInfoMethods = { - refetchAll: () => Promise; -}; - -export enum ServerInfoState { - /** - * Server info is being fetched. - */ - Loading, - /** - * Unable to retrieve server info. - */ - Error, - /** - * Server info was loaded successfully. - */ - Ready -} - -type ServerInfoLoading = { state: ServerInfoState.Loading }; -type ServerInfoError = { - state: ServerInfoState.Error; - error: string; -}; -type ServerInfoReady = { state: ServerInfoState.Ready } & ServerInfoProperties & - ServerInfoMethods; - -type ServerInfo = ServerInfoLoading | ServerInfoError | ServerInfoReady; - -/** - * A hook that fetches the current configuration and provider lists from the - * server. Returns a `ServerInfo` object that includes methods. - */ -export function useServerInfo(): ServerInfo { - const [state, setState] = useState(ServerInfoState.Loading); - const [serverInfoProps, setServerInfoProps] = - useState(); - const [error, setError] = useState(''); - - const fetchServerInfo = useCallback(async () => { - try { - const config = await ChatService.getConfig(); - setServerInfoProps({ config }); - - setState(ServerInfoState.Ready); - } catch (e) { - console.error(e); - if (e instanceof Error) { - setError(e.toString()); - } else { - setError('An unknown error occurred.'); - } - setState(ServerInfoState.Error); - } - }, []); - - /** - * Effect: fetch server info on initial render - */ - useEffect(() => { - fetchServerInfo(); - }, []); - - return useMemo(() => { - if (state === ServerInfoState.Loading) { - return { state }; - } - - if (state === ServerInfoState.Error || !serverInfoProps) { - return { state: ServerInfoState.Error, error }; - } - - return { - state, - ...serverInfoProps, - refetchAll: fetchServerInfo - }; - }, [state, serverInfoProps, error]); -} diff --git a/src/components/settings/validator.ts b/src/components/settings/validator.ts deleted file mode 100644 index cea6ad8..0000000 --- a/src/components/settings/validator.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class SettingsValidator { - constructor() {} -} diff --git a/src/handlers/websocket-handler.ts b/src/handlers/websocket-handler.ts index aedba8c..1d0d272 100644 --- a/src/handlers/websocket-handler.ts +++ b/src/handlers/websocket-handler.ts @@ -4,10 +4,17 @@ import { UUID } from '@lumino/coreutils'; import { requestAPI } from './handler'; import { ChatModel, IChatModel } from '../model'; -import { ChatService } from '../services'; +import { IChatHistory, IMessage, INewMessage } from '../types'; const CHAT_SERVICE_URL = 'api/chat'; +export type ConnectionMessage = { + type: 'connection'; + client_id: string; +}; + +type GenericMessage = IMessage | ConnectionMessage; + /** * An implementation of the chat model based on websocket handler. */ @@ -39,7 +46,7 @@ export class WebSocketHandler extends ChatModel { * Sends a message across the WebSocket. Promise resolves to the message ID * when the server sends the same message back, acknowledging receipt. */ - addMessage(message: ChatService.ChatRequest): Promise { + addMessage(message: INewMessage): Promise { message.id = UUID.uuid4(); return new Promise(resolve => { this._socket?.send(JSON.stringify(message)); @@ -47,8 +54,8 @@ export class WebSocketHandler extends ChatModel { }); } - async getHistory(): Promise { - let data: ChatService.ChatHistory = { messages: [] }; + async getHistory(): Promise { + let data: IChatHistory = { messages: [] }; try { data = await requestAPI('history', { method: 'GET' @@ -77,7 +84,7 @@ export class WebSocketHandler extends ChatModel { } } - onMessage(message: ChatService.IMessage): void { + onMessage(message: IMessage): void { // resolve promise from `sendMessage()` if (message.type === 'msg' && message.sender.id === this.id) { this._sendResolverQueue.get(message.id)?.(true); @@ -115,10 +122,7 @@ export class WebSocketHandler extends ChatModel { socket.onmessage = msg => msg.data && this.onMessage(JSON.parse(msg.data)); - const listenForConnection = ( - _: IChatModel, - message: ChatService.IMessage - ) => { + const listenForConnection = (_: IChatModel, message: GenericMessage) => { if (message.type !== 'connection') { return; } diff --git a/src/index.ts b/src/index.ts index 27b40d8..84630cf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ export * from './handlers/websocket-handler'; export * from './model'; -export * from './services'; +export * from './types'; export * from './widgets/chat-error'; export * from './widgets/chat-sidebar'; export * from './widgets/chat-widget'; diff --git a/src/model.ts b/src/model.ts index 0a9abf7..482e8fb 100644 --- a/src/model.ts +++ b/src/model.ts @@ -1,28 +1,42 @@ import { IDisposable } from '@lumino/disposable'; import { ISignal, Signal } from '@lumino/signaling'; -import { ChatService } from './services'; +import { + IChatHistory, + INewMessage, + IChatMessage, + IConfig, + IMessage +} from './types'; +/** + * The chat model interface. + */ export interface IChatModel extends IDisposable { /** * The chat model ID. */ id: string; + /** + * The configuration for the chat panel. + */ + config: IConfig; + /** * The signal emitted when a new message is received. */ - get incomingMessage(): ISignal; + get incomingMessage(): ISignal; /** * The signal emitted when a message is updated. */ - get messageUpdated(): ISignal; + get messageUpdated(): ISignal; /** * The signal emitted when a message is updated. */ - get messageDeleted(): ISignal; + get messageDeleted(): ISignal; /** * Send a message, to be defined depending on the chosen technology. @@ -31,9 +45,7 @@ export interface IChatModel extends IDisposable { * @param message - the message to send. * @returns whether the message has been sent or not, or nothing if not needed. */ - addMessage( - message: ChatService.ChatRequest - ): Promise | boolean | void; + addMessage(message: INewMessage): Promise | boolean | void; /** * Optional, to update a message from the chat. @@ -43,13 +55,13 @@ export interface IChatModel extends IDisposable { */ updateMessage?( id: string, - message: ChatService.ChatRequest + message: INewMessage ): Promise | boolean | void; /** * Optional, to get messages history. */ - getHistory?(): Promise; + getHistory?(): Promise; /** * Dispose the chat model. @@ -66,14 +78,14 @@ export interface IChatModel extends IDisposable { * * @param message - the new message, containing user information and body. */ - onMessage(message: ChatService.IMessage): void; + onMessage(message: IMessage): void; /** * Function to call when a message is updated. * * @param message - the message updated, containing user information and body. */ - onMessageUpdated?(message: ChatService.IMessage): void; + onMessageUpdated?(message: IMessage): void; } /** @@ -85,7 +97,9 @@ export class ChatModel implements IChatModel { /** * Create a new chat model. */ - constructor(options: ChatModel.IOptions = {}) {} + constructor(options: ChatModel.IOptions = {}) { + this._config = options.config ?? {}; + } /** * The chat model ID. @@ -98,23 +112,34 @@ export class ChatModel implements IChatModel { } /** + * The chat settings. + */ + get config(): IConfig { + return this._config; + } + set config(value: Partial) { + this._config = { ...this._config, ...value }; + } + + /** + * * The signal emitted when a new message is received. */ - get incomingMessage(): ISignal { + get incomingMessage(): ISignal { return this._incomingMessage; } /** * The signal emitted when a message is updated. */ - get messageUpdated(): ISignal { + get messageUpdated(): ISignal { return this._messageUpdated; } /** * The signal emitted when a message is updated. */ - get messageDeleted(): ISignal { + get messageDeleted(): ISignal { return this._messageDeleted; } @@ -125,9 +150,7 @@ export class ChatModel implements IChatModel { * @param message - the message to send. * @returns whether the message has been sent or not. */ - addMessage( - message: ChatService.ChatRequest - ): Promise | boolean | void {} + addMessage(message: INewMessage): Promise | boolean | void {} /** * Dispose the chat model. @@ -150,9 +173,7 @@ export class ChatModel implements IChatModel { * A function called before transferring the message to the panel(s). * Can be useful if some actions are required on the message. */ - protected formatChatMessage( - message: ChatService.IChatMessage - ): ChatService.IChatMessage { + protected formatChatMessage(message: IChatMessage): IChatMessage { return message; } @@ -161,23 +182,20 @@ export class ChatModel implements IChatModel { * * @param message - the message with user information and body. */ - onMessage(message: ChatService.IMessage): void { + onMessage(message: IMessage): void { if (message.type === 'msg') { - message = this.formatChatMessage(message as ChatService.IChatMessage); + message = this.formatChatMessage(message as IChatMessage); } this._incomingMessage.emit(message); } private _id: string = ''; + private _config: IConfig; private _isDisposed = false; - private _incomingMessage = new Signal(this); - private _messageUpdated = new Signal( - this - ); - private _messageDeleted = new Signal( - this - ); + private _incomingMessage = new Signal(this); + private _messageUpdated = new Signal(this); + private _messageDeleted = new Signal(this); } /** @@ -187,5 +205,10 @@ export namespace ChatModel { /** * The instantiation options for a ChatModel. */ - export interface IOptions {} + export interface IOptions { + /** + * Initial config for the chat widget. + */ + config?: IConfig; + } } diff --git a/src/services.ts b/src/services.ts deleted file mode 100644 index d63381a..0000000 --- a/src/services.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { requestAPI } from './handlers/handler'; - -export namespace ChatService { - export interface IUser { - id: string; - username?: string; - name?: string; - display_name?: string; - initials?: string; - color?: string; - avatar_url?: string; - } - - export interface IChatMessage { - type: 'msg'; - body: string; - id: string; - time: number; - sender: IUser; - } - - export type ConnectionMessage = { - type: 'connection'; - client_id: string; - }; - - export type ClearMessage = { - type: 'clear'; - }; - - export type IMessage = IChatMessage | ConnectionMessage | ClearMessage; - - export type ChatHistory = { - messages: IChatMessage[]; - }; - - export type ChatRequest = { - body: string; - id?: string; - }; - - export type DescribeConfigResponse = { - send_with_shift_enter: boolean; - last_read: number; - }; - - export type UpdateConfigRequest = { - send_with_shift_enter?: boolean; - last_read?: number; - }; - - export async function getConfig(): Promise { - return requestAPI('config'); - } - - export async function updateConfig( - config: UpdateConfigRequest - ): Promise { - return requestAPI('config', { - method: 'POST', - body: JSON.stringify(config) - }); - } -} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..5a81a52 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,57 @@ +/** + * The user description. + */ +export interface IUser { + id: string; + username?: string; + name?: string; + display_name?: string; + initials?: string; + color?: string; + avatar_url?: string; +} + +/** + * The configuration interface. + */ +export interface IConfig { + sendWithShiftEnter?: boolean; + lastRead?: number; +} + +/** + * The chat message decription. + */ +export interface IChatMessage { + type: 'msg'; + body: string; + id: string; + time: number; + sender: IUser; +} + +export type IClearMessage = { + type: 'clear'; +}; + +export type IMessage = IChatMessage | IClearMessage; + +/** + * The chat history interface. + */ +export interface IChatHistory { + messages: IChatMessage[]; +} + +/** + * The content of a new message. + */ +export interface INewMessage { + body: string; + id?: string; +} + +/** + * An empty interface to describe optional settings taht could be fetched from server. + */ +export interface ISettings {} diff --git a/src/widgets/chat-widget.tsx b/src/widgets/chat-widget.tsx index 08d278e..859aee1 100644 --- a/src/widgets/chat-widget.tsx +++ b/src/widgets/chat-widget.tsx @@ -7,7 +7,7 @@ import { chatIcon } from '../icons'; import { IChatModel } from '../model'; export class ChatWidget extends ReactWidget { - constructor(options: ChatWidget.IOptions) { + constructor(options: Chat.IOptions) { super(); this.id = 'jupyter-chat::widget'; @@ -35,9 +35,5 @@ export class ChatWidget extends ReactWidget { } export namespace ChatWidget { - export interface IOptions { - chatModel: IChatModel; - themeManager: IThemeManager | null; - rmRegistry: IRenderMimeRegistry; - } + export interface IOptions extends Chat.IOptions {} }