@alpaca-editor/core 1.0.3942 → 1.0.3943

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.
Files changed (308) hide show
  1. package/.prettierrc +3 -0
  2. package/build.css +3 -0
  3. package/components.json +21 -0
  4. package/dist/editor/ContentTree.d.ts +2 -1
  5. package/dist/editor/ContentTree.js +23 -21
  6. package/dist/editor/ContentTree.js.map +1 -1
  7. package/dist/editor/FieldActionsOverlay.js +0 -2
  8. package/dist/editor/FieldActionsOverlay.js.map +1 -1
  9. package/dist/editor/ScrollingContentTree.js +1 -1
  10. package/dist/editor/ScrollingContentTree.js.map +1 -1
  11. package/dist/editor/Titlebar.js +1 -1
  12. package/dist/editor/Titlebar.js.map +1 -1
  13. package/dist/editor/ai/GhostWriter.js +24 -3
  14. package/dist/editor/ai/GhostWriter.js.map +1 -1
  15. package/dist/editor/client/EditorClient.js +7 -7
  16. package/dist/editor/client/EditorClient.js.map +1 -1
  17. package/dist/editor/field-types/InternalLinkFieldEditor.js +60 -10
  18. package/dist/editor/field-types/InternalLinkFieldEditor.js.map +1 -1
  19. package/dist/editor/media-selector/MediaFolderBrowser.js +48 -1
  20. package/dist/editor/media-selector/MediaFolderBrowser.js.map +1 -1
  21. package/dist/editor/menubar/PageSelector.js +116 -65
  22. package/dist/editor/menubar/PageSelector.js.map +1 -1
  23. package/dist/editor/page-viewer/EditorForm.js +5 -2
  24. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  25. package/dist/editor/ui/ItemSearch.js +14 -8
  26. package/dist/editor/ui/ItemSearch.js.map +1 -1
  27. package/dist/editor/ui/PerfectTree.d.ts +4 -2
  28. package/dist/editor/ui/PerfectTree.js +78 -4
  29. package/dist/editor/ui/PerfectTree.js.map +1 -1
  30. package/dist/editor/ui/Splitter.js +1 -1
  31. package/dist/revision.d.ts +2 -2
  32. package/dist/revision.js +2 -2
  33. package/dist/styles.css +8 -2
  34. package/eslint.config.mjs +4 -0
  35. package/images/bg-shape-black.webp +0 -0
  36. package/images/wizard-bg.png +0 -0
  37. package/images/wizard-tour.png +0 -0
  38. package/images/wizard.png +0 -0
  39. package/package.json +2 -8
  40. package/src/client-components/api.ts +6 -0
  41. package/src/client-components/index.ts +19 -0
  42. package/src/components/ActionButton.tsx +50 -0
  43. package/src/components/Error.tsx +57 -0
  44. package/src/components/ui/CardConnector.tsx +56 -0
  45. package/src/components/ui/button.tsx +62 -0
  46. package/src/components/ui/card.tsx +372 -0
  47. package/src/components/ui/context-menu.tsx +250 -0
  48. package/src/config/config.tsx +917 -0
  49. package/src/config/types.ts +286 -0
  50. package/src/editor/ComponentInfo.tsx +90 -0
  51. package/src/editor/ConfirmationDialog.tsx +103 -0
  52. package/src/editor/ContentTree.tsx +733 -0
  53. package/src/editor/ContextMenu.tsx +230 -0
  54. package/src/editor/Editor.tsx +90 -0
  55. package/src/editor/EditorWarning.tsx +34 -0
  56. package/src/editor/EditorWarnings.tsx +33 -0
  57. package/src/editor/FieldActionsOverlay.tsx +296 -0
  58. package/src/editor/FieldEditorPopup.tsx +65 -0
  59. package/src/editor/FieldHistory.tsx +75 -0
  60. package/src/editor/FieldList.tsx +190 -0
  61. package/src/editor/FieldListField.tsx +391 -0
  62. package/src/editor/FieldListFieldWithFallbacks.tsx +217 -0
  63. package/src/editor/FloatingToolbar.tsx +163 -0
  64. package/src/editor/ImageEditor.tsx +128 -0
  65. package/src/editor/ItemInfo.tsx +90 -0
  66. package/src/editor/LinkEditorDialog.tsx +196 -0
  67. package/src/editor/MainLayout.tsx +95 -0
  68. package/src/editor/MobileLayout.tsx +68 -0
  69. package/src/editor/NewEditorClient.tsx +11 -0
  70. package/src/editor/PictureCropper.tsx +568 -0
  71. package/src/editor/PictureEditor.tsx +301 -0
  72. package/src/editor/PictureEditorDialog.tsx +381 -0
  73. package/src/editor/PublishDialog.ignore +74 -0
  74. package/src/editor/ScrollingContentTree.tsx +68 -0
  75. package/src/editor/Terminal.tsx +227 -0
  76. package/src/editor/Titlebar.tsx +104 -0
  77. package/src/editor/ai/AiPopup.tsx +59 -0
  78. package/src/editor/ai/AiResponseMessage.tsx +106 -0
  79. package/src/editor/ai/AiTerminal.tsx +503 -0
  80. package/src/editor/ai/AiToolCall.tsx +61 -0
  81. package/src/editor/ai/EditorAiTerminal.tsx +20 -0
  82. package/src/editor/ai/GhostWriter.tsx +480 -0
  83. package/src/editor/ai/aiPageModel.ts +108 -0
  84. package/src/editor/ai/editorAiContext.ts +18 -0
  85. package/src/editor/client/AboutDialog.tsx +44 -0
  86. package/src/editor/client/EditorClient.tsx +2241 -0
  87. package/src/editor/client/GenericDialog.tsx +50 -0
  88. package/src/editor/client/editContext.ts +416 -0
  89. package/src/editor/client/helpers.ts +44 -0
  90. package/src/editor/client/itemsRepository.ts +574 -0
  91. package/src/editor/client/operations.ts +768 -0
  92. package/src/editor/client/pageModelBuilder.ts +219 -0
  93. package/src/editor/commands/commands.ts +22 -0
  94. package/src/editor/commands/componentCommands.tsx +431 -0
  95. package/src/editor/commands/createVersionCommand.ts +33 -0
  96. package/src/editor/commands/deleteVersionCommand.ts +71 -0
  97. package/src/editor/commands/itemCommands.tsx +351 -0
  98. package/src/editor/commands/localizeItem/LocalizeItemDialog.tsx +201 -0
  99. package/src/editor/commands/localizeItem/LocalizeItemUtils.ts +27 -0
  100. package/src/editor/commands/undo.ts +39 -0
  101. package/src/editor/component-designer/ComponentDesigner.tsx +70 -0
  102. package/src/editor/component-designer/ComponentDesignerAiTerminal.tsx +11 -0
  103. package/src/editor/component-designer/ComponentDesignerMenu.tsx +91 -0
  104. package/src/editor/component-designer/ComponentEditor.tsx +97 -0
  105. package/src/editor/component-designer/ComponentRenderingCodeEditor.tsx +31 -0
  106. package/src/editor/component-designer/ComponentRenderingEditor.tsx +104 -0
  107. package/src/editor/component-designer/ComponentsDropdown.tsx +39 -0
  108. package/src/editor/component-designer/PlaceholdersEditor.tsx +179 -0
  109. package/src/editor/component-designer/RenderingsDropdown.tsx +36 -0
  110. package/src/editor/component-designer/TemplateEditor.tsx +236 -0
  111. package/src/editor/component-designer/aiContext.ts +23 -0
  112. package/src/editor/componentTreeHelper.tsx +116 -0
  113. package/src/editor/context-menu/CopyMoveMenu.tsx +103 -0
  114. package/src/editor/context-menu/InsertMenu.tsx +347 -0
  115. package/src/editor/control-center/About.tsx +342 -0
  116. package/src/editor/control-center/ControlCenterMenu.tsx +76 -0
  117. package/src/editor/control-center/IndexOverview.tsx +50 -0
  118. package/src/editor/control-center/IndexSettings.tsx +266 -0
  119. package/src/editor/control-center/Info.tsx +104 -0
  120. package/src/editor/control-center/QuotaInfo.tsx +301 -0
  121. package/src/editor/control-center/Status.tsx +113 -0
  122. package/src/editor/control-center/WebSocketMessages.tsx +155 -0
  123. package/src/editor/editor-warnings/ItemLocked.tsx +63 -0
  124. package/src/editor/editor-warnings/NoLanguageWriteAccess.tsx +22 -0
  125. package/src/editor/editor-warnings/NoWorkflowWriteAccess.tsx +23 -0
  126. package/src/editor/editor-warnings/NoWriteAccess.tsx +16 -0
  127. package/src/editor/editor-warnings/ValidationErrors.tsx +54 -0
  128. package/src/editor/field-types/AttachmentEditor.tsx +9 -0
  129. package/src/editor/field-types/CheckboxEditor.tsx +47 -0
  130. package/src/editor/field-types/DropLinkEditor.tsx +80 -0
  131. package/src/editor/field-types/DropListEditor.tsx +84 -0
  132. package/src/editor/field-types/ImageFieldEditor.tsx +65 -0
  133. package/src/editor/field-types/InternalLinkFieldEditor.tsx +188 -0
  134. package/src/editor/field-types/LinkFieldEditor.tsx +85 -0
  135. package/src/editor/field-types/MultiLineText.tsx +82 -0
  136. package/src/editor/field-types/PictureFieldEditor.tsx +121 -0
  137. package/src/editor/field-types/RawEditor.tsx +53 -0
  138. package/src/editor/field-types/ReactQuill.tsx +580 -0
  139. package/src/editor/field-types/RichTextEditor.tsx +22 -0
  140. package/src/editor/field-types/RichTextEditorComponent.tsx +127 -0
  141. package/src/editor/field-types/SingleLineText.tsx +174 -0
  142. package/src/editor/field-types/TreeListEditor.tsx +261 -0
  143. package/src/editor/fieldTypes.ts +140 -0
  144. package/src/editor/media-selector/AiImageSearch.tsx +185 -0
  145. package/src/editor/media-selector/AiImageSearchPrompt.tsx +94 -0
  146. package/src/editor/media-selector/MediaFolderBrowser.tsx +321 -0
  147. package/src/editor/media-selector/MediaSelector.tsx +42 -0
  148. package/src/editor/media-selector/Preview.tsx +14 -0
  149. package/src/editor/media-selector/Thumbnails.tsx +48 -0
  150. package/src/editor/media-selector/TreeSelector.tsx +292 -0
  151. package/src/editor/media-selector/UploadZone.tsx +137 -0
  152. package/src/editor/media-selector/index.ts +8 -0
  153. package/src/editor/menubar/ActionsMenu.tsx +94 -0
  154. package/src/editor/menubar/ActiveUsers.tsx +17 -0
  155. package/src/editor/menubar/ApproveAndPublish.tsx +18 -0
  156. package/src/editor/menubar/BrowseHistory.tsx +28 -0
  157. package/src/editor/menubar/ItemLanguageVersion.tsx +76 -0
  158. package/src/editor/menubar/LanguageSelector.tsx +226 -0
  159. package/src/editor/menubar/Menu.tsx +83 -0
  160. package/src/editor/menubar/NavButtons.tsx +74 -0
  161. package/src/editor/menubar/PageSelector.tsx +278 -0
  162. package/src/editor/menubar/PageViewerControls.tsx +120 -0
  163. package/src/editor/menubar/PreviewSecondaryControls.tsx +18 -0
  164. package/src/editor/menubar/SecondaryControls.tsx +45 -0
  165. package/src/editor/menubar/Separator.tsx +12 -0
  166. package/src/editor/menubar/SiteInfo.tsx +53 -0
  167. package/src/editor/menubar/User.tsx +27 -0
  168. package/src/editor/menubar/VersionSelector.tsx +142 -0
  169. package/src/editor/page-editor-chrome/CommentHighlighting.tsx +307 -0
  170. package/src/editor/page-editor-chrome/CommentHighlightings.tsx +35 -0
  171. package/src/editor/page-editor-chrome/FieldActionIndicator.tsx +59 -0
  172. package/src/editor/page-editor-chrome/FieldActionIndicators.tsx +23 -0
  173. package/src/editor/page-editor-chrome/FieldEditedIndicator.tsx +64 -0
  174. package/src/editor/page-editor-chrome/FieldEditedIndicators.tsx +35 -0
  175. package/src/editor/page-editor-chrome/FrameMenu.tsx +338 -0
  176. package/src/editor/page-editor-chrome/FrameMenus.tsx +48 -0
  177. package/src/editor/page-editor-chrome/InlineEditor.tsx +765 -0
  178. package/src/editor/page-editor-chrome/LockedFieldIndicator.tsx +61 -0
  179. package/src/editor/page-editor-chrome/NoLayout.tsx +36 -0
  180. package/src/editor/page-editor-chrome/PageEditorChrome.tsx +122 -0
  181. package/src/editor/page-editor-chrome/PictureEditorOverlay.tsx +161 -0
  182. package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +169 -0
  183. package/src/editor/page-editor-chrome/PlaceholderDropZones.tsx +315 -0
  184. package/src/editor/page-editor-chrome/SuggestionHighlighting.tsx +300 -0
  185. package/src/editor/page-editor-chrome/SuggestionHighlightings.tsx +40 -0
  186. package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +828 -0
  187. package/src/editor/page-viewer/DeviceToolbar.tsx +70 -0
  188. package/src/editor/page-viewer/EditorForm.tsx +262 -0
  189. package/src/editor/page-viewer/MiniMap.tsx +362 -0
  190. package/src/editor/page-viewer/PageViewer.tsx +169 -0
  191. package/src/editor/page-viewer/PageViewerFrame.tsx +1022 -0
  192. package/src/editor/page-viewer/pageModelSkeletonBuilder.ts +412 -0
  193. package/src/editor/page-viewer/pageViewContext.ts +186 -0
  194. package/src/editor/pageModel.ts +220 -0
  195. package/src/editor/picture-shared.tsx +53 -0
  196. package/src/editor/reviews/Comment.tsx +308 -0
  197. package/src/editor/reviews/Comments.tsx +125 -0
  198. package/src/editor/reviews/DiffView.tsx +109 -0
  199. package/src/editor/reviews/PreviewInfo.tsx +35 -0
  200. package/src/editor/reviews/Reviews.tsx +280 -0
  201. package/src/editor/reviews/SuggestedEdit.tsx +316 -0
  202. package/src/editor/reviews/reviewCommands.tsx +47 -0
  203. package/src/editor/reviews/useReviews.tsx +70 -0
  204. package/src/editor/services/aiService.ts +173 -0
  205. package/src/editor/services/componentDesignerService.ts +151 -0
  206. package/src/editor/services/contentService.ts +180 -0
  207. package/src/editor/services/editService.ts +488 -0
  208. package/src/editor/services/indexService.ts +24 -0
  209. package/src/editor/services/reviewsService.ts +53 -0
  210. package/src/editor/services/serviceHelper.ts +95 -0
  211. package/src/editor/services/suggestedEditsService.ts +39 -0
  212. package/src/editor/services/systemService.ts +5 -0
  213. package/src/editor/services/translationService.ts +21 -0
  214. package/src/editor/services-server/api.ts +150 -0
  215. package/src/editor/services-server/graphQL.ts +106 -0
  216. package/src/editor/sidebar/ComponentPalette.tsx +161 -0
  217. package/src/editor/sidebar/ComponentTree.tsx +549 -0
  218. package/src/editor/sidebar/Debug.tsx +111 -0
  219. package/src/editor/sidebar/DictionaryEditor.tsx +261 -0
  220. package/src/editor/sidebar/EditHistory.tsx +134 -0
  221. package/src/editor/sidebar/GraphQL.tsx +164 -0
  222. package/src/editor/sidebar/Insert.tsx +35 -0
  223. package/src/editor/sidebar/MainContentTree.tsx +102 -0
  224. package/src/editor/sidebar/Performance.tsx +53 -0
  225. package/src/editor/sidebar/Sessions.tsx +35 -0
  226. package/src/editor/sidebar/Sidebar.tsx +20 -0
  227. package/src/editor/sidebar/SidebarView.tsx +152 -0
  228. package/src/editor/sidebar/Translations.tsx +295 -0
  229. package/src/editor/sidebar/Validation.tsx +102 -0
  230. package/src/editor/sidebar/ViewSelector.tsx +60 -0
  231. package/src/editor/sidebar/Workbox.tsx +209 -0
  232. package/src/editor/ui/CenteredMessage.tsx +7 -0
  233. package/src/editor/ui/CopyMoveTargetSelectorDialog.tsx +81 -0
  234. package/src/editor/ui/CopyToClipboardButton.tsx +24 -0
  235. package/src/editor/ui/DialogButtons.tsx +11 -0
  236. package/src/editor/ui/Icons.tsx +709 -0
  237. package/src/editor/ui/ItemList.tsx +76 -0
  238. package/src/editor/ui/ItemNameDialogNew.tsx +118 -0
  239. package/src/editor/ui/ItemSearch.tsx +159 -0
  240. package/src/editor/ui/PerfectTree.tsx +676 -0
  241. package/src/editor/ui/Section.tsx +35 -0
  242. package/src/editor/ui/SimpleIconButton.tsx +54 -0
  243. package/src/editor/ui/SimpleMenu.tsx +40 -0
  244. package/src/editor/ui/SimpleTable.tsx +60 -0
  245. package/src/editor/ui/SimpleTabs.tsx +60 -0
  246. package/src/editor/ui/SimpleToolbar.tsx +7 -0
  247. package/src/editor/ui/Spinner.tsx +9 -0
  248. package/src/editor/ui/Splitter.tsx +420 -0
  249. package/src/editor/ui/StackedPanels.tsx +134 -0
  250. package/src/editor/ui/Toolbar.tsx +7 -0
  251. package/src/editor/utils/id-helper.ts +3 -0
  252. package/src/editor/utils/insertOptions.ts +69 -0
  253. package/src/editor/utils/itemutils.ts +29 -0
  254. package/src/editor/utils/useMemoDebug.ts +28 -0
  255. package/src/editor/utils.ts +486 -0
  256. package/src/editor/views/CompareView.tsx +245 -0
  257. package/src/editor/views/EditView.tsx +27 -0
  258. package/src/editor/views/ItemEditor.tsx +58 -0
  259. package/src/editor/views/MediaFolderEditView.tsx +66 -0
  260. package/src/editor/views/SingleEditView.tsx +57 -0
  261. package/src/fonts/Geist-Black.woff2 +0 -0
  262. package/src/fonts/Geist-Bold.woff2 +0 -0
  263. package/src/fonts/Geist-ExtraBold.woff2 +0 -0
  264. package/src/fonts/Geist-ExtraLight.woff2 +0 -0
  265. package/src/fonts/Geist-Light.woff2 +0 -0
  266. package/src/fonts/Geist-Medium.woff2 +0 -0
  267. package/src/fonts/Geist-Regular.woff2 +0 -0
  268. package/src/fonts/Geist-SemiBold.woff2 +0 -0
  269. package/src/fonts/Geist-Thin.woff2 +0 -0
  270. package/src/fonts/Geist[wght].woff2 +0 -0
  271. package/src/fonts/index.ts +10 -0
  272. package/src/index.ts +23 -0
  273. package/src/lib/safelist.tsx +16 -0
  274. package/src/lib/utils.ts +6 -0
  275. package/src/page-wizard/PageWizard.tsx +139 -0
  276. package/src/page-wizard/WizardBox.tsx +4 -0
  277. package/src/page-wizard/WizardBoxConnector.tsx +56 -0
  278. package/src/page-wizard/WizardSteps.tsx +458 -0
  279. package/src/page-wizard/service.ts +35 -0
  280. package/src/page-wizard/startPageWizardCommand.ts +26 -0
  281. package/src/page-wizard/steps/BuildPageStep.tsx +259 -0
  282. package/src/page-wizard/steps/CollectStep.tsx +296 -0
  283. package/src/page-wizard/steps/ComponentTypesSelector.tsx +454 -0
  284. package/src/page-wizard/steps/Components.tsx +193 -0
  285. package/src/page-wizard/steps/ContentStep.tsx +890 -0
  286. package/src/page-wizard/steps/EditButton.tsx +34 -0
  287. package/src/page-wizard/steps/FieldEditor.tsx +102 -0
  288. package/src/page-wizard/steps/Generate.tsx +60 -0
  289. package/src/page-wizard/steps/ImagesStep.tsx +382 -0
  290. package/src/page-wizard/steps/LayoutStep.tsx +227 -0
  291. package/src/page-wizard/steps/MetaDataStep.tsx +173 -0
  292. package/src/page-wizard/steps/SelectStep.tsx +281 -0
  293. package/src/page-wizard/steps/schema.ts +180 -0
  294. package/src/page-wizard/steps/usePageCreator.ts +325 -0
  295. package/src/page-wizard/usePageWizard.ts +79 -0
  296. package/src/revision.ts +2 -0
  297. package/src/splash-screen/NewPage.tsx +294 -0
  298. package/src/splash-screen/OpenPage.tsx +113 -0
  299. package/src/splash-screen/RecentPages.tsx +123 -0
  300. package/src/splash-screen/SectionHeadline.tsx +21 -0
  301. package/src/splash-screen/SplashScreen.tsx +195 -0
  302. package/src/tour/Tour.tsx +566 -0
  303. package/src/tour/default-tour.tsx +301 -0
  304. package/src/tour/preview-tour.tsx +128 -0
  305. package/src/types.ts +335 -0
  306. package/styles.css +765 -0
  307. package/tsconfig.build.json +31 -0
  308. package/tsconfig.json +14 -0
