Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ai assistant widget #4508

Merged
merged 13 commits into from
May 2, 2024
Merged
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
"sass-loader": "6.0.6",
"shortid": "2.2.8",
"style-loader": "0.20.2",
"superdesk-ui-framework": "^3.0.82",
"superdesk-ui-framework": "^3.1.3",
"ts-loader": "3.5.0",
"typescript": "4.9.5",
"uuid": "8.3.1",
Expand Down
9 changes: 9 additions & 0 deletions scripts/api/article.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import {sendItems} from './article-send';
import {authoringApiCommon} from 'apps/authoring-bridge/authoring-api-common';
import {CONTENT_FIELDS_DEFAULTS} from 'apps/authoring/authoring/helpers';
import _ from 'lodash';

Check warning on line 33 in scripts/api/article.ts

View workflow job for this annotation

GitHub Actions / test

'_' is defined but never used

const isLocked = (_article: IArticle) => _article.lock_session != null;
const isLockedInCurrentSession = (_article: IArticle) => _article.lock_session === ng.get('session').sessionId;
Expand Down Expand Up @@ -216,6 +216,13 @@
});
}

function createNewWithData(data: Partial<IArticle>, contentProfileId: string): void {
dataApi.create('archive', {type: 'text', ...data, profile: contentProfileId})
.then((item) => {
openArticle(item._id, 'edit');
});
}

