Skip to content

Commit

Permalink
#26520 Add translations to AI Extensions on Block Editor (#26871)
Browse files Browse the repository at this point in the history
* 26551 UI `AiImagePromptInputComponent` done

* 26551 UI `AiImagePromptInputComponent` fixes

* 26551 UI `AiImagePromptInputComponent` remove unnused code

* issue-26551 fix comments

* feat(block-editor) added dotMessageService to BlockEditor

* feat(block-editor) #26520 added dotMessageService to Block Editor

* feat(block-editor) #26520 added translations to Language.properties

* feat(ai-image-prompt): add pipe to translate strings

* fix: failing test

* fix: failing test
  • Loading branch information
oidacra authored Dec 4, 2023
1 parent 1e7037f commit 1814d9e
Show file tree
Hide file tree
Showing 15 changed files with 172 additions and 44 deletions.
3 changes: 2 additions & 1 deletion core-web/apps/dotcms-block-editor/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { NgModule, Injector, DoBootstrap } from '@angular/core';
import { DoBootstrap, Injector, NgModule } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
Expand All @@ -13,6 +13,7 @@ import { BlockEditorModule, DotBlockEditorComponent } from '@dotcms/block-editor
import { DotPropertiesService } from '@dotcms/data-access';

import { AppComponent } from './app.component';

@NgModule({
declarations: [AppComponent],
imports: [
Expand Down
10 changes: 9 additions & 1 deletion core-web/apps/dotcms-ui/.storybook/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ module.exports = function expressMiddleware(router) {
})
);

console.info(`\x1b[35m[dotCMS]\x1b[0m`, 'Using middleware for storybook');
router.use(
'/api/v2/languages/default/keys',
createProxyMiddleware({
target: 'http://localhost:8080',
changeOrigin: true
})
);

console.info(`\x1b[32m[dotCMS]\x1b[0m`, 'Using middleware for storybook');
}
};
33 changes: 25 additions & 8 deletions core-web/libs/block-editor/src/lib/block-editor.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

// DotCMS JS
import { DotMessageService } from '@dotcms/data-access';
import { LoggerService, StringUtils } from '@dotcms/dotcms-js';
import { DotFieldRequiredDirective } from '@dotcms/ui';
import { DotFieldRequiredDirective, DotMessagePipe } from '@dotcms/ui';

//Editor
import { DotBlockEditorComponent } from './components/dot-block-editor/dot-block-editor.component';
Expand All @@ -13,23 +14,26 @@ import {
AIContentActionsComponent,
AIContentPromptComponent,
AIImagePromptComponent,
BubbleFormComponent,
BubbleLinkFormComponent,
BubbleMenuButtonComponent,
BubbleMenuComponent,
DragHandlerComponent,
FloatingButtonComponent,
FormActionsComponent,
SuggestionPageComponent,
UploadPlaceholderComponent
} from './extensions';
import { AssetFormModule } from './extensions/asset-form/asset-form.module';
import { BubbleFormComponent } from './extensions/bubble-form/bubble-form.component';
import { FloatingButtonComponent } from './extensions/floating-button/floating-button.component';
import { ContentletBlockComponent } from './nodes';
import { DotAiService, DotUploadFileService } from './shared';
import { EditorDirective } from './shared/directives';
import { DotAiService, DotUploadFileService, EditorDirective } from './shared';
import { PrimengModule } from './shared/primeng.module';
import { SharedModule } from './shared/shared.module';

const initTranslations = (dotMessageService: DotMessageService) => {
return () => dotMessageService.init();
};

