Skip to content

Commit

Permalink
adding fade and scroll capabilities to focusMode
Browse files Browse the repository at this point in the history
  • Loading branch information
OvidijusParsiunas committed Jan 9, 2025
1 parent 0c9eb5e commit b64b33b
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 7 deletions.
3 changes: 2 additions & 1 deletion component/src/deepChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {ServiceIO} from './services/serviceIO';
import {Legacy} from './utils/legacy/legacy';
import {TextInput} from './types/textInput';
import {LoadHistory} from './types/history';
import {FocusMode} from './types/focusMode';
import {CustomStyle} from './types/styles';
import {Response} from './types/response';
import style from './deepChat.css?inline';
Expand Down Expand Up @@ -145,7 +146,7 @@ export class DeepChat extends InternalHTML {
remarkable?: RemarkableOptions;

@Property('boolean')
focusMode?: boolean;
focusMode?: FocusMode;

getMessages: () => MessageContent[] = () => [];

Expand Down
3 changes: 3 additions & 0 deletions component/src/types/focusMode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type FocusModeFade = boolean | number;

export type FocusMode = boolean | {isScroll?: boolean; fade?: FocusModeFade};
8 changes: 6 additions & 2 deletions component/src/utils/element/elementUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ export class ElementUtils {
return newElement;
}

public static scrollToBottom(element: HTMLElement) {
element.scrollTop = element.scrollHeight;
public static scrollToBottom(element: HTMLElement, isAnimation = false) {
if (isAnimation) {
element.scrollTo({left: 0, top: element.scrollHeight, behavior: 'smooth'});
} else {
element.scrollTop = element.scrollHeight;
}
}

public static scrollToTop(element: HTMLElement) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {FileAttachmentsType} from '../../fileAttachments/fileAttachmentTypes/fil
import {ValidationHandler} from '../../../../../types/validationHandler';
import {CustomButtonInnerElements} from '../customButtonInnerElements';
import {FileAttachments} from '../../fileAttachments/fileAttachments';
import {FocusModeUtils} from '../../../messages/utils/focusModeUtils';
import {SubmitButtonStyles} from '../../../../../types/submitButton';
import {SpeechToText} from '../microphone/speechToText/speechToText';
import {SUBMIT_ICON_STRING} from '../../../../../icons/submitIcon';
Expand Down Expand Up @@ -195,9 +196,12 @@ export class SubmitButton extends InputButton<Styles> {
public async attemptSubmit(content: UserContentI, isProgrammatic = false) {
if ((await this._validationHandler?.(isProgrammatic ? content : undefined)) === false) return;
this.changeToLoadingIcon();
this._textInput.clear();
if (typeof this._messages.focusMode !== 'boolean' && this._messages.focusMode?.fade) {
await FocusModeUtils.fadeAnimation(this._messages.elementRef, this._messages.focusMode.fade);
}
await this.addNewMessage(content);
if (!this._serviceIO.isWebModel()) this._messages.addLoadingMessage();
this._textInput.clear();
const filesData = content.files?.map((fileData) => fileData.file);
const requestContents = {text: content.text === '' ? undefined : content.text, files: filesData};
await this._serviceIO.callAPI(requestContents, this._messages);
Expand Down
13 changes: 10 additions & 3 deletions component/src/views/chat/messages/messagesBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import {FireEvents} from '../../../utils/events/fireEvents';
import {RemarkableOptions} from '../../../types/remarkable';
import {LoadingHistory} from './history/loadingHistory';
import {HTMLClassUtilities} from '../../../types/html';
import {FocusModeUtils} from './utils/focusModeUtils';
import {IntroPanel} from '../introPanel/introPanel';
import {Legacy} from '../../../utils/legacy/legacy';
import {FocusMode} from '../../../types/focusMode';
import {MessageUtils} from './utils/messageUtils';
import {Response} from '../../../types/response';
import {Avatars} from '../../../types/avatars';
Expand All @@ -25,7 +27,7 @@ export class MessagesBase {
textToSpeech?: ProcessedTextToSpeechConfig;
submitUserMessage?: (content: UserContent) => void;
readonly elementRef: HTMLElement;
readonly focusMode: boolean;
readonly focusMode?: FocusMode;
readonly messageStyles?: MessageStyles;
readonly htmlClassUtilities: HTMLClassUtilities = {};
readonly messageToElements: MessageToElements = [];
Expand All @@ -48,7 +50,10 @@ export class MessagesBase {
this._names = deepChat.names;
this._onMessage = FireEvents.onMessage.bind(this, deepChat);
if (deepChat.htmlClassUtilities) this.htmlClassUtilities = deepChat.htmlClassUtilities;
this.focusMode = !!deepChat.focusMode;
this.focusMode = deepChat.focusMode;
if (typeof this.focusMode !== 'boolean' && this.focusMode?.fade) {
FocusModeUtils.setFade(this.elementRef, this.focusMode.fade);
}
setTimeout(() => {
this.submitUserMessage = deepChat.submitUserMessage; // wait for it to be available in input.ts
});
Expand Down Expand Up @@ -99,7 +104,9 @@ export class MessagesBase {
const messageElements = this.createNewMessageElement(text, role);
this.appendOuterContainerElemet(messageElements.outerContainer);
if (role === 'user') {
setTimeout(() => ElementUtils.scrollToBottom(this.elementRef)); // timeout neeed when bubble font is large
const isAnimation = typeof this.focusMode !== 'boolean' && this.focusMode?.isScroll;
// timeout neeed when bubble font is large
setTimeout(() => ElementUtils.scrollToBottom(this.elementRef, isAnimation));
} else {
// prevents a browser bug where a long response from AI would sometimes scroll down
this.messageElementRefs[this.messageElementRefs.length - 2]?.outerContainer.scrollIntoView();
Expand Down
18 changes: 18 additions & 0 deletions component/src/views/chat/messages/utils/focusModeUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {FocusModeFade} from '../../../../types/focusMode';

export class FocusModeUtils {
private static readonly DEFAULT_FADE_MS = 500;

public static setFade(elementRef: HTMLElement, fade: FocusModeFade) {
elementRef.style.transitionDuration = typeof fade === 'number' ? `${fade}ms` : `${FocusModeUtils.DEFAULT_FADE_MS}ms`;
}

public static async fadeAnimation(elementRef: HTMLElement, fade: FocusModeFade) {
elementRef.style.opacity = '0';
const timeoutMS = typeof fade === 'number' ? fade : FocusModeUtils.DEFAULT_FADE_MS;
await new Promise<void>((resolve) => {
setTimeout(() => resolve(), timeoutMS);
});
elementRef.style.opacity = '1';
}
}

0 comments on commit b64b33b

Please sign in to comment.