/**
* Checks if associations is with rewrite_of item then open then modal to add associations.
* The user has options to add associated media to the current item and review the media change
Expand Down Expand Up @@ -258,7 +265,7 @@
});
}

function notifyPreconditionFailed($scope: any) {

Check warning on line 268 in scripts/api/article.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
notify.error(gettext('Item has changed since it was opened. ' +
'Please close and reopen the item to continue. ' +
'Regrettably, your changes cannot be saved.'));
Expand Down Expand Up @@ -556,12 +563,13 @@
unlock(itemId: IArticle['_id']): Promise<IArticle>;

createNewUsingDeskTemplate(): void;
createNewWithData(data: Partial<IArticle>, contentProfileId: string): void;
getWorkQueueItems(): Array<IArticle>;
rewrite(item: IArticle): void;
canPublishOnDesk(deskType: string): boolean;
showCloseAndContinue(item: IArticle, dirty: boolean): boolean;
showPublishAndContinue(item: IArticle, dirty: boolean): boolean;
publishItem_legacy(orig: IArticle, item: IArticle, $scope: any, action?: IAuthoringActionType): Promise<boolean>;

Check warning on line 572 in scripts/api/article.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type

getItemPatchWithKillOrTakedownTemplate(item: IArticle, action: IAuthoringActionType): Promise<IArticle>;

Expand Down Expand Up @@ -618,6 +626,7 @@
lock,
unlock,
createNewUsingDeskTemplate,
createNewWithData,
getWorkQueueItems,
get,
canPublishOnDesk,
Expand Down
12 changes: 6 additions & 6 deletions scripts/apps/authoring-react/authoring-integration-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ interface IPropsWrapper extends IProps {
interface IState {
sidebarMode: boolean | 'hidden';
sideWidget: null | {
name: string;
id: string;
pinned: boolean;
};
}
Expand Down Expand Up @@ -313,19 +313,19 @@ export class AuthoringIntegrationWrapper extends React.PureComponent<IPropsWrapp
icon: widget.icon,
size: 'big',
tooltip: widget.label,
id: widget.label,
id: widget._id,
};

return tab;
});

return (
<Nav.SideBarTabs
activeTab={this.state.sideWidget?.name}
activeTab={this.state.sideWidget?.id}
onActiveTabChange={(val) => {
this.setState({
sideWidget: {
name: val,
id: val,
pinned: this.state.sideWidget?.pinned ?? false,
},
});
Expand Down Expand Up @@ -483,8 +483,8 @@ export class AuthoringIntegrationWrapper extends React.PureComponent<IPropsWrapp
getSidebar={this.state.sidebarMode !== true ? null : getSidebar}
secondaryToolbarWidgets={secondaryToolbarWidgetsReady}
validateBeforeSaving={false}
getSideWidgetNameAtIndex={(article, index) => {
return getWidgetsFromExtensions(article)[index].label;
getSideWidgetIdAtIndex={(article, index) => {
return getWidgetsFromExtensions(article)[index]._id;
}}
/>
);
Expand Down
16 changes: 8 additions & 8 deletions scripts/apps/authoring-react/authoring-react.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -335,14 +335,14 @@ export class AuthoringReact<T extends IBaseRestApiResponse> extends React.PureCo
};

widgetReactIntegration.getActiveWidget = () => {
return this.props.sideWidget?.name ?? null;
return this.props.sideWidget?.id ?? null;
};

widgetReactIntegration.getPinnedWidget = () => {
const pinned = this.props.sideWidget?.pinned === true;

if (pinned) {
return this.props.sideWidget.name;
return this.props.sideWidget.id;
} else {
return null;
}
Expand Down Expand Up @@ -1113,13 +1113,13 @@ export class AuthoringReact<T extends IBaseRestApiResponse> extends React.PureCo
authoringStorage: authoringStorage,
storageAdapter: storageAdapter,
fieldsAdapter: fieldsAdapter,
sideWidget: this.props.sideWidget?.name ?? null,
toggleSideWidget: (name) => {
if (name == null || this.props.sideWidget?.name === name) {
sideWidget: this.props.sideWidget?.id ?? null,
toggleSideWidget: (id) => {
if (id == null || this.props.sideWidget?.id === id) {
this.props.onSideWidgetChange(null);
} else {
this.props.onSideWidgetChange({
name: name,
id: id,
pinned: false,
});
}
Expand Down Expand Up @@ -1229,10 +1229,10 @@ export class AuthoringReact<T extends IBaseRestApiResponse> extends React.PureCo

for (let i = 0; i < widgetsCount; i++) {
widgetKeybindings[`ctrl+alt+${i + 1}`] = () => {
const nextWidgetName: string = this.props.getSideWidgetNameAtIndex(exposed.item, i);
const nextWidgetName: string = this.props.getSideWidgetIdAtIndex(exposed.item, i);

this.props.onSideWidgetChange({
name: nextWidgetName,
id: nextWidgetName,
pinned: this.props.sideWidget?.pinned ?? false,
});
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,7 @@ export function AuthoringDirective(
angular.extend($scope.item, event.detail);
angular.extend($scope.origItem, event.detail);
$scope.$apply();
$scope.refresh();
}
},
);
Expand Down
9 changes: 8 additions & 1 deletion scripts/core/get-superdesk-api-implementation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
stripHtmlTags,
stripLockingFields,
getProjectedFieldsArticle,
getArticleLabel,
} from 'core/utils';
import {ListItem, ListItemColumn, ListItemRow, ListItemActionsMenu} from './components/ListItem';
import {getFormFieldPreviewComponent} from './ui/components/generic-form/form-field';
Expand Down Expand Up @@ -68,7 +69,7 @@ import {Icon} from './ui/components/Icon2';
import {AuthoringWorkspaceService} from 'apps/authoring/authoring/services/AuthoringWorkspaceService';
import ng from 'core/services/ng';
import {Spacer, SpacerBlock, SpacerInlineFlex} from './ui/components/Spacer';
import {appConfig} from 'appConfig';
import {appConfig, authoringReactViewEnabled} from 'appConfig';
import {httpRequestJsonLocal, httpRequestVoidLocal, httpRequestRawLocal} from './helpers/network';
import {memoize as memoizeLocal} from './memoize';
import {generatePatch} from './patch';
Expand Down Expand Up @@ -117,6 +118,7 @@ import {getCustomFieldVocabularies, getLanguageVocabulary} from './helpers/busin
import {PreviewFieldType} from 'apps/authoring/preview/previewFieldByType';
import {getLabelNameResolver} from 'apps/workspace/helpers/getLabelForFieldId';
import {getSortedFields, getSortedFieldsFiltered} from 'apps/authoring/preview/utils';
import {editor3ToOperationalFormat} from 'apps/authoring-react/fields/editor3';

function getContentType(id): Promise<IContentProfile> {
return dataApi.findOne('content_types', id);
Expand Down Expand Up @@ -277,10 +279,12 @@ export function getSuperdeskApiImplementation(
fixPatchRequest,
fixPatchResponse,
computeEditor3Output,
editor3ToOperationalFormat: editor3ToOperationalFormat,
getContentStateFromHtml: (html) => getContentStateFromHtml(html),
tryLocking,
tryUnlocking,
superdeskToElasticQuery: toElasticQuery,
getArticleLabel,
},
httpRequestJsonLocal,
httpRequestRawLocal,
Expand All @@ -293,6 +297,7 @@ export function getSuperdeskApiImplementation(
isLockedInCurrentSession: sdApi.article.isLockedInCurrentSession,
isLockedInOtherSession: sdApi.article.isLockedInOtherSession,
patch: patchArticle,
createNewWithData: sdApi.article.createNewWithData,
isArchived: sdApi.article.isArchived,
isPublished: (article) => sdApi.article.isPublished(article),
itemAction: (article) => sdApi.article.itemAction(article),
Expand All @@ -307,6 +312,7 @@ export function getSuperdeskApiImplementation(
.then((response) => response._items),
getActiveDeskId: sdApi.desks.getActiveDeskId,
waitTilReady: sdApi.desks.waitTilReady,
getDeskById: sdApi.desks.getDeskById,
},
contentProfile: {
get: (id) => {
Expand Down Expand Up @@ -353,6 +359,7 @@ export function getSuperdeskApiImplementation(
state: applicationState,
instance: {
config,
authoringReactViewEnabled,
},
ui: {
article: {
Expand Down
59 changes: 32 additions & 27 deletions scripts/core/superdesk-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ declare module 'superdesk-api' {
*/
export interface IExposedFromAuthoring<T> {
item: T;
sideWidget: string | null; // side widget name
sideWidget: string | null; // side widget id

/**
* Computes the latest entity from fields data. `item` property in
Expand All @@ -146,7 +146,7 @@ declare module 'superdesk-api' {
* we are passing a function instead.
*/
getLatestItem(options?: {preferIncomplete?: IStoreValueIncomplete}): T;
toggleSideWidget(name: string | null): void;
toggleSideWidget(id: string | null): void;
contentProfile: IContentProfileV2;
fieldsData: IFieldsData;
authoringStorage: IAuthoringStorage<T>;
Expand Down Expand Up @@ -215,11 +215,11 @@ declare module 'superdesk-api' {
getSidebarWidgetsCount(options: IExposedFromAuthoring<T>): number;

sideWidget: null | {
name: string;
id: string;
pinned: boolean;
};

getSideWidgetNameAtIndex(item: T, index: number): string;
getSideWidgetIdAtIndex(item: T, index: number): string;
onSideWidgetChange(openWidget: IPropsAuthoring<T>['sideWidget']): void;

// Runs before re-render.
Expand Down Expand Up @@ -601,6 +601,28 @@ declare module 'superdesk-api' {
isAllowed: (entity: T) => boolean;
}

export interface IArticleSideWidgetComponentType {
article: IArticle;

getLatestArticle: IExposedFromAuthoring<IArticle>['getLatestItem'];

// other props below are specific to authoring-react implementation

readOnly: boolean;
contentProfile?: IContentProfileV2;
fieldsData?: OrderedMap<string, unknown>;
authoringStorage: IAuthoringStorage<IArticle>;
fieldsAdapter: IFieldsAdapter<IArticle>;
storageAdapter: IStorageAdapter<IArticle>;

onItemChange?(article: IArticle): void;
onFieldsDataChange?(fieldsData?: OrderedMap<string, unknown>): void;
/**
* Will prompt user to save changes. The promise will get rejected if user cancels saving.
*/
handleUnsavedChanges(): Promise<IArticle>;
}

/**
* @deprecated: prefer {@link IGenericSideWidget}
*/
Expand All @@ -609,28 +631,7 @@ declare module 'superdesk-api' {
label: string;
order: number; // Integer. // NICE-TO-HAVE: manage order in the UI instead of here
icon: string;
component: React.ComponentType<{
article: IArticle;


getLatestArticle: IExposedFromAuthoring<IArticle>['getLatestItem'];

// other props below are specific to authoring-react implementation

readOnly: boolean;
contentProfile?: IContentProfileV2;
fieldsData?: OrderedMap<string, unknown>;
authoringStorage: IAuthoringStorage<IArticle>;
fieldsAdapter: IFieldsAdapter<IArticle>;
storageAdapter: IStorageAdapter<IArticle>;

onItemChange?(article: IArticle): void;
onFieldsDataChange?(fieldsData?: OrderedMap<string, unknown>): void;
/**
* Will prompt user to save changes. The promise will get rejected if user cancels saving.
*/
handleUnsavedChanges(): Promise<IArticle>;
}>;
component: React.ComponentType<IArticleSideWidgetComponentType>;
isAllowed?(article: IArticle): boolean; // enables limiting widgets depending on article data
}

Expand Down Expand Up @@ -2741,6 +2742,7 @@ declare module 'superdesk-api' {
};
instance: {
config: ISuperdeskGlobalConfig;
authoringReactViewEnabled: boolean;
};

/** Retrieves configuration options passed when registering an extension. */
Expand Down Expand Up @@ -2790,7 +2792,7 @@ declare module 'superdesk-api' {
patch: Partial<IArticle>,
dangerousOptions?: IDangerousArticlePatchingOptions,
): Promise<void>;

createNewWithData(data: Partial<IArticle>, contentProfileId: string): void;
isArchived(article: IArticle): boolean;
isPublished(article: IArticle): boolean;
itemAction(article: IArticle): {[key in IAuthoringActionType]: boolean};
Expand Down Expand Up @@ -2828,6 +2830,7 @@ declare module 'superdesk-api' {
getStagesOrdered(deskId: IDesk['_id']): Promise<Array<IStage>>;
getActiveDeskId(): IDesk['_id'] | null;
waitTilReady(): Promise<void>;
getDeskById(id: IDesk['_id']): IDesk;
};
attachment: IAttachmentsApi;
users: {
Expand All @@ -2844,6 +2847,7 @@ declare module 'superdesk-api' {
};
};
helpers: {
getArticleLabel(item: IArticle): string;
assertNever(x: never): never;
stripBaseRestApiFields<T extends IBaseRestApiResponse>(entity: T): Omit<T, keyof IBaseRestApiResponse>;
fixPatchResponse<T extends IBaseRestApiResponse>(entity: T & {_status: string}): T;
Expand All @@ -2864,6 +2868,7 @@ declare module 'superdesk-api' {
endpoint: string,
entityId: string,
): Promise<void>;
editor3ToOperationalFormat(value: IEditor3ValueStorage, language: string): IEditor3ValueOperational;
computeEditor3Output(
rawContentState: import('draft-js').RawDraftContentState,
config: IEditor3Config,
Expand Down
1 change: 1 addition & 0 deletions scripts/extensions/ai-widget/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
Loading
Loading