@NgModule({
imports: [
CommonModule,
Expand All @@ -40,7 +44,8 @@ import { SharedModule } from './shared/shared.module';
AssetFormModule,
DotFieldRequiredDirective,
UploadPlaceholderComponent,
AIImagePromptComponent
AIImagePromptComponent,
DotMessagePipe
],
declarations: [
EditorDirective,
Expand All @@ -58,7 +63,19 @@ import { SharedModule } from './shared/shared.module';
AIContentPromptComponent,
AIContentActionsComponent
],
providers: [DotUploadFileService, LoggerService, StringUtils, DotAiService],
providers: [
DotUploadFileService,
LoggerService,
StringUtils,
DotAiService,
DotMessageService,
{
provide: APP_INITIALIZER,
useFactory: initTranslations,
deps: [DotMessageService],
multi: true
}
],
exports: [
EditorDirective,
BubbleMenuComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { OrderListModule } from 'primeng/orderlist';

import { debounceTime, delay, tap } from 'rxjs/operators';

import { DotPropertiesService } from '@dotcms/data-access';
import { DotMessageService, DotPropertiesService } from '@dotcms/data-access';

import { DotBlockEditorComponent } from './dot-block-editor.component';

Expand All @@ -24,16 +24,17 @@ import {
} from '../../extensions';
import { ContentletBlockComponent } from '../../nodes';
import {
DotAiService,
ASSET_MOCK,
CONTENTLETS_MOCK,
DotAiService,
DotLanguageService,
DotUploadFileService,
FileStatus,
SearchService,
SuggestionsComponent,
SuggestionsService
} from '../../shared';
import { DotMessageServiceMock } from '../../shared/mocks/dot-message.service.mock';
import { DotAiServiceMock } from '../../shared/services/dot-ai/dot-ai-service.mock';

export default {
Expand Down Expand Up @@ -194,6 +195,13 @@ export const primary = () => ({
{
provide: DotAiService,
useClass: process.env.USE_MIDDLEWARE === 'true' ? DotAiService : DotAiServiceMock
},
{
provide: DotMessageService,
useClass:
process.env.USE_MIDDLEWARE === 'true'
? DotMessageService
: DotMessageServiceMock
}
],
// We need these here because they are dynamically rendered
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { Component, EventEmitter, Output, OnInit, ChangeDetectionStrategy } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
inject,
OnInit,
Output
} from '@angular/core';

import { DotMessageService } from '@dotcms/data-access';

interface ActionOption {
label: string;
Expand All @@ -21,26 +30,26 @@ export enum ACTIONS {
})
export class AIContentActionsComponent implements OnInit {
@Output() actionEmitter = new EventEmitter<ACTIONS>();

actionOptions!: ActionOption[];
tooltipContent = 'Describe the size, color palette, style, mood, etc.';
private dotMessageService: DotMessageService = inject(DotMessageService);

ngOnInit() {
this.actionOptions = [
{
label: 'Accept',
label: this.dotMessageService.get('block-editor.common.accept'),
icon: 'pi pi-check',
callback: () => this.emitAction(ACTIONS.ACCEPT),
selectedOption: true
},
{
label: 'Regenerate',
label: this.dotMessageService.get('block-editor.common.regenerate'),
icon: 'pi pi-sync',
callback: () => this.emitAction(ACTIONS.REGENERATE),
selectedOption: false
},
{
label: 'Delete',
label: this.dotMessageService.get('block-editor.common.delete'),
icon: 'pi pi-trash',
callback: () => this.emitAction(ACTIONS.DELETE),
selectedOption: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@
<input
#input
[disabled]="vm.loading"
autofocus
formControlName="textPrompt"
type="text"
pInputText
autofocus
placeholder="Ask AI to write something" />
placeholder="{{
'block-editor.extension.ai-content.ask-ai-to-write-something' | dm
}}"
type="text" />

<ng-container *ngIf="vm.loading; else submitButton">
<span class="pending-label">Pending</span>
<span class="pending-label">{{ 'block-editor.common.pending' | dm }}</span>
</ng-container>

<ng-template #submitButton>
<button
class="p-button-rounded p-button-text"
icon="pi pi-send"
pButton
type="submit"
icon="pi pi-send"></button>
type="submit"></button>
</ng-template>
</span>
</form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
[style]="{ width: '800px' }"
(onHide)="hideDialog()"
appendTo="body"
header="Generate AI Image">
header="{{ 'block-editor.extension.ai-image.dialog-title' | dm }}">
<div class="dialog-prompts__wrapper grid">
<dot-ai-image-prompt-input
class="col"
[isLoading]="vm.status === ComponentStatus.LOADING"
[selected]="vm.selectedPromptType === 'input'"
(click)="selectType('input', vm.selectedPromptType)"
(promptChanged)="generateImage($event)"
placeholder="Create a realistic image of a cow in the snow"
placeholder="{{ 'block-editor.extension.ai-image.input-text.placeholder' | dm }}"
type="input" />

<dot-ai-image-prompt-input
Expand All @@ -24,7 +24,7 @@
[selected]="vm.selectedPromptType === 'auto'"
(click)="selectType('auto', vm.selectedPromptType)"
(promptChanged)="generateImage($event)"
placeholder="E.g. 1200x800px, vibrant colors, impressionistic, adventurous."
placeholder="{{ 'block-editor.extension.ai-image.auto-text.placeholder' | dm }}"
type="auto" />
</div>
</p-dialog>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { OverlayPanelModule } from 'primeng/overlaypanel';
import { TooltipModule } from 'primeng/tooltip';

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

import { PromptType } from './ai-image-prompt.models';
import { DotAiImagePromptStore, VmAiImagePrompt } from './ai-image-prompt.store';
Expand All @@ -29,12 +30,13 @@ import { AiImagePromptInputComponent } from './components/ai-image-prompt-input/
DialogModule,
AiImagePromptInputComponent,
AiImagePromptInputComponent,
AsyncPipe
AsyncPipe,
DotMessagePipe
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AIImagePromptComponent {
vm$: Observable<VmAiImagePrompt> = inject(DotAiImagePromptStore).vm$;
protected readonly vm$: Observable<VmAiImagePrompt> = inject(DotAiImagePromptStore).vm$;

protected readonly ComponentStatus = ComponentStatus;
private store = inject(DotAiImagePromptStore);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@

<div class="flex gap-1">
<p class="text-center">
<strong>Generate</strong> an AI image based on your input and requests.
{{ 'block-editor.extension.ai-image.input-text.title' | dm }}
<i
class="pi pi-info-circle"
pTooltip="Describe the type of image you want to generate."
pTooltip="{{ 'block-editor.extension.ai-image.input-text.tooltip' | dm }}"
tooltipZIndex="999999"></i>
</p>
</div>
Expand All @@ -50,14 +50,14 @@
</div>
<dot-field-validation-message
[field]="promptControl"
message="The information provided is insufficient" />
message=" {{ 'block-editor.common.input-prompt-required-error' | dm }}" />

<button
class="p-button-outlined"
[loading]="isLoading"
(click)="generateImage()"
icon="pi pi-send"
label="Generate"
label="{{ 'block-editor.common.generate' | dm }}"
pButton
type="submit"></button>
</form>
Expand All @@ -84,11 +84,10 @@

<div class="flex gap-1">
<p class="text-center">
<strong>Auto-Generate</strong> an Image based on the content created within the
Block Editor.
{{ 'block-editor.extension.ai-image.auto-text.title' | dm }}
<i
class="pi pi-info-circle"
pTooltip="Describe the size, color palette, style, mood, etc."
pTooltip="{{ 'block-editor.extension.ai-image.auto-text.tooltip' | dm }}"
tooltipZIndex="999999"></i>
</p>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Injectable } from '@angular/core';

import { formatMessage } from '@dotcms/utils';

// Move all this translations to Language.properties
export const MessageServiceMock: Record<string, string> = {
'block-editor.common.accept': 'Accept',
'block-editor.common.delete': 'Delete',
'block-editor.common.regenerate': 'Regenerate',
'block-editor.common.pending': 'Pending',
'block-editor.common.generate': 'Generate',
'block-editor.common.input-prompt-required-error': 'The information provided is insufficient',

'block-editor.extension.ai-content.ask-ai-to-write-something': 'Ask AI to write something',

'block-editor.extension.ai-image.dialog-title': 'Generate AI Image',

'block-editor.extension.ai-image.input-text.title':
'Generate an AI image based on your input and requests.',
'block-editor.extension.ai-image.input-text.placeholder':
'Create a realistic image of a cow in the snow',
'block-editor.extension.ai-image.input-text.tooltip':
'Describe the type of image you want to generate.',

'block-editor.extension.ai-image.auto-text.title':
'Auto-Generate an Image based on the content created within the Block Editor.',
'block-editor.extension.ai-image.auto-text.tooltip':
'Describe the size, color palette, style, mood, etc.',
'block-editor.extension.ai-image.auto-text.placeholder':
'E.g. 1200x800px, vibrant colors, impressionistic, adventurous.'
};

@Injectable()
export class DotMessageServiceMock {
init() {
// fake init
}
get(key: string, ...args: string[]): string {
return MessageServiceMock[key]
? args.length
? formatMessage(MessageServiceMock[key], args)
: MessageServiceMock[key]
: key;
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { Observable, of } from 'rxjs';

import { delay } from 'rxjs/operators';

const DEFAULT_DELAY = 2000;
export class DotAiServiceMock {
getIAContent(): Observable<string> {
generateContent(): Observable<string> {
return of(
`
Title: Understanding the Inner Workings of an Internal Combustion Engine\n\nIntroduction:\nAn internal combustion engine, commonly known as a "motor," is a fundamental component powering various modes of transportation today. Whether it is used in cars, motorcycles, or even power generators, this remarkable mechanical device plays a pivotal role in our modern society. In this post, we will delve into the intricate workings of an internal combustion engine, shedding light on its main components and the amazing combustion process that propels our vehicles forward.\n\n1. The Components:\nTypically consisting of multiple intricate parts, an internal combustion engine can be simplified into four main components:\n\na. Cylinder Block:\nA robust casing houses the cylinder, piston, and other associated components. This block not only provides structural strength but also assists in dissipating the heat produced during the engine's operation.\n\nb. Pistons:\nPlaced inside each cylinder, pistons move up and down during the engine's operation. Connected to the crankshaft, these essential components transform the linear motion of the pistons into the rotary motion necessary to turn the wheels.\n\nc. Valves:\nIntake and exhaust valves control the flow of air-fuel mixture into the combustion chamber and the expulsion of combustion byproducts. These valves play a crucial role in optimizing engine performance and ensuring efficient fuel combustion.\n\nd. Spark Plugs:\nElectrically ignited by the engine control unit (ECU), spark plugs generate a spark within the combustion chamber, initiating the combustion process. This controlled ignition ensures the synchronized release of energy within the engine.\n\n2. The Combustion Process:\nThe combustion process within an internal combustion engine can be summarized into four essential steps:\n\na. Intake:\nWith the intake valve open, a carefully regulated mixture of air and fuel is drawn into the combustion chamber during the piston's downward stroke.\n\nb. Compression:\nDuring the upward stroke of the piston, the intake valve closes, sealing the combustion chamber. The piston compresses the air-fuel mixture, significantly increasing its pressure and temperature.\n\nc. Combustion and Power Stroke:\nAt the desired moment, the spark plug sparks, igniting the compressed air-fuel mixture. This rapid combustion creates an explosion, generating a force that drives the piston back down, converting the expanding high-pressure gases into mechanical energy.\n\nd. Exhaust:\nWhen the piston reaches the bottom of its stroke, the exhaust valve opens, allowing the expulsion of spent gases resulting from the combustion process. The piston then moves back up to restart the cycle, and the process repeats.\n\nConclusion:\nThe internal combustion engine's marvel lies in its ability to convert chemical energy stored in fuel into mechanical energy, enabling the propulsion of vehicles we rely on daily. Understanding its intricate components and the combustion process that takes place within can help us appreciate the engineering brilliance behind this staple technology. From the rhythmic motion of pistons to the precisely timed ignition of fuel, every aspect of an internal combustion engine harmoniously collaborates to fuel our ever-advancing world
`
);
).pipe(delay(DEFAULT_DELAY));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const headers = new HttpHeaders({

@Injectable()
export class DotAiService {

constructor(private http: HttpClient) {}

generateContent(prompt: string): Observable<string> {
Expand Down
Loading

0 comments on commit 1814d9e

Please sign in to comment.