@webiny/app-file-manager 6.4.11 → 6.6.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app.js +21 -2
- package/app.js.map +1 -1
- package/domain/tagsHelpers.d.ts +2 -2
- package/domain/types.d.ts +0 -8
- package/features/permissions/abstractions.d.ts +2 -2
- package/features/permissions/feature.d.ts +3 -3
- package/features/shared/FILE_FIELDS.js +4 -0
- package/features/shared/FILE_FIELDS.js.map +1 -1
- package/features/shared/FileFieldsProviderWithWcp.d.ts +5 -5
- package/features/shared/FileFieldsProviderWithWcp.js +7 -7
- package/features/shared/FileFieldsProviderWithWcp.js.map +1 -1
- package/modules/AiEnrichment/AiImageEnrichmentEventHandler.d.ts +18 -0
- package/modules/AiEnrichment/AiImageEnrichmentEventHandler.js +33 -0
- package/modules/AiEnrichment/AiImageEnrichmentEventHandler.js.map +1 -0
- package/modules/AiEnrichment/ReenrichFileGateway.d.ts +12 -0
- package/modules/AiEnrichment/ReenrichFileGateway.js +23 -0
- package/modules/AiEnrichment/ReenrichFileGateway.js.map +1 -0
- package/modules/AiEnrichment/ReenrichWithAi.d.ts +11 -0
- package/modules/AiEnrichment/ReenrichWithAi.js +57 -0
- package/modules/AiEnrichment/ReenrichWithAi.js.map +1 -0
- package/modules/AiEnrichment/ReenrichWithAiPresenter.d.ts +50 -0
- package/modules/AiEnrichment/ReenrichWithAiPresenter.js +114 -0
- package/modules/AiEnrichment/ReenrichWithAiPresenter.js.map +1 -0
- package/modules/AiEnrichment/StreamingPlaceholder.d.ts +15 -0
- package/modules/AiEnrichment/StreamingPlaceholder.js +32 -0
- package/modules/AiEnrichment/StreamingPlaceholder.js.map +1 -0
- package/modules/AiEnrichment/abstractions.d.ts +70 -0
- package/modules/AiEnrichment/abstractions.js +6 -0
- package/modules/AiEnrichment/abstractions.js.map +1 -0
- package/modules/AiEnrichment/feature.d.ts +3 -0
- package/modules/AiEnrichment/feature.js +21 -0
- package/modules/AiEnrichment/feature.js.map +1 -0
- package/modules/AiEnrichment/index.d.ts +2 -0
- package/modules/AiEnrichment/index.js +9 -0
- package/modules/AiEnrichment/index.js.map +1 -0
- package/modules/Enterprise/index.js +3 -3
- package/modules/Enterprise/index.js.map +1 -1
- package/modules/HeadlessCms/fieldType/AssetFieldSettingsModifier.d.ts +26 -0
- package/modules/HeadlessCms/fieldType/AssetFieldSettingsModifier.js +39 -0
- package/modules/HeadlessCms/fieldType/AssetFieldSettingsModifier.js.map +1 -0
- package/modules/HeadlessCms/fieldType/AssetFieldType.d.ts +34 -0
- package/modules/HeadlessCms/fieldType/AssetFieldType.js +42 -0
- package/modules/HeadlessCms/fieldType/AssetFieldType.js.map +1 -0
- package/modules/HeadlessCms/fieldType/AssetFormFieldType.d.ts +21 -0
- package/modules/HeadlessCms/fieldType/AssetFormFieldType.js +42 -0
- package/modules/HeadlessCms/fieldType/AssetFormFieldType.js.map +1 -0
- package/modules/HeadlessCms/fieldType/AssetInputRenderer.d.ts +15 -0
- package/modules/HeadlessCms/fieldType/AssetInputRenderer.js +20 -0
- package/modules/HeadlessCms/fieldType/AssetInputRenderer.js.map +1 -0
- package/modules/HeadlessCms/fieldType/AssetInputsRenderer.d.ts +15 -0
- package/modules/HeadlessCms/fieldType/AssetInputsRenderer.js +20 -0
- package/modules/HeadlessCms/fieldType/AssetInputsRenderer.js.map +1 -0
- package/modules/HeadlessCms/fieldType/FileFieldSettingsModifier.d.ts +16 -0
- package/modules/HeadlessCms/fieldType/FileFieldSettingsModifier.js +33 -0
- package/modules/HeadlessCms/fieldType/FileFieldSettingsModifier.js.map +1 -0
- package/modules/HeadlessCms/fieldType/FileFieldType.d.ts +25 -0
- package/modules/HeadlessCms/fieldType/FileFieldType.js +38 -0
- package/modules/HeadlessCms/fieldType/FileFieldType.js.map +1 -0
- package/modules/HeadlessCms/fieldType/FileInputRenderer.d.ts +15 -0
- package/modules/HeadlessCms/fieldType/FileInputRenderer.js +19 -0
- package/modules/HeadlessCms/fieldType/FileInputRenderer.js.map +1 -0
- package/modules/HeadlessCms/fieldType/FileInputsRenderer.d.ts +15 -0
- package/modules/HeadlessCms/fieldType/FileInputsRenderer.js +19 -0
- package/modules/HeadlessCms/fieldType/FileInputsRenderer.js.map +1 -0
- package/modules/HeadlessCms/fieldType/feature.d.ts +1 -0
- package/modules/HeadlessCms/fieldType/feature.js +27 -0
- package/modules/HeadlessCms/fieldType/feature.js.map +1 -0
- package/modules/HeadlessCms/fieldType/isAssetField.d.ts +2 -0
- package/modules/HeadlessCms/fieldType/isAssetField.js +4 -0
- package/modules/HeadlessCms/fieldType/isAssetField.js.map +1 -0
- package/package.json +24 -34
- package/presentation/FileActions/FileDetails/EditImage.d.ts +2 -0
- package/presentation/FileActions/FileDetails/EditImage.js +39 -0
- package/presentation/FileActions/FileDetails/EditImage.js.map +1 -0
- package/presentation/FileActions/index.js +8 -0
- package/presentation/FileActions/index.js.map +1 -1
- package/presentation/FileDetails/FileDetailsPresenter.d.ts +5 -4
- package/presentation/FileDetails/FileDetailsPresenter.js +40 -46
- package/presentation/FileDetails/FileDetailsPresenter.js.map +1 -1
- package/presentation/FileDetails/FileDetailsPresenter.test.js +39 -56
- package/presentation/FileDetails/FileDetailsPresenter.test.js.map +1 -1
- package/presentation/FileDetails/abstractions.d.ts +2 -0
- package/presentation/FileDetails/abstractions.js.map +1 -1
- package/presentation/FileDetails/components/Content.d.ts +6 -4
- package/presentation/FileDetails/components/Content.js +2 -2
- package/presentation/FileDetails/components/Content.js.map +1 -1
- package/presentation/FileDetails/components/FileDetailsDrawer.d.ts +2 -2
- package/presentation/FileDetails/components/FileDetailsDrawer.js +11 -5
- package/presentation/FileDetails/components/FileDetailsDrawer.js.map +1 -1
- package/presentation/FileList/FileListDataSource.d.ts +8 -12
- package/presentation/FileList/FileListDataSource.js +47 -87
- package/presentation/FileList/FileListDataSource.js.map +1 -1
- package/presentation/FileList/FileListPresenter.test.js +9 -3
- package/presentation/FileList/FileListPresenter.test.js.map +1 -1
- package/presentation/FileList/FileManagerPresenter.d.ts +1 -0
- package/presentation/FileList/FileManagerPresenter.js +15 -5
- package/presentation/FileList/FileManagerPresenter.js.map +1 -1
- package/presentation/FileList/abstractions.d.ts +3 -0
- package/presentation/FileList/abstractions.js.map +1 -1
- package/presentation/FileList/components/BulkActions/BulkActionBar.d.ts +2 -2
- package/presentation/FileList/components/BulkActions/BulkActionDelete.d.ts +2 -2
- package/presentation/FileList/components/BulkActions/BulkActionMove.d.ts +2 -2
- package/presentation/FileList/components/BulkActions/index.d.ts +0 -1
- package/presentation/FileList/components/BulkActions/index.js +0 -1
- package/presentation/FileList/components/BulkActions/useBulkActionWorker.d.ts +9 -8
- package/presentation/FileList/components/BulkActions/useBulkActionWorker.js +3 -9
- package/presentation/FileList/components/BulkActions/useBulkActionWorker.js.map +1 -1
- package/presentation/FileList/components/Filters/SubFoldersToggle.d.ts +2 -2
- package/presentation/FileList/components/Filters/TagsFilter.d.ts +2 -2
- package/presentation/FileList/components/Filters/TypeFilter.d.ts +2 -2
- package/presentation/FileList/components/Grid/FileGrid.d.ts +2 -2
- package/presentation/FileList/components/Grid/FileGrid.js +1 -1
- package/presentation/FileList/components/Grid/FileGrid.js.map +1 -1
- package/presentation/FileList/components/Grid/FileGrid.test.js +18 -7
- package/presentation/FileList/components/Grid/FileGrid.test.js.map +1 -1
- package/presentation/FileList/components/Header/FileManagerHeader.d.ts +2 -2
- package/presentation/FileList/components/Search/SearchBar.d.ts +2 -2
- package/presentation/FileList/components/Table/FileTable.d.ts +2 -2
- package/presentation/FileList/components/Table/FileTable.js +1 -1
- package/presentation/FileList/components/Table/FileTable.js.map +1 -1
- package/presentation/FileList/components/Table/FileTable.test.js +9 -4
- package/presentation/FileList/components/Table/FileTable.test.js.map +1 -1
- package/presentation/FileList/components/Upload/UploadProgress.d.ts +2 -2
- package/presentation/FileManager/FileManagerBreadcrumbs.d.ts +12 -0
- package/presentation/FileManager/FileManagerBreadcrumbs.js +37 -0
- package/presentation/FileManager/FileManagerBreadcrumbs.js.map +1 -0
- package/presentation/FileManager/FileManagerView.js +36 -2
- package/presentation/FileManager/FileManagerView.js.map +1 -1
- package/presentation/FileManager/RouteParamsSync.d.ts +2 -0
- package/presentation/FileManager/RouteParamsSync.js +30 -0
- package/presentation/FileManager/RouteParamsSync.js.map +1 -0
- package/presentation/Settings/components/SettingsView.d.ts +2 -2
- package/presentation/config/DefaultFileManagerConfig.js +13 -11
- package/presentation/config/DefaultFileManagerConfig.js.map +1 -1
- package/presentation/config/FileManagerViewConfig.d.ts +19 -25
- package/presentation/config/configComponents/Browser/BulkAction.d.ts +10 -8
- package/presentation/config/configComponents/Browser/BulkAction.js +3 -9
- package/presentation/config/configComponents/Browser/BulkAction.js.map +1 -1
- package/presentation/config/configComponents/Browser/FileAction.d.ts +2 -1
- package/presentation/config/configComponents/Browser/FolderAction.d.ts +2 -1
- package/presentation/config/configComponents/Browser/Table/Column.d.ts +9 -8
- package/presentation/config/configComponents/Browser/Table/index.d.ts +9 -9
- package/presentation/config/configComponents/Browser/index.d.ts +20 -20
- package/presentation/config/configComponents/Browser/index.js +0 -8
- package/presentation/config/configComponents/Browser/index.js.map +1 -1
- package/presentation/config/configComponents/FileDetails/index.d.ts +4 -2
- package/presentation/config/thumbnailRenderers/CroppedFileImage.d.ts +18 -0
- package/presentation/config/thumbnailRenderers/CroppedFileImage.js +66 -0
- package/presentation/config/thumbnailRenderers/CroppedFileImage.js.map +1 -0
- package/presentation/config/thumbnailRenderers/FilePreviewImageRenderer.js +6 -9
- package/presentation/config/thumbnailRenderers/FilePreviewImageRenderer.js.map +1 -1
- package/presentation/config/thumbnailRenderers/GridItemImageRenderer.js +6 -9
- package/presentation/config/thumbnailRenderers/GridItemImageRenderer.js.map +1 -1
- package/presentation/config/thumbnailRenderers/TableItemImageRenderer.js +6 -9
- package/presentation/config/thumbnailRenderers/TableItemImageRenderer.js.map +1 -1
- package/presentation/fieldRenderers/CmsAssetPickerRenderer.d.ts +22 -0
- package/presentation/fieldRenderers/CmsAssetPickerRenderer.js +56 -0
- package/presentation/fieldRenderers/CmsAssetPickerRenderer.js.map +1 -0
- package/presentation/fieldRenderers/CmsFilePickerRenderer.d.ts +13 -0
- package/presentation/fieldRenderers/CmsFilePickerRenderer.js +36 -0
- package/presentation/fieldRenderers/CmsFilePickerRenderer.js.map +1 -0
- package/presentation/fieldRenderers/CmsImageEditorDialog.d.ts +7 -0
- package/presentation/fieldRenderers/CmsImageEditorDialog.js +32 -0
- package/presentation/fieldRenderers/CmsImageEditorDialog.js.map +1 -0
- package/presentation/fieldRenderers/CmsMultiAssetPickerRenderer.d.ts +18 -0
- package/presentation/fieldRenderers/CmsMultiAssetPickerRenderer.js +88 -0
- package/presentation/fieldRenderers/CmsMultiAssetPickerRenderer.js.map +1 -0
- package/presentation/fieldRenderers/CmsMultiFilePickerRenderer.d.ts +13 -0
- package/presentation/fieldRenderers/CmsMultiFilePickerRenderer.js +60 -0
- package/presentation/fieldRenderers/CmsMultiFilePickerRenderer.js.map +1 -0
- package/presentation/fieldRenderers/CroppedAssetThumb.d.ts +15 -0
- package/presentation/fieldRenderers/CroppedAssetThumb.js +25 -0
- package/presentation/fieldRenderers/CroppedAssetThumb.js.map +1 -0
- package/presentation/fieldRenderers/assetValue.d.ts +10 -0
- package/presentation/fieldRenderers/assetValue.js +88 -0
- package/presentation/fieldRenderers/assetValue.js.map +1 -0
- package/presentation/hooks/useCmsImageEditor.d.ts +10 -0
- package/presentation/hooks/useCmsImageEditor.js +76 -0
- package/presentation/hooks/useCmsImageEditor.js.map +1 -0
- package/presentation/hooks/useEditImage.d.ts +14 -0
- package/presentation/hooks/useEditImage.js +67 -0
- package/presentation/hooks/useEditImage.js.map +1 -0
- package/routes.d.ts +1 -0
- package/routes.js +2 -1
- package/routes.js.map +1 -1
- package/modules/HeadlessCms/fileField.d.ts +0 -2
- package/modules/HeadlessCms/fileField.js +0 -44
- package/modules/HeadlessCms/fileField.js.map +0 -1
- package/modules/HeadlessCms/fileRenderer/fileField.d.ts +0 -2
- package/modules/HeadlessCms/fileRenderer/fileField.js +0 -53
- package/modules/HeadlessCms/fileRenderer/fileField.js.map +0 -1
- package/modules/HeadlessCms/fileRenderer/fileFields.d.ts +0 -2
- package/modules/HeadlessCms/fileRenderer/fileFields.js +0 -73
- package/modules/HeadlessCms/fileRenderer/fileFields.js.map +0 -1
- package/modules/HeadlessCms/fileRenderer/utils.d.ts +0 -2
- package/modules/HeadlessCms/fileRenderer/utils.js +0 -15
- package/modules/HeadlessCms/fileRenderer/utils.js.map +0 -1
- package/modules/HeadlessCms/index.d.ts +0 -1
- package/modules/HeadlessCms/index.js +0 -14
- package/modules/HeadlessCms/index.js.map +0 -1
- package/modules/Settings/graphql.d.ts +0 -12
- package/modules/Settings/graphql.js +0 -36
- package/modules/Settings/graphql.js.map +0 -1
- package/modules/Settings/views/FileManagerSettings.d.ts +0 -2
- package/modules/Settings/views/FileManagerSettings.js +0 -134
- package/modules/Settings/views/FileManagerSettings.js.map +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/AddOperation.d.ts +0 -7
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/AddOperation.js +0 -15
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/AddOperation.js.map +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditor.d.ts +0 -15
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditor.js +0 -44
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditor.js.map +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialog.d.ts +0 -15
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialog.js +0 -52
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialog.js.map +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.d.ts +0 -61
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.js +0 -111
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.js.map +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.test.d.ts +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.test.js +0 -372
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.test.js.map +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/FieldRenderer.d.ts +0 -8
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/FieldRenderer.js +0 -31
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/FieldRenderer.js.map +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/Operation.d.ts +0 -11
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/Operation.js +0 -33
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/Operation.js.map +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/index.d.ts +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/index.js +0 -1
- package/presentation/FileList/components/BulkActions/BulkActionEdit.d.ts +0 -4
- package/presentation/FileList/components/BulkActions/BulkActionEdit.js +0 -105
- package/presentation/FileList/components/BulkActions/BulkActionEdit.js.map +0 -1
- package/presentation/config/configComponents/Browser/FolderFieldDecorator.d.ts +0 -7
- package/presentation/config/configComponents/Browser/FolderFieldDecorator.js +0 -11
- package/presentation/config/configComponents/Browser/FolderFieldDecorator.js.map +0 -1
package/app.js
CHANGED
|
@@ -4,8 +4,8 @@ import { FolderTreeFieldRenderer } from "@webiny/app-aco/presentation/folderTree
|
|
|
4
4
|
import { SettingsModule } from "./modules/Settings/index.js";
|
|
5
5
|
import { FileModelModule } from "./modules/FileModelModule.js";
|
|
6
6
|
import { DefaultFileManagerConfig } from "./presentation/config/DefaultFileManagerConfig.js";
|
|
7
|
-
import { HeadlessCmsModule } from "./modules/HeadlessCms/index.js";
|
|
8
7
|
import { EnterpriseModule } from "./modules/Enterprise/index.js";
|
|
8
|
+
import { AiEnrichmentModule } from "./modules/AiEnrichment/index.js";
|
|
9
9
|
import { SecurityPermissions } from "./modules/SecurityPermissions.js";
|
|
10
10
|
import { FileUrlFormatterModule } from "./modules/FileUrlFormatter.js";
|
|
11
11
|
import { FmPermissionsFeature } from "./features/permissions/feature.js";
|
|
@@ -22,6 +22,11 @@ import { FileManagerPresenterFeature } from "./presentation/FileList/index.js";
|
|
|
22
22
|
import { FileManagerRoutes } from "./modules/FileManagerRoutes.js";
|
|
23
23
|
import { FileManagerRendererDecorator } from "./presentation/FileManager/FileManagerRenderer.js";
|
|
24
24
|
import { FileModelProviderFeature } from "./features/fileModel/index.js";
|
|
25
|
+
import { FileFieldTypeFeature } from "./modules/HeadlessCms/fieldType/feature.js";
|
|
26
|
+
import { CmsFilePickerRenderer } from "./presentation/fieldRenderers/CmsFilePickerRenderer.js";
|
|
27
|
+
import { CmsMultiFilePickerRenderer } from "./presentation/fieldRenderers/CmsMultiFilePickerRenderer.js";
|
|
28
|
+
import { CmsAssetPickerRenderer } from "./presentation/fieldRenderers/CmsAssetPickerRenderer.js";
|
|
29
|
+
import { CmsMultiAssetPickerRenderer } from "./presentation/fieldRenderers/CmsMultiAssetPickerRenderer.js";
|
|
25
30
|
const FileManager = ()=>/*#__PURE__*/ react.createElement(react.Fragment, null, /*#__PURE__*/ react.createElement(RegisterFeature, {
|
|
26
31
|
feature: FileModelProviderFeature
|
|
27
32
|
}), /*#__PURE__*/ react.createElement(RegisterFeature, {
|
|
@@ -51,7 +56,21 @@ const FileManager = ()=>/*#__PURE__*/ react.createElement(react.Fragment, null,
|
|
|
51
56
|
}), /*#__PURE__*/ react.createElement(AdminConfig, null, /*#__PURE__*/ react.createElement(AdminConfig.Form.FieldRenderer, {
|
|
52
57
|
name: "folderTree",
|
|
53
58
|
component: FolderTreeFieldRenderer
|
|
54
|
-
})
|
|
59
|
+
}), /*#__PURE__*/ react.createElement(AdminConfig.Form.FieldRenderer, {
|
|
60
|
+
name: "cmsFilePicker",
|
|
61
|
+
component: CmsFilePickerRenderer
|
|
62
|
+
}), /*#__PURE__*/ react.createElement(AdminConfig.Form.FieldRenderer, {
|
|
63
|
+
name: "cmsMultiFilePicker",
|
|
64
|
+
component: CmsMultiFilePickerRenderer
|
|
65
|
+
}), /*#__PURE__*/ react.createElement(AdminConfig.Form.FieldRenderer, {
|
|
66
|
+
name: "cmsAssetPicker",
|
|
67
|
+
component: CmsAssetPickerRenderer
|
|
68
|
+
}), /*#__PURE__*/ react.createElement(AdminConfig.Form.FieldRenderer, {
|
|
69
|
+
name: "cmsMultiAssetPicker",
|
|
70
|
+
component: CmsMultiAssetPickerRenderer
|
|
71
|
+
})), /*#__PURE__*/ react.createElement(FileManagerRoutes, null), /*#__PURE__*/ react.createElement(SettingsModule, null), /*#__PURE__*/ react.createElement(FileManagerRendererDecorator, null), /*#__PURE__*/ react.createElement(DefaultFileManagerConfig, null), /*#__PURE__*/ react.createElement(RegisterFeature, {
|
|
72
|
+
feature: FileFieldTypeFeature
|
|
73
|
+
}), /*#__PURE__*/ react.createElement(EnterpriseModule, null), /*#__PURE__*/ react.createElement(AiEnrichmentModule, null), /*#__PURE__*/ react.createElement(SecurityPermissions, null), /*#__PURE__*/ react.createElement(FileUrlFormatterModule, null));
|
|
55
74
|
export { FileManager };
|
|
56
75
|
|
|
57
76
|
//# sourceMappingURL=app.js.map
|
package/app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","sources":["../src/app.tsx"],"sourcesContent":["import React from \"react\";\nimport { AdminConfig, RegisterFeature } from \"@webiny/app-admin\";\nimport { FolderTreeFieldRenderer } from \"@webiny/app-aco/presentation/folderTree/FolderTreeFieldRenderer.js\";\nimport { SettingsModule } from \"~/modules/Settings/index.js\";\nimport { FileModelModule } from \"~/modules/FileModelModule.js\";\nimport { DefaultFileManagerConfig } from \"~/presentation/config/DefaultFileManagerConfig.js\";\nimport {
|
|
1
|
+
{"version":3,"file":"app.js","sources":["../src/app.tsx"],"sourcesContent":["import React from \"react\";\nimport { AdminConfig, RegisterFeature } from \"@webiny/app-admin\";\nimport { FolderTreeFieldRenderer } from \"@webiny/app-aco/presentation/folderTree/FolderTreeFieldRenderer.js\";\nimport { SettingsModule } from \"~/modules/Settings/index.js\";\nimport { FileModelModule } from \"~/modules/FileModelModule.js\";\nimport { DefaultFileManagerConfig } from \"~/presentation/config/DefaultFileManagerConfig.js\";\nimport { EnterpriseModule } from \"~/modules/Enterprise/index.js\";\nimport { AiEnrichmentModule } from \"~/modules/AiEnrichment/index.js\";\nimport { SecurityPermissions } from \"./modules/SecurityPermissions.js\";\nimport { FileUrlFormatterModule } from \"./modules/FileUrlFormatter.js\";\nimport { FmPermissionsFeature } from \"~/features/permissions/feature.js\";\nimport { GetFileFeature } from \"~/features/getFile/feature.js\";\nimport { ResolveImageToolFeature } from \"~/features/resolveImageTool/feature.js\";\nimport { ListFilesFeature } from \"~/features/listFiles/index.js\";\nimport { UpdateFileFeature } from \"~/features/updateFile/index.js\";\nimport { DeleteFileFeature } from \"~/features/deleteFile/index.js\";\nimport { FileUploaderFeature } from \"~/features/fileUploader/index.js\";\nimport { ListTagsFeature } from \"~/features/tags/index.js\";\nimport { GetSettingsFeature } from \"~/features/settings/index.js\";\nimport { SharedCacheFeature } from \"~/features/shared/index.js\";\nimport { FileManagerPresenterFeature } from \"~/presentation/FileList/index.js\";\nimport { FileManagerRoutes } from \"~/modules/FileManagerRoutes.js\";\nimport { FileManagerRendererDecorator } from \"~/presentation/FileManager/FileManagerRenderer.js\";\nimport { FileModelProviderFeature } from \"~/features/fileModel/index.js\";\nimport { FileFieldTypeFeature } from \"~/modules/HeadlessCms/fieldType/feature.js\";\nimport { CmsFilePickerRenderer } from \"~/presentation/fieldRenderers/CmsFilePickerRenderer.js\";\nimport { CmsMultiFilePickerRenderer } from \"~/presentation/fieldRenderers/CmsMultiFilePickerRenderer.js\";\nimport { CmsAssetPickerRenderer } from \"~/presentation/fieldRenderers/CmsAssetPickerRenderer.js\";\nimport { CmsMultiAssetPickerRenderer } from \"~/presentation/fieldRenderers/CmsMultiAssetPickerRenderer.js\";\n\nexport const FileManager = () => {\n return (\n <>\n {/* Headless features. */}\n <RegisterFeature feature={FileModelProviderFeature} />\n <RegisterFeature feature={SharedCacheFeature} />\n <RegisterFeature feature={ListFilesFeature} />\n <RegisterFeature feature={GetFileFeature} />\n <RegisterFeature feature={UpdateFileFeature} />\n <RegisterFeature feature={DeleteFileFeature} />\n <RegisterFeature feature={FileUploaderFeature} />\n <RegisterFeature feature={ListTagsFeature} />\n <RegisterFeature feature={GetSettingsFeature} />\n <RegisterFeature feature={FmPermissionsFeature} />\n <RegisterFeature feature={GetFileFeature} />\n <RegisterFeature feature={ResolveImageToolFeature} />\n <FileModelModule />\n {/* Presentation features. */}\n <RegisterFeature feature={FileManagerPresenterFeature} />\n {/* Field renderers. */}\n <AdminConfig>\n <AdminConfig.Form.FieldRenderer\n name={\"folderTree\"}\n component={FolderTreeFieldRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"cmsFilePicker\"}\n component={CmsFilePickerRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"cmsMultiFilePicker\"}\n component={CmsMultiFilePickerRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"cmsAssetPicker\"}\n component={CmsAssetPickerRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"cmsMultiAssetPicker\"}\n component={CmsMultiAssetPickerRenderer}\n />\n </AdminConfig>\n {/* Legacy modules. */}\n <FileManagerRoutes />\n <SettingsModule />\n <FileManagerRendererDecorator />\n <DefaultFileManagerConfig />\n <RegisterFeature feature={FileFieldTypeFeature} />\n <EnterpriseModule />\n <AiEnrichmentModule />\n <SecurityPermissions />\n <FileUrlFormatterModule />\n </>\n );\n};\n"],"names":["FileManager","RegisterFeature","FileModelProviderFeature","SharedCacheFeature","ListFilesFeature","GetFileFeature","UpdateFileFeature","DeleteFileFeature","FileUploaderFeature","ListTagsFeature","GetSettingsFeature","FmPermissionsFeature","ResolveImageToolFeature","FileModelModule","FileManagerPresenterFeature","AdminConfig","FolderTreeFieldRenderer","CmsFilePickerRenderer","CmsMultiFilePickerRenderer","CmsAssetPickerRenderer","CmsMultiAssetPickerRenderer","FileManagerRoutes","SettingsModule","FileManagerRendererDecorator","DefaultFileManagerConfig","FileFieldTypeFeature","EnterpriseModule","AiEnrichmentModule","SecurityPermissions","FileUrlFormatterModule"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BO,MAAMA,cAAc,IAChB,WAAP,GACI,wDAEI,oBAACC,iBAAeA;QAAC,SAASC;sBAC1B,oBAACD,iBAAeA;QAAC,SAASE;sBAC1B,oBAACF,iBAAeA;QAAC,SAASG;sBAC1B,oBAACH,iBAAeA;QAAC,SAASI;sBAC1B,oBAACJ,iBAAeA;QAAC,SAASK;sBAC1B,oBAACL,iBAAeA;QAAC,SAASM;sBAC1B,oBAACN,iBAAeA;QAAC,SAASO;sBAC1B,oBAACP,iBAAeA;QAAC,SAASQ;sBAC1B,oBAACR,iBAAeA;QAAC,SAASS;sBAC1B,oBAACT,iBAAeA;QAAC,SAASU;sBAC1B,oBAACV,iBAAeA;QAAC,SAASI;sBAC1B,oBAACJ,iBAAeA;QAAC,SAASW;sBAC1B,oBAACC,iBAAeA,OAAAA,WAAAA,GAEhB,oBAACZ,iBAAeA;QAAC,SAASa;sBAE1B,oBAACC,aAAWA,MAAAA,WAAAA,GACR,oBAACA,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWC;sBAEf,oBAACD,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWE;sBAEf,oBAACF,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWG;sBAEf,oBAACH,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWI;sBAEf,oBAACJ,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWK;uBAInB,oBAACC,mBAAiBA,OAAAA,WAAAA,GAClB,oBAACC,gBAAcA,OAAAA,WAAAA,GACf,oBAACC,8BAA4BA,OAAAA,WAAAA,GAC7B,oBAACC,0BAAwBA,OAAAA,WAAAA,GACzB,oBAACvB,iBAAeA;QAAC,SAASwB;sBAC1B,oBAACC,kBAAgBA,OAAAA,WAAAA,GACjB,oBAACC,oBAAkBA,OAAAA,WAAAA,GACnB,oBAACC,qBAAmBA,OAAAA,WAAAA,GACpB,oBAACC,wBAAsBA"}
|
package/domain/tagsHelpers.d.ts
CHANGED
|
@@ -4,11 +4,11 @@ interface GetTagsInitialParams {
|
|
|
4
4
|
own?: boolean;
|
|
5
5
|
}
|
|
6
6
|
export declare const getTagsInitialParams: ({ scope, own }: GetTagsInitialParams) => {
|
|
7
|
-
tags_startsWith: string;
|
|
8
7
|
createdBy?: string | undefined;
|
|
8
|
+
tags_startsWith: string;
|
|
9
9
|
} | {
|
|
10
|
-
tags_not_startsWith: string;
|
|
11
10
|
createdBy?: string | undefined;
|
|
11
|
+
tags_not_startsWith: string;
|
|
12
12
|
};
|
|
13
13
|
export declare const tagsModifier: (scope?: string) => (tags: TagItem[]) => {
|
|
14
14
|
tag: string;
|
package/domain/types.d.ts
CHANGED
|
@@ -18,14 +18,6 @@ export interface Settings {
|
|
|
18
18
|
uploadMaxFileSize: string;
|
|
19
19
|
srcPrefix: string;
|
|
20
20
|
}
|
|
21
|
-
export interface QueryGetSettingsResult {
|
|
22
|
-
fileManager: {
|
|
23
|
-
getSettings: {
|
|
24
|
-
data: Settings;
|
|
25
|
-
error: Error | null;
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
21
|
export interface FileTag {
|
|
30
22
|
tag: string;
|
|
31
23
|
count: number;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Permissions } from "@webiny/app-admin/exports/admin/security.js";
|
|
2
2
|
import { FM_PERMISSIONS_SCHEMA } from "../../domain/permissionsSchema.js";
|
|
3
3
|
export declare const FileManagerPermissions: import("@webiny/di").Abstraction<{
|
|
4
|
-
canAccess: (entityId: "
|
|
5
|
-
canAction: (action: string, entityId: "
|
|
4
|
+
canAccess: (entityId: "file" | "settings") => boolean;
|
|
5
|
+
canAction: (action: string, entityId: "file" | "settings") => boolean;
|
|
6
6
|
} & {
|
|
7
7
|
canRead: (entityId: "file") => boolean;
|
|
8
8
|
canCreate: (entityId: "file") => boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export declare const FmPermissionsFeature: import("@webiny/feature/admin").FeatureDefinition<{
|
|
1
|
+
export declare const FmPermissionsFeature: import("@webiny/feature/admin/createFeature.js").FeatureDefinition<{
|
|
2
2
|
permissions: {
|
|
3
|
-
canAccess: (entityId: "
|
|
4
|
-
canAction: (action: string, entityId: "
|
|
3
|
+
canAccess: (entityId: "file" | "settings") => boolean;
|
|
4
|
+
canAction: (action: string, entityId: "file" | "settings") => boolean;
|
|
5
5
|
} & {
|
|
6
6
|
canRead: (entityId: "file") => boolean;
|
|
7
7
|
canCreate: (entityId: "file") => boolean;
|
|
@@ -24,6 +24,10 @@ const FILE_FIELDS = [
|
|
|
24
24
|
"metadata.image.height",
|
|
25
25
|
"metadata.image.format",
|
|
26
26
|
"metadata.image.orientation",
|
|
27
|
+
"metadata.image.crop",
|
|
28
|
+
"metadata.image.focalPoint",
|
|
29
|
+
"metadata.image.alt",
|
|
30
|
+
"metadata.image.caption",
|
|
27
31
|
"metadata.exif",
|
|
28
32
|
"metadata.iptc"
|
|
29
33
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/shared/FILE_FIELDS.js","sources":["../../../src/features/shared/FILE_FIELDS.ts"],"sourcesContent":["export const FILE_FIELDS = [\n \"id\",\n \"name\",\n \"description\",\n \"key\",\n \"src\",\n \"type\",\n \"size\",\n \"tags\",\n \"createdOn\",\n \"savedOn\",\n \"modifiedOn\",\n \"createdBy.id\",\n \"createdBy.displayName\",\n \"createdBy.type\",\n \"savedBy.id\",\n \"savedBy.displayName\",\n \"savedBy.type\",\n \"modifiedBy.id\",\n \"modifiedBy.displayName\",\n \"modifiedBy.type\",\n \"location.folderId\",\n \"metadata.image.width\",\n \"metadata.image.height\",\n \"metadata.image.format\",\n \"metadata.image.orientation\",\n \"metadata.exif\",\n \"metadata.iptc\"\n];\n"],"names":["FILE_FIELDS"],"mappings":"AAAO,MAAMA,cAAc;IACvB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACH"}
|
|
1
|
+
{"version":3,"file":"features/shared/FILE_FIELDS.js","sources":["../../../src/features/shared/FILE_FIELDS.ts"],"sourcesContent":["export const FILE_FIELDS = [\n \"id\",\n \"name\",\n \"description\",\n \"key\",\n \"src\",\n \"type\",\n \"size\",\n \"tags\",\n \"createdOn\",\n \"savedOn\",\n \"modifiedOn\",\n \"createdBy.id\",\n \"createdBy.displayName\",\n \"createdBy.type\",\n \"savedBy.id\",\n \"savedBy.displayName\",\n \"savedBy.type\",\n \"modifiedBy.id\",\n \"modifiedBy.displayName\",\n \"modifiedBy.type\",\n \"location.folderId\",\n \"metadata.image.width\",\n \"metadata.image.height\",\n \"metadata.image.format\",\n \"metadata.image.orientation\",\n \"metadata.image.crop\",\n \"metadata.image.focalPoint\",\n \"metadata.image.alt\",\n \"metadata.image.caption\",\n \"metadata.exif\",\n \"metadata.iptc\"\n];\n"],"names":["FILE_FIELDS"],"mappings":"AAAO,MAAMA,cAAc;IACvB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACH"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FeatureFlagsService } from "@webiny/app-admin/features/featureFlags/abstractions.js";
|
|
2
2
|
import { FileFieldsProvider as Abstraction } from "./abstractions.js";
|
|
3
|
-
declare class
|
|
4
|
-
private
|
|
3
|
+
declare class FileFieldsProviderWithFeatureFlagsImpl implements Abstraction.Interface {
|
|
4
|
+
private featureFlagsService;
|
|
5
5
|
private decoratee;
|
|
6
|
-
constructor(
|
|
6
|
+
constructor(featureFlagsService: FeatureFlagsService.Interface, decoratee: Abstraction.Interface);
|
|
7
7
|
execute(): Promise<string[]>;
|
|
8
8
|
}
|
|
9
|
-
export declare const FileFieldsProviderWithWcp: typeof
|
|
9
|
+
export declare const FileFieldsProviderWithWcp: typeof FileFieldsProviderWithFeatureFlagsImpl & {
|
|
10
10
|
__abstraction: import("@webiny/di").Abstraction<import("./abstractions.js").IFileFieldsProvider>;
|
|
11
11
|
};
|
|
12
12
|
export {};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FeatureFlagsService } from "@webiny/app-admin/features/featureFlags/abstractions.js";
|
|
2
2
|
import { FileFieldsProvider } from "./abstractions.js";
|
|
3
|
-
class
|
|
4
|
-
constructor(
|
|
5
|
-
this.
|
|
3
|
+
class FileFieldsProviderWithFeatureFlagsImpl {
|
|
4
|
+
constructor(featureFlagsService, decoratee){
|
|
5
|
+
this.featureFlagsService = featureFlagsService;
|
|
6
6
|
this.decoratee = decoratee;
|
|
7
7
|
}
|
|
8
8
|
async execute() {
|
|
9
9
|
const fields = await this.decoratee.execute();
|
|
10
|
-
if (this.
|
|
10
|
+
if (this.featureFlagsService.getFlags().isEnabled("advancedAccessControlLayer.privateFiles")) return [
|
|
11
11
|
...fields,
|
|
12
12
|
"accessControl.type"
|
|
13
13
|
];
|
|
@@ -15,9 +15,9 @@ class FileFieldsProviderWithWcpImpl {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
const FileFieldsProviderWithWcp = FileFieldsProvider.createDecorator({
|
|
18
|
-
decorator:
|
|
18
|
+
decorator: FileFieldsProviderWithFeatureFlagsImpl,
|
|
19
19
|
dependencies: [
|
|
20
|
-
|
|
20
|
+
FeatureFlagsService
|
|
21
21
|
]
|
|
22
22
|
});
|
|
23
23
|
export { FileFieldsProviderWithWcp };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/shared/FileFieldsProviderWithWcp.js","sources":["../../../src/features/shared/FileFieldsProviderWithWcp.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"features/shared/FileFieldsProviderWithWcp.js","sources":["../../../src/features/shared/FileFieldsProviderWithWcp.ts"],"sourcesContent":["import { FeatureFlagsService } from \"@webiny/app-admin/features/featureFlags/abstractions.js\";\nimport { FileFieldsProvider as Abstraction } from \"./abstractions.js\";\n\nclass FileFieldsProviderWithFeatureFlagsImpl implements Abstraction.Interface {\n constructor(\n private featureFlagsService: FeatureFlagsService.Interface,\n private decoratee: Abstraction.Interface\n ) {}\n\n async execute(): Promise<string[]> {\n const fields = await this.decoratee.execute();\n\n if (\n this.featureFlagsService.getFlags().isEnabled(\"advancedAccessControlLayer.privateFiles\")\n ) {\n return [...fields, \"accessControl.type\"];\n }\n\n return fields;\n }\n}\n\nexport const FileFieldsProviderWithWcp = Abstraction.createDecorator({\n decorator: FileFieldsProviderWithFeatureFlagsImpl,\n dependencies: [FeatureFlagsService]\n});\n"],"names":["FileFieldsProviderWithFeatureFlagsImpl","featureFlagsService","decoratee","fields","FileFieldsProviderWithWcp","Abstraction","FeatureFlagsService"],"mappings":";;AAGA,MAAMA;IACF,YACYC,mBAAkD,EAClDC,SAAgC,CAC1C;aAFUD,mBAAmB,GAAnBA;aACAC,SAAS,GAATA;IACT;IAEH,MAAM,UAA6B;QAC/B,MAAMC,SAAS,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO;QAE3C,IACI,IAAI,CAAC,mBAAmB,CAAC,QAAQ,GAAG,SAAS,CAAC,4CAE9C,OAAO;eAAIA;YAAQ;SAAqB;QAG5C,OAAOA;IACX;AACJ;AAEO,MAAMC,4BAA4BC,mBAAAA,eAA2B,CAAC;IACjE,WAAWL;IACX,cAAc;QAACM;KAAoB;AACvC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { WebsocketEventHandler } from "@webiny/app-websockets";
|
|
2
|
+
import { Notifications } from "@webiny/app-admin/features/notifications/abstractions.js";
|
|
3
|
+
import { FilesListCache } from "../../features/shared/abstractions.js";
|
|
4
|
+
/**
|
|
5
|
+
* Reacts to the `fm.file.enrichment` websocket message (published as a `WebsocketEvent`):
|
|
6
|
+
* patches the affected file in the shared list cache with the AI-generated tags and description
|
|
7
|
+
* (cache-only — the data is already persisted server-side), then surfaces a success notification.
|
|
8
|
+
*/
|
|
9
|
+
declare class AiImageEnrichmentEventHandlerImpl implements WebsocketEventHandler.Interface {
|
|
10
|
+
private filesListCache;
|
|
11
|
+
private notifications;
|
|
12
|
+
constructor(filesListCache: FilesListCache.Interface, notifications: Notifications.Interface);
|
|
13
|
+
handle(event: WebsocketEventHandler.Event): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export declare const AiImageEnrichmentEventHandler: typeof AiImageEnrichmentEventHandlerImpl & {
|
|
16
|
+
__abstraction: import("@webiny/di").Abstraction<import("@webiny/app/features/eventPublisher").IEventHandler<import("@webiny/app-websockets").WebsocketEvent>>;
|
|
17
|
+
};
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { WebsocketEventHandler } from "@webiny/app-websockets";
|
|
2
|
+
import { Notifications } from "@webiny/app-admin/features/notifications/abstractions.js";
|
|
3
|
+
import { FilesListCache } from "../../features/shared/abstractions.js";
|
|
4
|
+
const FILE_ENRICHMENT_ACTION = "fm.file.enrichment";
|
|
5
|
+
class AiImageEnrichmentEventHandlerImpl {
|
|
6
|
+
constructor(filesListCache, notifications){
|
|
7
|
+
this.filesListCache = filesListCache;
|
|
8
|
+
this.notifications = notifications;
|
|
9
|
+
}
|
|
10
|
+
async handle(event) {
|
|
11
|
+
if (event.payload.action !== FILE_ENRICHMENT_ACTION) return;
|
|
12
|
+
const { id, tags, description } = event.payload.data;
|
|
13
|
+
this.filesListCache.updateItems((item)=>item.id === id ? {
|
|
14
|
+
...item,
|
|
15
|
+
tags,
|
|
16
|
+
description
|
|
17
|
+
} : item);
|
|
18
|
+
this.notifications.success({
|
|
19
|
+
title: "Image enriched",
|
|
20
|
+
description: "AI-generated tags and description have been added."
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const AiImageEnrichmentEventHandler = WebsocketEventHandler.createImplementation({
|
|
25
|
+
implementation: AiImageEnrichmentEventHandlerImpl,
|
|
26
|
+
dependencies: [
|
|
27
|
+
FilesListCache,
|
|
28
|
+
Notifications
|
|
29
|
+
]
|
|
30
|
+
});
|
|
31
|
+
export { AiImageEnrichmentEventHandler };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=AiImageEnrichmentEventHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modules/AiEnrichment/AiImageEnrichmentEventHandler.js","sources":["../../../src/modules/AiEnrichment/AiImageEnrichmentEventHandler.ts"],"sourcesContent":["import { WebsocketEventHandler } from \"@webiny/app-websockets\";\nimport { Notifications } from \"@webiny/app-admin/features/notifications/abstractions.js\";\nimport { FilesListCache } from \"~/features/shared/abstractions.js\";\n\nconst FILE_ENRICHMENT_ACTION = \"fm.file.enrichment\";\n\ninterface FileEnrichmentData {\n id: string;\n tags: string[];\n description: string;\n}\n\n/**\n * Reacts to the `fm.file.enrichment` websocket message (published as a `WebsocketEvent`):\n * patches the affected file in the shared list cache with the AI-generated tags and description\n * (cache-only — the data is already persisted server-side), then surfaces a success notification.\n */\nclass AiImageEnrichmentEventHandlerImpl implements WebsocketEventHandler.Interface {\n constructor(\n private filesListCache: FilesListCache.Interface,\n private notifications: Notifications.Interface\n ) {}\n\n async handle(event: WebsocketEventHandler.Event): Promise<void> {\n if (event.payload.action !== FILE_ENRICHMENT_ACTION) {\n return;\n }\n\n // Payload envelope is `{ action, data: { ... } }` (see the api-side sender + the threat-scan\n // handler which reads message.data). The enrichment fields live under `.data`, not top-level.\n const { id, tags, description } = (event.payload as unknown as { data: FileEnrichmentData })\n .data;\n\n this.filesListCache.updateItems(item =>\n item.id === id ? { ...item, tags, description } : item\n );\n\n this.notifications.success({\n title: \"Image enriched\",\n description: \"AI-generated tags and description have been added.\"\n });\n }\n}\n\nexport const AiImageEnrichmentEventHandler = WebsocketEventHandler.createImplementation({\n implementation: AiImageEnrichmentEventHandlerImpl,\n dependencies: [FilesListCache, Notifications]\n});\n"],"names":["FILE_ENRICHMENT_ACTION","AiImageEnrichmentEventHandlerImpl","filesListCache","notifications","event","id","tags","description","item","AiImageEnrichmentEventHandler","WebsocketEventHandler","FilesListCache","Notifications"],"mappings":";;;AAIA,MAAMA,yBAAyB;AAa/B,MAAMC;IACF,YACYC,cAAwC,EACxCC,aAAsC,CAChD;aAFUD,cAAc,GAAdA;aACAC,aAAa,GAAbA;IACT;IAEH,MAAM,OAAOC,KAAkC,EAAiB;QAC5D,IAAIA,MAAM,OAAO,CAAC,MAAM,KAAKJ,wBACzB;QAKJ,MAAM,EAAEK,EAAE,EAAEC,IAAI,EAAEC,WAAW,EAAE,GAAIH,MAAM,OAAO,CAC3C,IAAI;QAET,IAAI,CAAC,cAAc,CAAC,WAAW,CAACI,CAAAA,OAC5BA,KAAK,EAAE,KAAKH,KAAK;gBAAE,GAAGG,IAAI;gBAAEF;gBAAMC;YAAY,IAAIC;QAGtD,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC;YACvB,OAAO;YACP,aAAa;QACjB;IACJ;AACJ;AAEO,MAAMC,gCAAgCC,sBAAsB,oBAAoB,CAAC;IACpF,gBAAgBT;IAChB,cAAc;QAACU;QAAgBC;KAAc;AACjD"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ApiStreamClient } from "@webiny/app/exports/admin.js";
|
|
2
|
+
import { ReenrichFileGateway as GatewayAbstraction } from "./abstractions.js";
|
|
3
|
+
import type { EnrichmentStreamEvent, IReenrichFileOptions } from "./abstractions.js";
|
|
4
|
+
declare class ReenrichFileGatewayImpl implements GatewayAbstraction.Interface {
|
|
5
|
+
private client;
|
|
6
|
+
constructor(client: ApiStreamClient.Interface);
|
|
7
|
+
execute(fileId: string, options?: IReenrichFileOptions): AsyncGenerator<EnrichmentStreamEvent>;
|
|
8
|
+
}
|
|
9
|
+
export declare const ReenrichFileGateway: typeof ReenrichFileGatewayImpl & {
|
|
10
|
+
__abstraction: import("@webiny/di").Abstraction<import("./abstractions.js").IReenrichFileGateway>;
|
|
11
|
+
};
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ApiStreamClient, readServerSentEvents } from "@webiny/app/exports/admin.js";
|
|
2
|
+
import { ReenrichFileGateway } from "./abstractions.js";
|
|
3
|
+
class ReenrichFileGatewayImpl {
|
|
4
|
+
constructor(client){
|
|
5
|
+
this.client = client;
|
|
6
|
+
}
|
|
7
|
+
async *execute(fileId, options = {}) {
|
|
8
|
+
const response = await this.client.execute({
|
|
9
|
+
path: `/stream/fm/files/${encodeURIComponent(fileId)}/enrich`,
|
|
10
|
+
signal: options.signal
|
|
11
|
+
});
|
|
12
|
+
yield* readServerSentEvents(response);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const ReenrichFileGateway_ReenrichFileGateway = ReenrichFileGateway.createImplementation({
|
|
16
|
+
implementation: ReenrichFileGatewayImpl,
|
|
17
|
+
dependencies: [
|
|
18
|
+
ApiStreamClient
|
|
19
|
+
]
|
|
20
|
+
});
|
|
21
|
+
export { ReenrichFileGateway_ReenrichFileGateway as ReenrichFileGateway };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=ReenrichFileGateway.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modules/AiEnrichment/ReenrichFileGateway.js","sources":["../../../src/modules/AiEnrichment/ReenrichFileGateway.ts"],"sourcesContent":["import { ApiStreamClient, readServerSentEvents } from \"@webiny/app/exports/admin.js\";\nimport { ReenrichFileGateway as GatewayAbstraction } from \"./abstractions.js\";\nimport type { EnrichmentStreamEvent, IReenrichFileOptions } from \"./abstractions.js\";\n\nclass ReenrichFileGatewayImpl implements GatewayAbstraction.Interface {\n constructor(private client: ApiStreamClient.Interface) {}\n\n async *execute(\n fileId: string,\n options: IReenrichFileOptions = {}\n ): AsyncGenerator<EnrichmentStreamEvent> {\n const response = await this.client.execute({\n path: `/stream/fm/files/${encodeURIComponent(fileId)}/enrich`,\n signal: options.signal\n });\n\n yield* readServerSentEvents<EnrichmentStreamEvent>(response);\n }\n}\n\nexport const ReenrichFileGateway = GatewayAbstraction.createImplementation({\n implementation: ReenrichFileGatewayImpl,\n dependencies: [ApiStreamClient]\n});\n"],"names":["ReenrichFileGatewayImpl","client","fileId","options","response","encodeURIComponent","readServerSentEvents","ReenrichFileGateway","GatewayAbstraction","ApiStreamClient"],"mappings":";;AAIA,MAAMA;IACF,YAAoBC,MAAiC,CAAE;aAAnCA,MAAM,GAANA;IAAoC;IAExD,OAAO,QACHC,MAAc,EACdC,UAAgC,CAAC,CAAC,EACG;QACrC,MAAMC,WAAW,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;YACvC,MAAM,CAAC,iBAAiB,EAAEC,mBAAmBH,QAAQ,OAAO,CAAC;YAC7D,QAAQC,QAAQ,MAAM;QAC1B;QAEA,OAAOG,qBAA4CF;IACvD;AACJ;AAEO,MAAMG,0CAAsBC,oBAAAA,oBAAuC,CAAC;IACvE,gBAAgBR;IAChB,cAAc;QAACS;KAAgB;AACnC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Test-bed for HTTP response streaming: re-runs AI enrichment for the open file and renders the
|
|
4
|
+
* model's output as it arrives, rather than waiting for the whole response.
|
|
5
|
+
*
|
|
6
|
+
* The stream only PROPOSES. Save drops the values into the file details form as pending edits; the
|
|
7
|
+
* file is written when the user presses Update in the drawer, exactly as for a hand-typed edit.
|
|
8
|
+
*/
|
|
9
|
+
export declare const ReenrichWithAi: (() => React.JSX.Element) & React.NamedExoticComponent<unknown> & {
|
|
10
|
+
readonly type: () => React.JSX.Element;
|
|
11
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import react, { useEffect } from "react";
|
|
2
|
+
import { ReactComponent } from "@webiny/icons/auto_awesome.svg";
|
|
3
|
+
import { Dialog } from "@webiny/admin-ui";
|
|
4
|
+
import { createReactiveComponent } from "@webiny/app-admin";
|
|
5
|
+
import { useFeature } from "@webiny/app";
|
|
6
|
+
import { FileManagerViewConfig, useFile } from "../../index.js";
|
|
7
|
+
import { AiEnrichmentFeature } from "./feature.js";
|
|
8
|
+
import { StreamingPlaceholder } from "./StreamingPlaceholder.js";
|
|
9
|
+
const { FileDetails: FileDetails } = FileManagerViewConfig;
|
|
10
|
+
const ReenrichWithAi_ReenrichWithAi = createReactiveComponent(function() {
|
|
11
|
+
const { file } = useFile();
|
|
12
|
+
const { presenter } = useFeature(AiEnrichmentFeature);
|
|
13
|
+
const { vm } = presenter;
|
|
14
|
+
useEffect(()=>()=>presenter.dispose(), [
|
|
15
|
+
presenter
|
|
16
|
+
]);
|
|
17
|
+
return /*#__PURE__*/ react.createElement(react.Fragment, null, /*#__PURE__*/ react.createElement(FileDetails.Action.Button, {
|
|
18
|
+
label: "Re-enrich with AI",
|
|
19
|
+
icon: /*#__PURE__*/ react.createElement(ReactComponent, null),
|
|
20
|
+
onAction: ()=>presenter.start(file.id),
|
|
21
|
+
"data-testid": "fm.file-details.action.reenrich-with-ai"
|
|
22
|
+
}), /*#__PURE__*/ react.createElement(Dialog, {
|
|
23
|
+
open: vm.open,
|
|
24
|
+
onOpenChange: (open)=>presenter.setOpen(open),
|
|
25
|
+
title: "Re-enrich with AI",
|
|
26
|
+
description: vm.message,
|
|
27
|
+
actions: /*#__PURE__*/ react.createElement(react.Fragment, null, /*#__PURE__*/ react.createElement(Dialog.CancelAction, null), /*#__PURE__*/ react.createElement(Dialog.ConfirmAction, {
|
|
28
|
+
text: "Apply",
|
|
29
|
+
onClick: ()=>presenter.accept(),
|
|
30
|
+
disabled: !vm.canSave
|
|
31
|
+
}))
|
|
32
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
33
|
+
className: "flex flex-col gap-md"
|
|
34
|
+
}, /*#__PURE__*/ react.createElement("div", null, /*#__PURE__*/ react.createElement("div", {
|
|
35
|
+
className: "text-sm font-semibold mb-xs"
|
|
36
|
+
}, "Tags"), /*#__PURE__*/ react.createElement("div", {
|
|
37
|
+
className: "min-h-[48px]"
|
|
38
|
+
}, vm.tags.length ? /*#__PURE__*/ react.createElement("div", {
|
|
39
|
+
className: "flex flex-wrap gap-xs"
|
|
40
|
+
}, vm.tags.map((tag)=>/*#__PURE__*/ react.createElement("span", {
|
|
41
|
+
key: tag,
|
|
42
|
+
className: "text-sm px-sm py-xxs rounded bg-neutral-dimmed"
|
|
43
|
+
}, tag))) : /*#__PURE__*/ react.createElement(StreamingPlaceholder, {
|
|
44
|
+
loading: vm.loading,
|
|
45
|
+
variant: "chips"
|
|
46
|
+
}))), /*#__PURE__*/ react.createElement("div", null, /*#__PURE__*/ react.createElement("div", {
|
|
47
|
+
className: "text-sm font-semibold mb-xs"
|
|
48
|
+
}, "Description"), /*#__PURE__*/ react.createElement("div", {
|
|
49
|
+
className: "text-sm min-h-[4lh]"
|
|
50
|
+
}, vm.description ? vm.description : /*#__PURE__*/ react.createElement(StreamingPlaceholder, {
|
|
51
|
+
loading: vm.loading,
|
|
52
|
+
variant: "text"
|
|
53
|
+
}))))));
|
|
54
|
+
});
|
|
55
|
+
export { ReenrichWithAi_ReenrichWithAi as ReenrichWithAi };
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=ReenrichWithAi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modules/AiEnrichment/ReenrichWithAi.js","sources":["../../../src/modules/AiEnrichment/ReenrichWithAi.tsx"],"sourcesContent":["import React, { useEffect } from \"react\";\nimport { ReactComponent as AiIcon } from \"@webiny/icons/auto_awesome.svg\";\nimport { Dialog } from \"@webiny/admin-ui\";\nimport { createReactiveComponent } from \"@webiny/app-admin\";\nimport { useFeature } from \"@webiny/app\";\nimport { FileManagerViewConfig, useFile } from \"~/index.js\";\nimport { AiEnrichmentFeature } from \"./feature.js\";\nimport { StreamingPlaceholder } from \"./StreamingPlaceholder.js\";\n\nconst { FileDetails } = FileManagerViewConfig;\n\n/**\n * Test-bed for HTTP response streaming: re-runs AI enrichment for the open file and renders the\n * model's output as it arrives, rather than waiting for the whole response.\n *\n * The stream only PROPOSES. Save drops the values into the file details form as pending edits; the\n * file is written when the user presses Update in the drawer, exactly as for a hand-typed edit.\n */\nexport const ReenrichWithAi = createReactiveComponent(function ReenrichWithAi() {\n const { file } = useFile();\n const { presenter } = useFeature(AiEnrichmentFeature);\n const { vm } = presenter;\n\n // Abandon an in-flight stream when this view goes away, so the read loop doesn't keep running\n // against a component that no longer exists.\n useEffect(() => {\n return () => presenter.dispose();\n }, [presenter]);\n\n return (\n <>\n <FileDetails.Action.Button\n label={\"Re-enrich with AI\"}\n icon={<AiIcon />}\n onAction={() => presenter.start(file.id)}\n data-testid={\"fm.file-details.action.reenrich-with-ai\"}\n />\n <Dialog\n open={vm.open}\n onOpenChange={open => presenter.setOpen(open)}\n title={\"Re-enrich with AI\"}\n description={vm.message}\n actions={\n <>\n <Dialog.CancelAction />\n <Dialog.ConfirmAction\n text={\"Apply\"}\n onClick={() => presenter.accept()}\n disabled={!vm.canSave}\n />\n </>\n }\n >\n <div className={\"flex flex-col gap-md\"}>\n {/*\n * Each value box reserves its space so the dialog doesn't resize while the model\n * streams, and the footer buttons don't slide out from under the pointer.\n *\n * On the value boxes rather than once on the body: tags and description grow\n * independently, so a single body minimum would still let wrapping tags shove the\n * description down. The reservations cover what this prompt (\"up to 5 tags and one\n * short sentence\") actually produces — two rows of chips, four lines of text.\n * Beyond that the dialog grows, which beats clipping.\n */}\n <div>\n <div className={\"text-sm font-semibold mb-xs\"}>{\"Tags\"}</div>\n <div className={\"min-h-[48px]\"}>\n {vm.tags.length ? (\n <div className={\"flex flex-wrap gap-xs\"}>\n {vm.tags.map(tag => (\n <span\n key={tag}\n className={\n \"text-sm px-sm py-xxs rounded bg-neutral-dimmed\"\n }\n >\n {tag}\n </span>\n ))}\n </div>\n ) : (\n <StreamingPlaceholder loading={vm.loading} variant={\"chips\"} />\n )}\n </div>\n </div>\n <div>\n <div className={\"text-sm font-semibold mb-xs\"}>{\"Description\"}</div>\n {/*\n * `lh` is the line-height unit, so four lines stays four lines if the type\n * scale changes. Browsers without it simply ignore the minimum.\n */}\n <div className={\"text-sm min-h-[4lh]\"}>\n {vm.description ? (\n vm.description\n ) : (\n <StreamingPlaceholder loading={vm.loading} variant={\"text\"} />\n )}\n </div>\n </div>\n </div>\n </Dialog>\n </>\n );\n});\n"],"names":["FileDetails","FileManagerViewConfig","ReenrichWithAi","createReactiveComponent","file","useFile","presenter","useFeature","AiEnrichmentFeature","vm","useEffect","AiIcon","Dialog","open","tag","StreamingPlaceholder"],"mappings":";;;;;;;;AASA,MAAM,EAAEA,aAAAA,WAAW,EAAE,GAAGC;AASjB,MAAMC,gCAAiBC,wBAAwB;IAClD,MAAM,EAAEC,IAAI,EAAE,GAAGC;IACjB,MAAM,EAAEC,SAAS,EAAE,GAAGC,WAAWC;IACjC,MAAM,EAAEC,EAAE,EAAE,GAAGH;IAIfI,UAAU,IACC,IAAMJ,UAAU,OAAO,IAC/B;QAACA;KAAU;IAEd,OAAO,WAAP,GACI,wDACI,oBAACN,YAAY,MAAM,CAAC,MAAM;QACtB,OAAO;QACP,oBAAM,oBAACW,gBAAMA;QACb,UAAU,IAAML,UAAU,KAAK,CAACF,KAAK,EAAE;QACvC,eAAa;sBAEjB,oBAACQ,QAAMA;QACH,MAAMH,GAAG,IAAI;QACb,cAAcI,CAAAA,OAAQP,UAAU,OAAO,CAACO;QACxC,OAAO;QACP,aAAaJ,GAAG,OAAO;QACvB,uBACI,wDACI,oBAACG,OAAO,YAAY,uBACpB,oBAACA,OAAO,aAAa;YACjB,MAAM;YACN,SAAS,IAAMN,UAAU,MAAM;YAC/B,UAAU,CAACG,GAAG,OAAO;;qBAKjC,oBAAC;QAAI,WAAW;qBAWZ,oBAAC,2BACG,oBAAC;QAAI,WAAW;OAAgC,uBAChD,oBAAC;QAAI,WAAW;OACXA,GAAG,IAAI,CAAC,MAAM,GAAG,WAAH,GACX,oBAAC;QAAI,WAAW;OACXA,GAAG,IAAI,CAAC,GAAG,CAACK,CAAAA,MAAAA,WAAAA,GACT,oBAAC;YACG,KAAKA;YACL,WACI;WAGHA,SAAAA,WAAAA,GAKb,oBAACC,sBAAoBA;QAAC,SAASN,GAAG,OAAO;QAAE,SAAS;wBAIhE,oBAAC,2BACG,oBAAC;QAAI,WAAW;OAAgC,8BAKhD,oBAAC;QAAI,WAAW;OACXA,GAAG,WAAW,GACXA,GAAG,WAAW,iBAEd,oBAACM,sBAAoBA;QAAC,SAASN,GAAG,OAAO;QAAE,SAAS;;AAQpF"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Notifications } from "@webiny/app-admin/features/notifications/abstractions.js";
|
|
2
|
+
import { FileDetailsPresenter } from "../../presentation/FileDetails/abstractions.js";
|
|
3
|
+
import type { IReenrichFileGateway, IReenrichWithAiPresenter, IReenrichWithAiViewModel } from "./abstractions.js";
|
|
4
|
+
declare class ReenrichWithAiPresenterImpl implements IReenrichWithAiPresenter {
|
|
5
|
+
private gateway;
|
|
6
|
+
private fileDetails;
|
|
7
|
+
private notifications;
|
|
8
|
+
private open;
|
|
9
|
+
private status;
|
|
10
|
+
private tags;
|
|
11
|
+
private description;
|
|
12
|
+
private error;
|
|
13
|
+
/**
|
|
14
|
+
* Not observable state — nothing renders from it. It exists so an in-flight stream can be
|
|
15
|
+
* abandoned when the dialog closes or the view goes away; a streaming response stays open for as
|
|
16
|
+
* long as the producer runs, so without this the read loop would outlive its reader.
|
|
17
|
+
*/
|
|
18
|
+
private controller;
|
|
19
|
+
constructor(gateway: IReenrichFileGateway, fileDetails: FileDetailsPresenter.Interface, notifications: Notifications.Interface);
|
|
20
|
+
get vm(): IReenrichWithAiViewModel;
|
|
21
|
+
start(fileId: string): Promise<void>;
|
|
22
|
+
accept(): void;
|
|
23
|
+
setOpen(open: boolean): void;
|
|
24
|
+
/**
|
|
25
|
+
* Called when the view unmounts. Resets, not just aborts: the presenter is a DI singleton, so a
|
|
26
|
+
* left-over `open` would pop the dialog straight back up the next time the view mounts.
|
|
27
|
+
*/
|
|
28
|
+
dispose(): void;
|
|
29
|
+
/** Aborts any previous run, clears the last result, and opens the dialog on a fresh one. */
|
|
30
|
+
private beginRun;
|
|
31
|
+
/**
|
|
32
|
+
* Merges one streamed value into the displayed proposal.
|
|
33
|
+
*
|
|
34
|
+
* The model streams the tag array CHARACTER by character, so the last entry of a partial value is
|
|
35
|
+
* usually a half-written word — "webiny" arrives as "web", "webi", "webiny". While the stream is
|
|
36
|
+
* running that trailing entry is dropped, so a chip appears only once its text is settled; the
|
|
37
|
+
* `done` value is complete and used whole.
|
|
38
|
+
*
|
|
39
|
+
* Do NOT accumulate across partials instead of replacing. Every prefix differs from the last, so
|
|
40
|
+
* "keep everything seen" turns one tag into a chip per keystroke.
|
|
41
|
+
*/
|
|
42
|
+
private applyOutput;
|
|
43
|
+
private finish;
|
|
44
|
+
private fail;
|
|
45
|
+
private abort;
|
|
46
|
+
}
|
|
47
|
+
export declare const ReenrichWithAiPresenter: typeof ReenrichWithAiPresenterImpl & {
|
|
48
|
+
__abstraction: import("@webiny/di").Abstraction<IReenrichWithAiPresenter>;
|
|
49
|
+
};
|
|
50
|
+
export {};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { makeAutoObservable } from "mobx";
|
|
2
|
+
import { Notifications } from "@webiny/app-admin/features/notifications/abstractions.js";
|
|
3
|
+
import { FileDetailsPresenter } from "../../presentation/FileDetails/abstractions.js";
|
|
4
|
+
import { ReenrichFileGateway, ReenrichWithAiPresenter } from "./abstractions.js";
|
|
5
|
+
const STATUS_LABEL = {
|
|
6
|
+
idle: "",
|
|
7
|
+
running: "Analyzing image…",
|
|
8
|
+
ready: "Applying fills in the form below. Press Update to save the file.",
|
|
9
|
+
error: "Failed."
|
|
10
|
+
};
|
|
11
|
+
class ReenrichWithAiPresenterImpl {
|
|
12
|
+
constructor(gateway, fileDetails, notifications){
|
|
13
|
+
this.gateway = gateway;
|
|
14
|
+
this.fileDetails = fileDetails;
|
|
15
|
+
this.notifications = notifications;
|
|
16
|
+
this.open = false;
|
|
17
|
+
this.status = "idle";
|
|
18
|
+
this.tags = [];
|
|
19
|
+
this.description = "";
|
|
20
|
+
this.error = null;
|
|
21
|
+
this.controller = null;
|
|
22
|
+
makeAutoObservable(this, {
|
|
23
|
+
controller: false
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
get vm() {
|
|
27
|
+
return {
|
|
28
|
+
open: this.open,
|
|
29
|
+
message: this.error ?? STATUS_LABEL[this.status],
|
|
30
|
+
tags: this.tags,
|
|
31
|
+
description: this.description,
|
|
32
|
+
canSave: "ready" === this.status,
|
|
33
|
+
loading: "running" === this.status
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async start(fileId) {
|
|
37
|
+
const controller = this.beginRun();
|
|
38
|
+
try {
|
|
39
|
+
for await (const event of this.gateway.execute(fileId, {
|
|
40
|
+
signal: controller.signal
|
|
41
|
+
})){
|
|
42
|
+
if ("partial" === event.type || "done" === event.type) this.applyOutput(event.tags, event.description, "done" === event.type);
|
|
43
|
+
if ("done" === event.type) this.finish("ready");
|
|
44
|
+
if ("error" === event.type) this.fail(event.message);
|
|
45
|
+
}
|
|
46
|
+
} catch (err) {
|
|
47
|
+
if (err instanceof DOMException && "AbortError" === err.name) return;
|
|
48
|
+
this.fail(err instanceof Error ? err.message : String(err));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
accept() {
|
|
52
|
+
if ("ready" !== this.status) return;
|
|
53
|
+
this.fileDetails.vm.form.setData({
|
|
54
|
+
tags: this.tags,
|
|
55
|
+
description: this.description
|
|
56
|
+
}, {
|
|
57
|
+
dirty: true
|
|
58
|
+
});
|
|
59
|
+
this.setOpen(false);
|
|
60
|
+
this.notifications.notify({
|
|
61
|
+
title: "Suggestion applied",
|
|
62
|
+
description: "Press Update to save the new tags and description."
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
setOpen(open) {
|
|
66
|
+
if (!open) this.abort();
|
|
67
|
+
this.open = open;
|
|
68
|
+
}
|
|
69
|
+
dispose() {
|
|
70
|
+
this.abort();
|
|
71
|
+
this.open = false;
|
|
72
|
+
this.status = "idle";
|
|
73
|
+
this.tags = [];
|
|
74
|
+
this.description = "";
|
|
75
|
+
this.error = null;
|
|
76
|
+
}
|
|
77
|
+
beginRun() {
|
|
78
|
+
this.abort();
|
|
79
|
+
const controller = new AbortController();
|
|
80
|
+
this.controller = controller;
|
|
81
|
+
this.open = true;
|
|
82
|
+
this.status = "running";
|
|
83
|
+
this.tags = [];
|
|
84
|
+
this.description = "";
|
|
85
|
+
this.error = null;
|
|
86
|
+
return controller;
|
|
87
|
+
}
|
|
88
|
+
applyOutput(tags, description, final) {
|
|
89
|
+
this.tags = final ? tags : tags.slice(0, -1);
|
|
90
|
+
this.description = description;
|
|
91
|
+
}
|
|
92
|
+
finish(status) {
|
|
93
|
+
this.status = status;
|
|
94
|
+
}
|
|
95
|
+
fail(message) {
|
|
96
|
+
this.error = message;
|
|
97
|
+
this.status = "error";
|
|
98
|
+
}
|
|
99
|
+
abort() {
|
|
100
|
+
this.controller?.abort();
|
|
101
|
+
this.controller = null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const ReenrichWithAiPresenter_ReenrichWithAiPresenter = ReenrichWithAiPresenter.createImplementation({
|
|
105
|
+
implementation: ReenrichWithAiPresenterImpl,
|
|
106
|
+
dependencies: [
|
|
107
|
+
ReenrichFileGateway,
|
|
108
|
+
FileDetailsPresenter,
|
|
109
|
+
Notifications
|
|
110
|
+
]
|
|
111
|
+
});
|
|
112
|
+
export { ReenrichWithAiPresenter_ReenrichWithAiPresenter as ReenrichWithAiPresenter };
|
|
113
|
+
|
|
114
|
+
//# sourceMappingURL=ReenrichWithAiPresenter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modules/AiEnrichment/ReenrichWithAiPresenter.js","sources":["../../../src/modules/AiEnrichment/ReenrichWithAiPresenter.ts"],"sourcesContent":["import { makeAutoObservable } from \"mobx\";\nimport { Notifications } from \"@webiny/app-admin/features/notifications/abstractions.js\";\nimport { FileDetailsPresenter } from \"~/presentation/FileDetails/abstractions.js\";\nimport {\n ReenrichFileGateway,\n ReenrichWithAiPresenter as PresenterAbstraction\n} from \"./abstractions.js\";\nimport type {\n IReenrichFileGateway,\n IReenrichWithAiPresenter,\n IReenrichWithAiViewModel\n} from \"./abstractions.js\";\n\ntype Status = \"idle\" | \"running\" | \"ready\" | \"error\";\n\nconst STATUS_LABEL: Record<Status, string> = {\n idle: \"\",\n running: \"Analyzing image…\",\n // Says what Apply does AND does not do. Applying only fills the form in — the file is written by\n // the drawer's Update button, and nothing else on screen says so.\n ready: \"Applying fills in the form below. Press Update to save the file.\",\n error: \"Failed.\"\n};\n\nclass ReenrichWithAiPresenterImpl implements IReenrichWithAiPresenter {\n private open = false;\n private status: Status = \"idle\";\n private tags: string[] = [];\n private description = \"\";\n private error: string | null = null;\n\n /**\n * Not observable state — nothing renders from it. It exists so an in-flight stream can be\n * abandoned when the dialog closes or the view goes away; a streaming response stays open for as\n * long as the producer runs, so without this the read loop would outlive its reader.\n */\n private controller: AbortController | null = null;\n\n constructor(\n private gateway: IReenrichFileGateway,\n private fileDetails: FileDetailsPresenter.Interface,\n private notifications: Notifications.Interface\n ) {\n // The second type parameter is what lets a PRIVATE field appear in the annotation map.\n makeAutoObservable<ReenrichWithAiPresenterImpl, \"controller\">(this, { controller: false });\n }\n\n get vm(): IReenrichWithAiViewModel {\n return {\n open: this.open,\n message: this.error ?? STATUS_LABEL[this.status],\n tags: this.tags,\n description: this.description,\n canSave: this.status === \"ready\",\n loading: this.status === \"running\"\n };\n }\n\n async start(fileId: string) {\n const controller = this.beginRun();\n\n try {\n for await (const event of this.gateway.execute(fileId, {\n signal: controller.signal\n })) {\n if (event.type === \"partial\" || event.type === \"done\") {\n this.applyOutput(event.tags, event.description, event.type === \"done\");\n }\n\n // Nothing is written yet — the route only proposes. The user accepts with save().\n if (event.type === \"done\") {\n this.finish(\"ready\");\n }\n\n if (event.type === \"error\") {\n this.fail(event.message);\n }\n }\n } catch (err) {\n // An abort is this presenter's own doing, not a failure to report.\n if (err instanceof DOMException && err.name === \"AbortError\") {\n return;\n }\n this.fail(err instanceof Error ? err.message : String(err));\n }\n }\n\n accept() {\n if (this.status !== \"ready\") {\n return;\n }\n\n // Through the form's own `setData` rather than a method on FileDetailsPresenter: the drawer\n // has no business knowing that AI enrichment exists, and every future feature that fills in\n // fields would otherwise add one more method to it.\n //\n // `dirty: true` skips the baseline snapshot, so the form stays dirty and Update has something\n // to save. setData merges — it only touches the keys passed — so Name and Access Control keep\n // whatever the user has typed.\n this.fileDetails.vm.form.setData(\n { tags: this.tags, description: this.description },\n { dirty: true }\n );\n this.setOpen(false);\n\n // The dialog is gone the moment it is accepted, so the reminder has to outlive it. `notify`\n // rather than `success`: nothing has been saved yet, and a green tick would say it had.\n this.notifications.notify({\n title: \"Suggestion applied\",\n description: \"Press Update to save the new tags and description.\"\n });\n }\n\n setOpen(open: boolean) {\n if (!open) {\n this.abort();\n }\n this.open = open;\n }\n\n /**\n * Called when the view unmounts. Resets, not just aborts: the presenter is a DI singleton, so a\n * left-over `open` would pop the dialog straight back up the next time the view mounts.\n */\n dispose() {\n this.abort();\n this.open = false;\n this.status = \"idle\";\n this.tags = [];\n this.description = \"\";\n this.error = null;\n }\n\n /** Aborts any previous run, clears the last result, and opens the dialog on a fresh one. */\n private beginRun(): AbortController {\n this.abort();\n\n const controller = new AbortController();\n this.controller = controller;\n\n this.open = true;\n this.status = \"running\";\n this.tags = [];\n this.description = \"\";\n this.error = null;\n\n return controller;\n }\n\n /**\n * Merges one streamed value into the displayed proposal.\n *\n * The model streams the tag array CHARACTER by character, so the last entry of a partial value is\n * usually a half-written word — \"webiny\" arrives as \"web\", \"webi\", \"webiny\". While the stream is\n * running that trailing entry is dropped, so a chip appears only once its text is settled; the\n * `done` value is complete and used whole.\n *\n * Do NOT accumulate across partials instead of replacing. Every prefix differs from the last, so\n * \"keep everything seen\" turns one tag into a chip per keystroke.\n */\n private applyOutput(tags: string[], description: string, final: boolean) {\n this.tags = final ? tags : tags.slice(0, -1);\n this.description = description;\n }\n\n private finish(status: Status) {\n this.status = status;\n }\n\n private fail(message: string) {\n this.error = message;\n this.status = \"error\";\n }\n\n private abort() {\n this.controller?.abort();\n this.controller = null;\n }\n}\n\nexport const ReenrichWithAiPresenter = PresenterAbstraction.createImplementation({\n implementation: ReenrichWithAiPresenterImpl,\n dependencies: [ReenrichFileGateway, FileDetailsPresenter, Notifications]\n});\n"],"names":["STATUS_LABEL","ReenrichWithAiPresenterImpl","gateway","fileDetails","notifications","makeAutoObservable","fileId","controller","event","err","DOMException","Error","String","open","AbortController","tags","description","final","status","message","ReenrichWithAiPresenter","PresenterAbstraction","ReenrichFileGateway","FileDetailsPresenter","Notifications"],"mappings":";;;;AAeA,MAAMA,eAAuC;IACzC,MAAM;IACN,SAAS;IAGT,OAAO;IACP,OAAO;AACX;AAEA,MAAMC;IAcF,YACYC,OAA6B,EAC7BC,WAA2C,EAC3CC,aAAsC,CAChD;aAHUF,OAAO,GAAPA;aACAC,WAAW,GAAXA;aACAC,aAAa,GAAbA;aAhBJ,IAAI,GAAG;aACP,MAAM,GAAW;aACjB,IAAI,GAAa,EAAE;aACnB,WAAW,GAAG;aACd,KAAK,GAAkB;QAM9B,KACO,UAAU,GAA2B;QAQzCC,mBAA8D,IAAI,EAAE;YAAE,YAAY;QAAM;IAC5F;IAEA,IAAI,KAA+B;QAC/B,OAAO;YACH,MAAM,IAAI,CAAC,IAAI;YACf,SAAS,IAAI,CAAC,KAAK,IAAIL,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC;YAChD,MAAM,IAAI,CAAC,IAAI;YACf,aAAa,IAAI,CAAC,WAAW;YAC7B,SAAS,AAAgB,YAAhB,IAAI,CAAC,MAAM;YACpB,SAAS,AAAgB,cAAhB,IAAI,CAAC,MAAM;QACxB;IACJ;IAEA,MAAM,MAAMM,MAAc,EAAE;QACxB,MAAMC,aAAa,IAAI,CAAC,QAAQ;QAEhC,IAAI;YACA,WAAW,MAAMC,SAAS,IAAI,CAAC,OAAO,CAAC,OAAO,CAACF,QAAQ;gBACnD,QAAQC,WAAW,MAAM;YAC7B,GAAI;gBACA,IAAIC,AAAe,cAAfA,MAAM,IAAI,IAAkBA,AAAe,WAAfA,MAAM,IAAI,EACtC,IAAI,CAAC,WAAW,CAACA,MAAM,IAAI,EAAEA,MAAM,WAAW,EAAEA,AAAe,WAAfA,MAAM,IAAI;gBAI9D,IAAIA,AAAe,WAAfA,MAAM,IAAI,EACV,IAAI,CAAC,MAAM,CAAC;gBAGhB,IAAIA,AAAe,YAAfA,MAAM,IAAI,EACV,IAAI,CAAC,IAAI,CAACA,MAAM,OAAO;YAE/B;QACJ,EAAE,OAAOC,KAAK;YAEV,IAAIA,eAAeC,gBAAgBD,AAAa,iBAAbA,IAAI,IAAI,EACvC;YAEJ,IAAI,CAAC,IAAI,CAACA,eAAeE,QAAQF,IAAI,OAAO,GAAGG,OAAOH;QAC1D;IACJ;IAEA,SAAS;QACL,IAAI,AAAgB,YAAhB,IAAI,CAAC,MAAM,EACX;QAUJ,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAC5B;YAAE,MAAM,IAAI,CAAC,IAAI;YAAE,aAAa,IAAI,CAAC,WAAW;QAAC,GACjD;YAAE,OAAO;QAAK;QAElB,IAAI,CAAC,OAAO,CAAC;QAIb,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC;YACtB,OAAO;YACP,aAAa;QACjB;IACJ;IAEA,QAAQI,IAAa,EAAE;QACnB,IAAI,CAACA,MACD,IAAI,CAAC,KAAK;QAEd,IAAI,CAAC,IAAI,GAAGA;IAChB;IAMA,UAAU;QACN,IAAI,CAAC,KAAK;QACV,IAAI,CAAC,IAAI,GAAG;QACZ,IAAI,CAAC,MAAM,GAAG;QACd,IAAI,CAAC,IAAI,GAAG,EAAE;QACd,IAAI,CAAC,WAAW,GAAG;QACnB,IAAI,CAAC,KAAK,GAAG;IACjB;IAGQ,WAA4B;QAChC,IAAI,CAAC,KAAK;QAEV,MAAMN,aAAa,IAAIO;QACvB,IAAI,CAAC,UAAU,GAAGP;QAElB,IAAI,CAAC,IAAI,GAAG;QACZ,IAAI,CAAC,MAAM,GAAG;QACd,IAAI,CAAC,IAAI,GAAG,EAAE;QACd,IAAI,CAAC,WAAW,GAAG;QACnB,IAAI,CAAC,KAAK,GAAG;QAEb,OAAOA;IACX;IAaQ,YAAYQ,IAAc,EAAEC,WAAmB,EAAEC,KAAc,EAAE;QACrE,IAAI,CAAC,IAAI,GAAGA,QAAQF,OAAOA,KAAK,KAAK,CAAC,GAAG;QACzC,IAAI,CAAC,WAAW,GAAGC;IACvB;IAEQ,OAAOE,MAAc,EAAE;QAC3B,IAAI,CAAC,MAAM,GAAGA;IAClB;IAEQ,KAAKC,OAAe,EAAE;QAC1B,IAAI,CAAC,KAAK,GAAGA;QACb,IAAI,CAAC,MAAM,GAAG;IAClB;IAEQ,QAAQ;QACZ,IAAI,CAAC,UAAU,EAAE;QACjB,IAAI,CAAC,UAAU,GAAG;IACtB;AACJ;AAEO,MAAMC,kDAA0BC,wBAAAA,oBAAyC,CAAC;IAC7E,gBAAgBpB;IAChB,cAAc;QAACqB;QAAqBC;QAAsBC;KAAc;AAC5E"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type StreamingPlaceholderVariant = "chips" | "text";
|
|
3
|
+
export interface StreamingPlaceholderProps {
|
|
4
|
+
/** While the producer is still running, an empty field is pending — not empty. */
|
|
5
|
+
loading: boolean;
|
|
6
|
+
variant: StreamingPlaceholderVariant;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Stands in for a field that has no content yet: skeleton bars while a stream is running, and a dash
|
|
10
|
+
* once it has stopped and the field genuinely came back empty.
|
|
11
|
+
*
|
|
12
|
+
* The bar widths and layout live here rather than in props, so a caller says what KIND of content is
|
|
13
|
+
* coming and nothing more.
|
|
14
|
+
*/
|
|
15
|
+
export declare const StreamingPlaceholder: ({ loading, variant }: StreamingPlaceholderProps) => React.JSX.Element;
|