Skip to content

Commit

Permalink
#26912 include in 23.10.24
Browse files Browse the repository at this point in the history
  • Loading branch information
erickgonzalez committed Feb 26, 2024
1 parent de3b11e commit a25a92e
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,17 @@
}
}

.ai-loading {
display: flex;
justify-content: center;
align-items: center;
min-width: 100%;
padding: $spacing-1;
border-radius: $spacing-1;
border: 1px solid $color-palette-gray-400;
color: $color-palette-primary;
}

.video-container {
margin-bottom: $spacing-3;
aspect-ratio: 16/9;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,13 @@
padding: $spacing-1;
margin-left: $spacing-8;

li {
.p-listbox-list .p-listbox-item {
padding: $spacing-2 $spacing-3;
border-bottom: 1px solid $color-palette-gray-300;
background-color: $white;

// accept option
&:first-child {
background-color: $color-palette-primary-200;
}

// regenerate option
&:nth-child(2) {
padding: $spacing-3;
background-color: $white;
}

// delete option
&:last-child {
border-top: 1px solid $color-palette-gray-300;
background-color: $white;
border-bottom: none;
}

&:hover {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@
<span class="p-input-icon-right">
<input
#input
[attr.disabled]="vm.status === 'loading' || null"
[attr.disabled]="vm.status === ComponentStatus.LOADING || null"
(keyup.escape)="handleScape($event)"
(keydown.escape)="$event.stopPropagation()"
autofocus
formControlName="textPrompt"
pInputText
placeholder="{{
'block-editor.extension.ai-content.ask-ai-to-write-something' | dm
}}"
type="text" />

<ng-container *ngIf="vm.status === 'loading'; else submitButton">
<ng-container *ngIf="vm.status === ComponentStatus.LOADING; else submitButton">
<span class="pi pi-spin pi-spinner"></span>
</ng-container>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import {
OnInit,
ViewChild
} from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { FormControl, FormGroup, Validators } from '@angular/forms';

import { filter, takeUntil } from 'rxjs/operators';

import { ComponentStatus } from '@dotcms/dotcms-models';

import { AiContentPromptState, AiContentPromptStore } from './store/ai-content-prompt.store';

interface AIContentForm {
Expand All @@ -26,6 +28,7 @@ interface AIContentForm {
})
export class AIContentPromptComponent implements OnInit, OnDestroy {
vm$: Observable<AiContentPromptState> = this.aiContentPromptStore.vm$;
readonly ComponentStatus = ComponentStatus;
private destroy$: Subject<boolean> = new Subject<boolean>();

@ViewChild('input') private input: ElementRef;
Expand All @@ -40,7 +43,7 @@ export class AIContentPromptComponent implements OnInit, OnDestroy {
this.aiContentPromptStore.status$
.pipe(
takeUntil(this.destroy$),
filter((status) => status === 'open')
filter((status) => status === ComponentStatus.IDLE)
)
.subscribe(() => {
this.form.reset();
Expand Down Expand Up @@ -73,7 +76,7 @@ export class AIContentPromptComponent implements OnInit, OnDestroy {
* @memberof AIContentPromptComponent
*/
handleScape(event: KeyboardEvent): void {
this.aiContentPromptStore.setStatus('exit');
this.aiContentPromptStore.setStatus(ComponentStatus.INIT);
event.stopPropagation();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ export const AIContentPromptExtension = (viewContainerRef: ViewContainerRef) =>
({ chain }) => {
return chain()
.command(({ tr }) => {
tr.setMeta(AI_CONTENT_PROMPT_PLUGIN_KEY, { open: true });
tr.setMeta(AI_CONTENT_PROMPT_PLUGIN_KEY, {
aIContentPromptOpen: true
});

return true;
})
Expand All @@ -58,7 +60,9 @@ export const AIContentPromptExtension = (viewContainerRef: ViewContainerRef) =>
({ chain }) => {
return chain()
.command(({ tr }) => {
tr.setMeta(AI_CONTENT_PROMPT_PLUGIN_KEY, { open: false });
tr.setMeta(AI_CONTENT_PROMPT_PLUGIN_KEY, {
aIContentPromptOpen: false
});

return true;
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { filter, skip, takeUntil, tap } from 'rxjs/operators';

import { Editor } from '@tiptap/core';

import { ComponentStatus } from '@dotcms/dotcms-models';

import { DotTiptapNodeInformation, findNodeByType, replaceNodeWithContent } from '../../../shared';
import { NodeTypes } from '../../bubble-menu/models';
import { AIContentPromptComponent } from '../ai-content-prompt.component';
Expand All @@ -29,7 +31,7 @@ interface AIContentPromptProps {
}

interface PluginState {
open: boolean;
aIContentPromptOpen: boolean;
}

export type AIContentPromptViewProps = AIContentPromptProps & {
Expand Down Expand Up @@ -126,15 +128,13 @@ export class AIContentPromptView {
* Subscription to exit the tippy since that can happen on escape listener that is in the html
* template in ai-content-prompt.component.html
*/
this.componentStore.status$
.pipe(
skip(1),
takeUntil(this.destroy$),
filter((status) => status === 'exit')
)
.subscribe(() => {
this.componentStore.status$.pipe(skip(1), takeUntil(this.destroy$)).subscribe((status) => {
if (status === ComponentStatus.INIT) {
this.tippy?.hide();
});
} else if (status === ComponentStatus.LOADING) {
this.editor.commands.setLoadingAIContentNode(true);
}
});

/**
* Subscription to delete AI_CONTENT node.
Expand Down Expand Up @@ -165,17 +165,20 @@ export class AIContentPromptView {

update(view: EditorView, prevState?: EditorState) {
const next = this.pluginKey?.getState(view.state);
const prev = prevState ? this.pluginKey?.getState(prevState) : { open: false };

if (next?.open === prev?.open) {
this.tippy?.popperInstance?.forceUpdate();
const prev = prevState
? this.pluginKey?.getState(prevState)
: { aIContentPromptOpen: false };

if (next?.aIContentPromptOpen === prev?.aIContentPromptOpen) {
return;
}

next.open
next.aIContentPromptOpen
? this.show()
: this.hide(this.storeSate.status === 'open' || this.storeSate.status === 'loaded');
: this.hide(
this.storeSate.status === ComponentStatus.IDLE ||
this.storeSate.status === ComponentStatus.LOADED
);
}

createTooltip() {
Expand Down Expand Up @@ -220,7 +223,7 @@ export class AIContentPromptView {
this.manageClickListener(true);
this.editor.setEditable(false);
this.tippy?.show();
this.componentStore.setStatus('open');
this.componentStore.setStatus(ComponentStatus.IDLE);
}

/**
Expand All @@ -229,11 +232,12 @@ export class AIContentPromptView {
* @param notifyStore
*/
hide(notifyStore = true) {
this.tippy?.hide();
this.editor.setEditable(true);

this.editor.view.focus();
if (notifyStore) {
this.componentStore.setStatus('close');
this.componentStore.setStatus(ComponentStatus.INIT);
}

this.manageClickListener(false);
Expand All @@ -251,7 +255,10 @@ export class AIContentPromptView {
* and not in a loading state, this function hides the associated Tippy tooltip.
*/
handleClick(): void {
if (this.storeSate.status === 'open' || this.storeSate.status === 'loaded') {
if (
this.storeSate.status === ComponentStatus.IDLE ||
this.storeSate.status === ComponentStatus.LOADED
) {
this.tippy.hide();
}
}
Expand All @@ -276,7 +283,7 @@ export const aiContentPromptPlugin = (options: AIContentPromptProps) => {
state: {
init(): PluginState {
return {
open: false
aIContentPromptOpen: false
};
},

Expand All @@ -285,11 +292,12 @@ export const aiContentPromptPlugin = (options: AIContentPromptProps) => {
value: PluginState,
oldState: EditorState
): PluginState {
const { open } = transaction.getMeta(AI_CONTENT_PROMPT_PLUGIN_KEY) || {};
const { aIContentPromptOpen } =
transaction.getMeta(AI_CONTENT_PROMPT_PLUGIN_KEY) || {};
const state = AI_CONTENT_PROMPT_PLUGIN_KEY.getState(oldState);

if (typeof open === 'boolean') {
return { open };
if (typeof aIContentPromptOpen === 'boolean') {
return { aIContentPromptOpen };
}

// keep the old state in case we do not receive a new one.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import { Injectable } from '@angular/core';

import { catchError, switchMap, tap, withLatestFrom } from 'rxjs/operators';

import { ComponentStatus } from '@dotcms/dotcms-models';

import { DotAiService } from '../../../shared';

export interface AiContentPromptState {
prompt: string;
content: string;
acceptContent: boolean;
deleteContent: boolean;
status: 'loading' | 'loaded' | 'open' | 'exit' | 'close';
status: ComponentStatus;
}

@Injectable({
Expand All @@ -25,7 +27,7 @@ export class AiContentPromptStore extends ComponentStore<AiContentPromptState> {
content: '',
acceptContent: false,
deleteContent: false,
status: 'close'
status: ComponentStatus.INIT
});
}

Expand All @@ -37,7 +39,7 @@ export class AiContentPromptStore extends ComponentStore<AiContentPromptState> {
readonly vm$ = this.select((state) => state);

//Updaters
readonly setStatus = this.updater((state, status: AiContentPromptState['status']) => ({
readonly setStatus = this.updater((state, status: ComponentStatus) => ({
...state,
status
}));
Expand All @@ -55,13 +57,13 @@ export class AiContentPromptStore extends ComponentStore<AiContentPromptState> {
readonly generateContent = this.effect((prompt$: Observable<string>) => {
return prompt$.pipe(
switchMap((prompt) => {
this.patchState({ status: 'loading', prompt });
this.patchState({ status: ComponentStatus.LOADING, prompt });

return this.dotAiService.generateContent(prompt).pipe(
tap((content) => this.patchState({ status: 'loaded', content })),
tap((content) => this.patchState({ status: ComponentStatus.LOADED, content })),
catchError(() => {
//TODO: Notify to handle error in the UI.
this.patchState({ status: 'loaded', content: '' });
this.patchState({ status: ComponentStatus.LOADED, content: '' });

return of(null);
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,22 @@ declare module '@tiptap/core' {
interface Commands<ReturnType> {
AIContentNode: {
insertAINode: (content?: string) => ReturnType;
setLoadingAIContentNode: (loading: boolean) => ReturnType;
};
}
}

const AI_LOADING_CLASS = 'ai-loading';
export const AIContentNode = Node.create({
name: 'aiContent',

addAttributes() {
return {
content: {
default: ''
},
loading: {
default: false
}
};
},
Expand Down Expand Up @@ -70,7 +75,8 @@ export const AIContentNode = Node.create({
// If an AI_CONTENT node is found, replace its content.
if (nodeInformation) {
tr.setNodeMarkup(nodeInformation.from, undefined, {
content: content
content: content,
loading: false
});
// Set the node selection to the beginning of the replaced content.
commands.setNodeSelection(nodeInformation.from);
Expand All @@ -83,6 +89,20 @@ export const AIContentNode = Node.create({
type: this.name,
attrs: { content: content }
});
},
setLoadingAIContentNode:
(loading: boolean) =>
({ tr, editor }) => {
const nodeInformation = findNodeByType(editor, NodeTypes.AI_CONTENT);
// Set the loading attribute to the specified value.
if (nodeInformation) {
tr.setNodeMarkup(nodeInformation.from, undefined, {
...nodeInformation.node.attrs,
loading: loading
});
}

return true;
}
};
},
Expand All @@ -96,13 +116,17 @@ export const AIContentNode = Node.create({
const dom = document.createElement('div');
const div = document.createElement('div');

div.innerHTML = node.attrs.content || '';
div.innerHTML = node.attrs.loading
? `<span class="pi pi-spin pi-spinner"></span>`
: node.attrs.content;

dom.contentEditable = 'true';
dom.classList.add('ai-content-container');
dom.className = `ai-content-container ${node.attrs.loading ? AI_LOADING_CLASS : ''}`;
dom.append(div);

return { dom };
return {
dom
};
};
}
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ResolvedPos } from 'prosemirror-model';
import { ResolvedPos, Node } from 'prosemirror-model';
import { SelectionRange, TextSelection } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';

Expand Down
3 changes: 2 additions & 1 deletion dotCMS/hotfix_tracking.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ This maintenance release includes the following code fixes:
49. https://github.com/dotCMS/core/issues/26520 : [UI] Add DotMessageService to Block Editor to able translate the labels/messages #26520
50. https://github.com/dotCMS/core/issues/26665 : [UI] - Disable the input while is pending/loading #26665
51. https://github.com/dotCMS/core/issues/26666 : [UI] When the BlockEditor lose the focus, and get it again the generated content is duplicated #26666
52. https://github.com/dotCMS/core/issues/26895 : [UI] Update AIService of Block Editor with new endpoint shape #26895
52. https://github.com/dotCMS/core/issues/26895 : [UI] Update AIService of Block Editor with new endpoint shape #26895
53. https://github.com/dotCMS/core/issues/26912 : [UI] AI actions menu selection is not working as should. #26912
Loading

0 comments on commit a25a92e

Please sign in to comment.