@@ -0,0 +1,220 @@
1
+ import { InsertOption } from "../types";
2
+
3
+ export type PageSkeleton = {
4
+ editRevision: string;
5
+ item: ItemDescriptor;
6
+ rootComponent: ComponentSkeleton;
7
+ };
8
+
9
+ export type PlaceholderSkeleton = {
10
+ name: string;
11
+ key: string;
12
+ description: string;
13
+ components: ComponentSkeleton[];
14
+ parentComponent: ComponentSkeleton;
15
+ };
16
+
17
+ export type ComponentSkeleton = {
18
+ id: string;
19
+ name: string;
20
+ type: string;
21
+ typeId: string;
22
+ items: RenderedItemSkeleton[];
23
+ placeholders: PlaceholderSkeleton[];
24
+ datasourceItem?: RenderedItemSkeleton;
25
+ isRemovedFromMasterLanguage?: boolean;
26
+ isInheritedFromMasterLanguage?: boolean;
27
+ visible?: boolean;
28
+ parentPlaceholder?: PlaceholderSkeleton;
29
+ renderedDictionaryKeys: string[];
30
+ layoutId?: string;
31
+ editorFields: EditorFields;
32
+ firstDOMElement?: Element;
33
+ lastDOMElement?: Element;
34
+ };
35
+
36
+ export type RenderedItemSkeleton = ItemDescriptor & {
37
+ renderedFieldIds: string[];
38
+ };
39
+
40
+ export type ItemDescriptor = {
41
+ id: string;
42
+ language: string;
43
+ version: number;
44
+ name?: string;
45
+ };
46
+
47
+ export type Field = {
48
+ id: string;
49
+ value: unknown;
50
+ rawValue?: string | null;
51
+ name?: string;
52
+ type: string;
53
+ descriptor: FieldDescriptor;
54
+ displayName?: string;
55
+ sortOrder?: number;
56
+ sectionSortOrder?: number;
57
+ customProperties?: any;
58
+ canWrite?: boolean;
59
+ hasLock?: boolean;
60
+ canLock?: boolean;
61
+ lockedBy?: LockSession;
62
+ section?: string;
63
+ isFallback?: boolean;
64
+ isStandardValue?: boolean;
65
+ isShared?: boolean;
66
+ sourceItems: ItemRef[];
67
+ buttons?: FieldButton[];
68
+ fallbackChain?: FieldDescriptor[];
69
+ isHistoric?: boolean; // true if the value is from the fields history
70
+ };
71
+
72
+ export type ItemRef = {
73
+ id: string;
74
+ name?: string;
75
+ };
76
+
77
+ export type FieldButtonParameter = {
78
+ id: string;
79
+ label: string;
80
+ placeholder?: string;
81
+ type?: "text" | "textarea" | "number" | "select";
82
+ required?: boolean;
83
+ defaultValue?: string;
84
+ options?: { value: string; label: string }[]; // for select type
85
+ rows?: number; // for textarea type
86
+ };
87
+
88
+ export type FieldButton = {
89
+ id: string;
90
+ label: string;
91
+ description: string;
92
+ action?: string;
93
+ icon?: string;
94
+ isGenerator: boolean;
95
+ parameters?: FieldButtonParameter[];
96
+ };
97
+
98
+ export type LockSession = {
99
+ sessionId: string;
100
+ name: string;
101
+ };
102
+
103
+ export type FieldDescriptor = {
104
+ item: ItemDescriptor;
105
+ fieldId: string;
106
+ };
107
+
108
+ export type Language = {
109
+ name: string;
110
+ versions: number;
111
+ icon: string;
112
+ languageCode: string;
113
+ };
114
+
115
+ export type Version = {
116
+ version: number;
117
+ updated: Date;
118
+ updatedBy: string;
119
+ };
120
+
121
+ export type Timings = {
122
+ [key: string]: number;
123
+ };
124
+
125
+ export type ItemStub = ItemDescriptor & {
126
+ name: string;
127
+ templateId: string;
128
+ templateName: string;
129
+ icon: string;
130
+ path: string;
131
+ };
132
+
133
+ export type FullItem = ItemDescriptor & {
134
+ descriptor: ItemDescriptor;
135
+ name: string;
136
+ templateId: string;
137
+ templateName: string;
138
+ icon: string;
139
+ largeIcon: string;
140
+ thumbnail: string;
141
+ path: string;
142
+ masterLanguages?: string[];
143
+ translations?: string[];
144
+ fields: Field[];
145
+ canWriteItem: boolean;
146
+ canWriteLanguage: boolean;
147
+ canWriteWorkflow: boolean;
148
+ canRename: boolean;
149
+ canDelete: boolean;
150
+ canCreate: boolean;
151
+ workflowState: string;
152
+ hasLock: boolean;
153
+ hasLayout: boolean;
154
+ idPath: string;
155
+ canLock: boolean;
156
+ lockedBy: string;
157
+ isInheritedFromMasterLanguage: boolean;
158
+ mainItemId?: string;
159
+ hasChildren: boolean;
160
+ parentId: string;
161
+ versions: number;
162
+ };
163
+
164
+ export type Page = {
165
+ editRevision: string;
166
+ item: FullItem;
167
+ rootComponent: Component;
168
+ };
169
+
170
+ export type RenderedItem = RenderedItemSkeleton & ItemStub;
171
+
172
+ type EditorFields = {
173
+ [group: string]: {
174
+ addFields: string[];
175
+ removeFields: string[];
176
+ };
177
+ };
178
+
179
+ export type Component = {
180
+ id: string;
181
+ name: string;
182
+ type: string;
183
+ typeId: string;
184
+ items: RenderedItem[];
185
+ placeholders: Placeholder[];
186
+ datasourceItem?: RenderedItem;
187
+ isRemovedFromMasterLanguage?: boolean;
188
+ isInheritedFromMasterLanguage?: boolean;
189
+ visible?: boolean;
190
+ parentPlaceholder?: Placeholder;
191
+ renderedDictionaryKeys: string[];
192
+ isShared: boolean;
193
+ rendering?: RenderingReference;
194
+ layoutId?: string;
195
+ editorFields: EditorFields;
196
+ firstDOMElement?: Element;
197
+ lastDOMElement?: Element;
198
+ canBeMoved: boolean;
199
+ icon?: string;
200
+ };
201
+
202
+ export type Placeholder = {
203
+ key: string;
204
+ name: string;
205
+ description: string;
206
+ components: Component[];
207
+ parentComponent: Component;
208
+ canSynchronize: boolean;
209
+ insertOptions: InsertOption[];
210
+ };
211
+
212
+ export interface RenderingReference {
213
+ id: string;
214
+ revision: string;
215
+ }
216
+
217
+ export type ItemIdAndName = {
218
+ name: string;
219
+ id: string;
220
+ };
@@ -0,0 +1,53 @@
1
+ import { PictureVariant } from "./fieldTypes";
2
+
3
+ export type PictureParams = {
4
+ variant?: string;
5
+ aspectRatio?: number;
6
+ scales?: number[];
7
+ maxWidth?: number;
8
+ custom?: { [paramName: string]: string | number | boolean | undefined };
9
+ };
10
+
11
+ export type MediaPictureParams = {
12
+ default: PictureParams;
13
+ [mediaQuery: string]: PictureParams;
14
+ };
15
+
16
+ export function getRenderedPictureVariant(
17
+ params: PictureParams,
18
+ variants: PictureVariant[]
19
+ ) {
20
+ const variantName = params.variant ?? "auto";
21
+
22
+ const variant =
23
+ variantName.toLocaleLowerCase() === "auto" && params.aspectRatio
24
+ ? getBestMatchingVariant(variants, params.aspectRatio)
25
+ : variants.find(
26
+ (v) => v.name.toLowerCase() == variantName.toLowerCase()
27
+ ) ?? variants[0];
28
+ return variant;
29
+ }
30
+
31
+ export function getBestMatchingVariant(
32
+ variants: PictureVariant[],
33
+ aspectRatio: number | undefined
34
+ ) {
35
+ if (!aspectRatio)
36
+ return (
37
+ variants.find((v) => v.name.toLowerCase() == "default") ?? variants[0]
38
+ );
39
+
40
+ let minDistance = 1000000;
41
+ let bestMatch = undefined;
42
+ for (let i = 0; i < variants.length; i++) {
43
+ const variant = variants[i];
44
+ if (!variant.src) continue;
45
+ const distance = Math.abs(aspectRatio - variant.aspectRatio);
46
+ if (distance < minDistance) {
47
+ minDistance = distance;
48
+ bestMatch = variant;
49
+ }
50
+ }
51
+
52
+ return bestMatch ?? variants[0];
53
+ }
@@ -0,0 +1,308 @@
1
+ import { Comment as CommentType } from "../../types";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { useEditContext } from "../client/editContext";
4
+ import {
5
+ createOrUpdateComment,
6
+ deleteComment,
7
+ resolveComment,
8
+ unresolveComment,
9
+ } from "../services/reviewsService";
10
+ import { Button } from "../../components/ui/button";
11
+ import { formatDate } from "../utils";
12
+ import { SimpleIconButton } from "../ui/SimpleIconButton";
13
+ import { OverlayPanel } from "primereact/overlaypanel";
14
+ import { ProgressSpinner } from "primereact/progressspinner";
15
+ import { useDebouncedCallback } from "use-debounce";
16
+ import { ActionButton } from "../../components/ActionButton";
17
+
18
+ export function Comment({ comment }: { comment: CommentType }) {
19
+ const editContext = useEditContext();
20
+ const [commentText, setCommentText] = useState(comment.text);
21
+ const [isEditing, setIsEditing] = useState(false);
22
+ const [isSaving, setIsSaving] = useState(false);
23
+ const ref = useRef<HTMLDivElement>(null);
24
+
25
+ useEffect(() => {
26
+ if (comment.isNew) setIsEditing(true);
27
+ }, [comment]);
28
+
29
+ const isSelected = comment.id === editContext?.selectedComment?.id;
30
+
31
+ useEffect(() => {
32
+ if (isSelected) {
33
+ if (ref.current) {
34
+ ref.current.scrollIntoView({ behavior: "smooth", block: "nearest" });
35
+ }
36
+ }
37
+ }, [isSelected]);
38
+
39
+ const renderContextInfo = () => {
40
+ const showItemName =
41
+ comment.itemId && comment.mainItemId !== comment.itemId;
42
+
43
+ const showFieldName = comment.fieldId;
44
+
45
+ if (!showItemName && !showFieldName) return null;
46
+
47
+ return (
48
+ <div className="mt-3 flex items-center border-t pt-3 text-xs">
49
+ {showItemName && (
50
+ <div className="text-2xs text-gray-500">{comment.itemName}</div>
51
+ )}
52
+
53
+ {showFieldName && showItemName && (
54
+ <div className="text-2xs mx-2 text-gray-500">&gt;</div>
55
+ )}
56
+
57
+ {showFieldName && (
58
+ <div className="text-2xs text-gray-500">{comment.fieldName}</div>
59
+ )}
60
+ </div>
61
+ );
62
+ };
63
+
64
+ const canDelete =
65
+ !comment.isNew && comment.author === editContext?.user?.name;
66
+
67
+ const canEdit =
68
+ !comment.isNew &&
69
+ !comment.isResolved &&
70
+ comment.author === editContext?.user?.name;
71
+
72
+ const canResolve = !comment.isNew && !editContext?.readonly;
73
+
74
+ const getHiddenSystemPrompt = (comment: CommentType) => {
75
+ let prompt =
76
+ "Please make suggestions how to resolve a review comment. Ask the user whether you should apply the suggested changes.";
77
+ if (comment.itemId) {
78
+ prompt += "\n\nItemId: " + comment.itemId;
79
+ }
80
+ if (comment.itemName) {
81
+ prompt += "\n\nItem name: " + comment.itemName;
82
+ }
83
+ if (comment.itemId) {
84
+ prompt += "\n\nItemId: " + comment.itemId;
85
+ }
86
+ if (comment.fieldName) {
87
+ prompt += "\n\nField name: " + comment.fieldName;
88
+ }
89
+ return prompt;
90
+ };
91
+
92
+ const renderHeader = () => {
93
+ const overlayPanelRef = useRef<OverlayPanel>(null);
94
+ const deleteOverlayPanelRef = useRef<OverlayPanel>(null);
95
+ return (
96
+ <>
97
+ <div className="mb-3 flex items-start justify-between">
98
+ <div>
99
+ <div
100
+ className="text-xs font-bold text-gray-900"
101
+ title={comment.author}
102
+ >
103
+ {comment.authorDisplayName}
104
+ </div>
105
+ <div className="text-xs text-gray-500">
106
+ {comment.created ? formatDate(new Date(comment.created)) : ""}
107
+ </div>
108
+ </div>
109
+ <div className="text-xs text-gray-500">
110
+ {!isEditing && canEdit && (
111
+ <SimpleIconButton
112
+ icon="pi pi-pencil"
113
+ label="Edit"
114
+ onClick={() => setIsEditing(true)}
115
+ />
116
+ )}
117
+ {canDelete && (
118
+ <SimpleIconButton
119
+ icon="pi pi-trash"
120
+ label="Delete"
121
+ onClick={(e) => {
122
+ deleteOverlayPanelRef.current?.toggle(e);
123
+ }}
124
+ />
125
+ )}
126
+ <OverlayPanel ref={deleteOverlayPanelRef}>
127
+ <Button
128
+ className="m-2"
129
+ variant="outline"
130
+ onClick={async () => {
131
+ await deleteComment(comment);
132
+ }}
133
+ >
134
+ Delete
135
+ </Button>
136
+ </OverlayPanel>
137
+ {canResolve && !comment.isResolved && (
138
+ <SimpleIconButton
139
+ icon="pi pi-check"
140
+ label="Resolve"
141
+ onClick={async () => {
142
+ await resolveComment(comment);
143
+ }}
144
+ />
145
+ )}
146
+ {comment.isResolved && (
147
+ <>
148
+ <i
149
+ className="pi pi-check cursor-pointer px-1 text-xs text-green-500"
150
+ style={{ fontWeight: "bold" }}
151
+ title={
152
+ "Resolved by " +
153
+ comment.resolvedBy +
154
+ " (" +
155
+ formatDate(new Date(comment.resolvedDate!)) +
156
+ ")"
157
+ }
158
+ onClick={(e) => {
159
+ if (canResolve) {
160
+ overlayPanelRef.current?.toggle(e);
161
+ }
162
+ }}
163
+ ></i>
164
+ <OverlayPanel ref={overlayPanelRef}>
165
+ <Button
166
+ className="m-2"
167
+ variant="outline"
168
+ onClick={async () => {
169
+ await unresolveComment(comment);
170
+ }}
171
+ >
172
+ Unresolve
173
+ </Button>
174
+ </OverlayPanel>
175
+ </>
176
+ )}
177
+ {canResolve && !comment.isResolved && (
178
+ <SimpleIconButton
179
+ icon="pi pi-sparkles"
180
+ label="AI"
181
+ onClick={async (event) => {
182
+ editContext?.showAiPopup(event as any, {
183
+ initialPrompt:
184
+ 'Please help me resolve this comment: "' +
185
+ comment.text +
186
+ '"',
187
+ hiddenSystemPrompt: getHiddenSystemPrompt(comment),
188
+ });
189
+ }}
190
+ />
191
+ )}
192
+ </div>
193
+ </div>
194
+ </>
195
+ );
196
+ };
197
+
198
+ const setCommentsDebounced = useDebouncedCallback(() => {
199
+ if (!editContext?.setComments) return;
200
+ editContext.setComments([...editContext.comments]);
201
+ }, 300);
202
+
203
+ return !isEditing ? (
204
+ <div
205
+ ref={ref}
206
+ key={comment.id}
207
+ data-testid="comment"
208
+ className={`mb-3 cursor-pointer rounded-lg border-2 bg-white p-3 shadow-sm hover:bg-gray-50 ${
209
+ isSelected ? "border-blue-500" : "border-transparent"
210
+ }`}
211
+ onClick={() => {
212
+ editContext?.setSelectedComment(comment);
213
+ editContext?.setScrollIntoView(comment.itemId);
214
+ editContext?.select([comment.itemId]);
215
+ if (comment.fieldId) {
216
+ editContext?.setFocusedField(
217
+ {
218
+ fieldId: comment.fieldId,
219
+ item: {
220
+ id: comment.itemId,
221
+ language: comment.language,
222
+ version: comment.version,
223
+ },
224
+ },
225
+ false,
226
+ );
227
+ }
228
+ }}
229
+ >
230
+ {renderHeader()}
231
+ <div className="text-sm whitespace-pre-wrap text-gray-700">
232
+ {comment.text}
233
+ </div>
234
+ {renderContextInfo()}
235
+ </div>
236
+ ) : (
237
+ <div key="new-comment" className="mb-3 rounded-lg bg-white p-3 shadow-sm">
238
+ {renderHeader()}
239
+ <textarea
240
+ className="mt-2 min-h-[100px] w-full rounded border border-gray-200 p-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
241
+ placeholder="Add a comment..."
242
+ value={commentText}
243
+ onChange={async (e) => {
244
+ if (!editContext) return;
245
+ setCommentText(e.target.value);
246
+
247
+ setCommentsDebounced();
248
+ }}
249
+ autoFocus
250
+ />
251
+ {isSaving && (
252
+ <div className="mt-1 flex justify-end gap-2">
253
+ <div className="flex items-center gap-2 text-xs text-gray-500">
254
+ <ProgressSpinner className="h-4 w-4" />
255
+ Saving...
256
+ </div>
257
+ </div>
258
+ )}
259
+ {!isSaving && (
260
+ <div className="mt-1 flex justify-end gap-2">
261
+ <ActionButton
262
+ variant="outline"
263
+ onClick={() => {
264
+ if (comment.isNew) {
265
+ if (editContext?.setComments) {
266
+ editContext.setComments([
267
+ ...editContext.comments.filter((c) => c.id !== comment.id),
268
+ ]);
269
+ }
270
+ } else {
271
+ setIsEditing(false);
272
+ }
273
+ }}
274
+ >
275
+ Cancel
276
+ </ActionButton>
277
+
278
+ <ActionButton
279
+ className="tour-submit-comment-button"
280
+ onClick={async () => {
281
+ comment.text = commentText;
282
+ setIsSaving(true);
283
+ const item = await editContext?.itemsRepository?.getItem({
284
+ id: comment.itemId,
285
+ language: comment.language,
286
+ version: comment.version,
287
+ });
288
+
289
+ if (item) {
290
+ comment.fieldValue = item.fields.find(
291
+ (f) => f.id === comment.fieldId,
292
+ )?.rawValue;
293
+ }
294
+
295
+ await createOrUpdateComment(comment);
296
+
297
+ setIsSaving(false);
298
+ setIsEditing(false);
299
+ }}
300
+ >
301
+ {comment.isNew ? "Comment" : "Save"}
302
+ </ActionButton>
303
+ </div>
304
+ )}
305
+ {renderContextInfo()}
306
+ </div>
307
+ );
308
+ }
@@ -0,0 +1,125 @@
1
+ import { useEffect, useState } from "react";
2
+ import { useEditContext } from "../client/editContext";
3
+ import { Comment as CommentComponent } from "./Comment";
4
+ import { SuggestedEditComponent } from "./SuggestedEdit";
5
+ import { Comment as CommentType, SuggestedEdit } from "../../types";
6
+ import { SimpleToolbar } from "../ui/SimpleToolbar";
7
+ import { SimpleIconButton } from "../ui/SimpleIconButton";
8
+ import {
9
+ FileDiff,
10
+ SquarePen,
11
+ CheckCircle,
12
+ CheckCircle2,
13
+ MessageSquareMore,
14
+ } from "lucide-react";
15
+
16
+ // Define a union type for feedback items:
17
+ export type FeedbackItem = CommentType | SuggestedEdit;
18
+
19
+ export function Comments() {
20
+ const editContext = useEditContext();
21
+ const [feedbackItems, setFeedbackItems] = useState<FeedbackItem[]>([]);
22
+ const [hideAppliedSuggestions, setHideAppliedSuggestions] =
23
+ useState<boolean>(true);
24
+
25
+ useEffect(() => {
26
+ // Retrieve your list of comments (and ensure there's an array of suggested edits,
27
+ // for instance from your editContext)
28
+ const comments: CommentType[] = editContext?.comments || [];
29
+ const suggestedEdits: SuggestedEdit[] = editContext?.suggestedEdits || [];
30
+
31
+ // Filter out applied suggestions if hideAppliedSuggestions is true
32
+ const filteredSuggestedEdits = hideAppliedSuggestions
33
+ ? suggestedEdits.filter((edit) => edit.status !== "Applied")
34
+ : suggestedEdits;
35
+
36
+ const combined: FeedbackItem[] = [...comments, ...filteredSuggestedEdits];
37
+
38
+ // Sort by creation date. Adjust the comparison as needed if the date properties differ.
39
+ combined.sort(
40
+ (a, b) =>
41
+ new Date(b.created || "").getTime() -
42
+ new Date(a.created || "").getTime(),
43
+ );
44
+
45
+ setFeedbackItems(combined);
46
+ }, [editContext, hideAppliedSuggestions]);
47
+
48
+ return (
49
+ <div className="flex h-full flex-col">
50
+ <SimpleToolbar>
51
+ <SimpleIconButton
52
+ id="add-comment"
53
+ icon="pi pi-plus"
54
+ label="Add Comment"
55
+ onClick={() => {
56
+ editContext?.addComment();
57
+ }}
58
+ />
59
+ <SimpleIconButton
60
+ selected={editContext?.showComments}
61
+ icon={<MessageSquareMore size={16} className="p-0.5" />}
62
+ label="Show Comments"
63
+ onClick={() => {
64
+ editContext?.setShowComments((x) => !x);
65
+ }}
66
+ />
67
+ <SimpleIconButton
68
+ selected={editContext?.showSuggestedEdits}
69
+ icon={<SquarePen size={16} className="p-0.5" />}
70
+ label="Show Suggestions"
71
+ onClick={() => {
72
+ editContext?.setShowSuggestedEdits((x) => !x);
73
+ }}
74
+ />
75
+ {editContext?.showSuggestedEdits && (
76
+ <SimpleIconButton
77
+ selected={editContext?.showSuggestedEditsDiff}
78
+ icon={<FileDiff size={16} className="p-0.5" />}
79
+ label="Show Suggestions Diff"
80
+ onClick={() => {
81
+ editContext?.setShowSuggestedEditsDiff((x) => !x);
82
+ }}
83
+ />
84
+ )}
85
+ <SimpleIconButton
86
+ selected={hideAppliedSuggestions}
87
+ icon={
88
+ hideAppliedSuggestions ? (
89
+ <CheckCircle2 size={16} className="p-0.5" />
90
+ ) : (
91
+ <CheckCircle size={16} className="p-0.5" />
92
+ )
93
+ }
94
+ label={
95
+ hideAppliedSuggestions
96
+ ? "Show Applied Suggestions"
97
+ : "Hide Applied Suggestions"
98
+ }
99
+ onClick={() => {
100
+ setHideAppliedSuggestions((x) => !x);
101
+ }}
102
+ />
103
+ </SimpleToolbar>
104
+
105
+ <div className="flex-1 overflow-auto">
106
+ <div className="h-fill-available border-l border-gray-200 bg-gray-50 p-4">
107
+ {feedbackItems.map((item) => {
108
+ // Use a discriminator check. Here we assume suggested edits have the properties oldValue and newValue.
109
+ if ("oldValue" in item && "newValue" in item) {
110
+ return (
111
+ <SuggestedEditComponent
112
+ key={item.id}
113
+ edit={item as SuggestedEdit}
114
+ />
115
+ );
116
+ }
117
+ return (
118
+ <CommentComponent key={item.id} comment={item as CommentType} />
119
+ );
120
+ })}
121
+ </div>
122
+ </div>
123
+ </div>
124
+ );
125
+ }