@@ -13,11 +13,16 @@ import { each } from '@js/core/utils/iterator';
1313import {
1414 isDefined , isEmptyObject , isObject , isString ,
1515} from '@js/core/utils/type' ;
16+ import type { AICommandName , AICustomCommand , AIToolbarItem } from '@js/ui/html_editor' ;
1617import type { Item } from '@js/ui/toolbar' ;
1718import Toolbar from '@js/ui/toolbar' ;
1819import errors from '@js/ui/widget/ui.errors' ;
20+ import { capitalize } from '@ts/core/utils/capitalize' ;
1921import Quill from 'devextreme-quill' ;
2022
23+ import {
24+ buildCommandsMap , defaultCommandNames , getDefaultOptionsByCommand ,
25+ } from '../utils/ai' ;
2126import { getTableFormats , TABLE_OPERATIONS } from '../utils/m_table_helper' ;
2227import {
2328 applyFormat , getDefaultClickHandler , getFormatHandlers , ICON_MAP ,
@@ -55,6 +60,8 @@ if (Quill) {
5560 u : 85 ,
5661 } ;
5762
63+ const TOOLBAR_AI_ITEM_NAME = 'ai' ;
64+
5865 const localize = ( name ) => localizationMessage . format ( `dxHtmlEditor-${ camelize ( name ) } ` ) ;
5966
6067 const localizeValue = ( value , name ) => {
@@ -288,6 +295,8 @@ if (Quill) {
288295 this . _detectRenamedOptions ( item ) ;
289296 if ( isObject ( item ) ) {
290297 newItem = this . _handleObjectItem ( item ) ;
298+ } else if ( item === TOOLBAR_AI_ITEM_NAME ) {
299+ resultItems . push ( this . _getToolbarItem ( this . _prepareAIMenuItemConfig ( item ) ) ) ;
291300 } else if ( isString ( item ) ) {
292301 const buttonItemConfig = this . _prepareButtonItemConfig ( item ) ;
293302 newItem = this . _getToolbarItem ( buttonItemConfig ) ;
@@ -301,16 +310,23 @@ if (Quill) {
301310 }
302311
303312 _handleObjectItem ( item ) {
313+ if ( item . name === TOOLBAR_AI_ITEM_NAME ) {
314+ return this . _getToolbarItem ( this . _prepareAIMenuItemConfig ( item ) ) ;
315+ }
316+
304317 if ( item . name && item . acceptedValues && this . _isAcceptableItem ( item . widget , 'dxSelectBox' ) ) {
305318 const selectItemConfig = this . _prepareSelectItemConfig ( item ) ;
306319
307320 return this . _getToolbarItem ( selectItemConfig ) ;
308- } if ( item . name && this . _isAcceptableItem ( item . widget , 'dxButton' ) ) {
321+ }
322+
323+ if ( item . name && this . _isAcceptableItem ( item . widget , 'dxButton' ) ) {
309324 const defaultButtonItemConfig = this . _prepareButtonItemConfig ( item . name ) ;
310325 const buttonItemConfig = extend ( true , defaultButtonItemConfig , item ) ;
311326
312327 return this . _getToolbarItem ( buttonItemConfig ) ;
313328 }
329+
314330 return this . _getToolbarItem ( item ) ;
315331 }
316332
@@ -358,6 +374,92 @@ if (Quill) {
358374 } , item ) ;
359375 }
360376
377+ private _createCommandMenuItem (
378+ command : AICommandName ,
379+ text ?: string ,
380+ commandOptions ?: string [ ] ,
381+ ) {
382+ const options = commandOptions ?? getDefaultOptionsByCommand ( command ) ?. map ( capitalize ) ;
383+
384+ return {
385+ id : command ,
386+ text : text ?? defaultCommandNames [ command ] ,
387+ items : options ?. map ( ( option ) => ( {
388+ id : option ,
389+ text : option ,
390+ parentCommand : command ,
391+ options : options ?. map ( capitalize ) ,
392+ } ) ) ,
393+ } ;
394+ }
395+
396+ private _buildMenuItems ( commands : AIToolbarItem [ 'commands' ] ) {
397+ return commands ?. map ( ( command ) => {
398+ if ( typeof command === 'object' ) {
399+ if ( command . name === 'custom' ) {
400+ return {
401+ id : 'custom' ,
402+ text : command . text ,
403+ items : command . options ?. map ( ( option ) => ( {
404+ parentCommand : 'custom' ,
405+ id : option ,
406+ text : option ,
407+ options : command . options . map ( capitalize ) ,
408+ prompt,
409+ } ) ) ,
410+ prompt : ( command as AICustomCommand ) . prompt ,
411+ } ;
412+ }
413+
414+ return this . _createCommandMenuItem ( command . name , command . text , command . options ) ;
415+ }
416+
417+ return this . _createCommandMenuItem ( command ) ;
418+ } ) ;
419+ }
420+
421+ _prepareAIMenuItemConfig ( item : AIToolbarItem ) {
422+ const {
423+ name = TOOLBAR_AI_ITEM_NAME ,
424+ commands = Object . keys ( defaultCommandNames ) as AICommandName [ ] ,
425+ } = item ;
426+
427+ const commandsMap = buildCommandsMap ( commands ) ;
428+ const menuItems = this . _buildMenuItems ( commands ) ;
429+
430+ const dataSource = [ {
431+ id : 'root' ,
432+ icon : 'sparkle' ,
433+ items : menuItems ,
434+ } ] ;
435+
436+ const options = {
437+ dataSource,
438+ onItemClick : ( e ) : void => {
439+ const { itemData } = e ;
440+
441+ if ( itemData . items ?. length ) {
442+ return ;
443+ }
444+
445+ const aiDialogOptions = {
446+ command : itemData . id ,
447+ parentCommand : itemData . parentCommand ,
448+ commandsMap,
449+ prompt : itemData . prompt ,
450+ } ;
451+
452+ this . _formatHandlers [ name ] ( aiDialogOptions ) ;
453+ } ,
454+ } ;
455+
456+ return extend ( true , {
457+ widget : 'dxMenu' ,
458+ name,
459+ options,
460+ } , typeof item === 'string' ? { } : item ) ;
461+ }
462+
361463 _hideAdaptiveMenu ( ) {
362464 if ( this . toolbarInstance . option ( 'overflowMenuVisible' ) ) {
363465 this . toolbarInstance . option ( 'overflowMenuVisible' , false ) ;
0 commit comments