@atlaskit/emoji 71.6.6 → 71.7.0

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 (55) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/afm-cc/tsconfig.json +3 -0
  3. package/afm-products/tsconfig.json +3 -0
  4. package/dist/cjs/api/ai/generateEmojiImage.js +101 -0
  5. package/dist/cjs/components/common/CreateEmojiWithRovo.compiled.css +12 -0
  6. package/dist/cjs/components/common/CreateEmojiWithRovo.js +150 -0
  7. package/dist/cjs/components/common/EmojiActions.js +6 -2
  8. package/dist/cjs/components/common/EmojiUploadPicker.js +30 -1
  9. package/dist/cjs/components/i18n.js +25 -0
  10. package/dist/cjs/components/picker/EmojiPickerComponent.compiled.css +3 -0
  11. package/dist/cjs/components/picker/EmojiPickerComponent.js +22 -3
  12. package/dist/cjs/components/picker/EmojiPickerList.js +6 -2
  13. package/dist/cjs/components/uploader/EmojiUploadComponent.js +5 -2
  14. package/dist/cjs/util/ai-emoji.js +72 -0
  15. package/dist/cjs/util/analytics/analytics.js +14 -2
  16. package/dist/cjs/util/analytics/index.js +18 -0
  17. package/dist/es2019/api/ai/generateEmojiImage.js +64 -0
  18. package/dist/es2019/components/common/CreateEmojiWithRovo.compiled.css +12 -0
  19. package/dist/es2019/components/common/CreateEmojiWithRovo.js +113 -0
  20. package/dist/es2019/components/common/EmojiActions.js +6 -2
  21. package/dist/es2019/components/common/EmojiUploadPicker.js +28 -1
  22. package/dist/es2019/components/i18n.js +25 -0
  23. package/dist/es2019/components/picker/EmojiPickerComponent.compiled.css +3 -0
  24. package/dist/es2019/components/picker/EmojiPickerComponent.js +22 -3
  25. package/dist/es2019/components/picker/EmojiPickerList.js +6 -2
  26. package/dist/es2019/components/uploader/EmojiUploadComponent.js +5 -2
  27. package/dist/es2019/util/ai-emoji.js +64 -0
  28. package/dist/es2019/util/analytics/analytics.js +5 -1
  29. package/dist/es2019/util/analytics/index.js +1 -1
  30. package/dist/esm/api/ai/generateEmojiImage.js +94 -0
  31. package/dist/esm/components/common/CreateEmojiWithRovo.compiled.css +12 -0
  32. package/dist/esm/components/common/CreateEmojiWithRovo.js +141 -0
  33. package/dist/esm/components/common/EmojiActions.js +6 -2
  34. package/dist/esm/components/common/EmojiUploadPicker.js +30 -1
  35. package/dist/esm/components/i18n.js +25 -0
  36. package/dist/esm/components/picker/EmojiPickerComponent.compiled.css +3 -0
  37. package/dist/esm/components/picker/EmojiPickerComponent.js +22 -3
  38. package/dist/esm/components/picker/EmojiPickerList.js +6 -2
  39. package/dist/esm/components/uploader/EmojiUploadComponent.js +5 -2
  40. package/dist/esm/util/ai-emoji.js +66 -0
  41. package/dist/esm/util/analytics/analytics.js +13 -1
  42. package/dist/esm/util/analytics/index.js +1 -1
  43. package/dist/types/api/ai/generateEmojiImage.d.ts +29 -0
  44. package/dist/types/components/common/CreateEmojiWithRovo.d.ts +24 -0
  45. package/dist/types/components/common/EmojiActions.d.ts +11 -0
  46. package/dist/types/components/common/EmojiUploadPicker.d.ts +9 -0
  47. package/dist/types/components/i18n.d.ts +25 -0
  48. package/dist/types/components/picker/EmojiPicker.d.ts +6 -0
  49. package/dist/types/components/picker/EmojiPickerComponent.d.ts +6 -0
  50. package/dist/types/components/picker/EmojiPickerList.d.ts +5 -0
  51. package/dist/types/components/uploader/EmojiUploadComponent.d.ts +2 -0
  52. package/dist/types/util/ai-emoji.d.ts +41 -0
  53. package/dist/types/util/analytics/analytics.d.ts +7 -0
  54. package/dist/types/util/analytics/index.d.ts +1 -1
  55. package/package.json +7 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @atlaskit/emoji
2
2
 
3
+ ## 71.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`cdddb84518902`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/cdddb84518902) -
8
+ Add a "Create an emoji with Rovo" section to the emoji picker's "Add your own emoji" flow, behind
9
+ the `confluence_ai_generated_emojis` experiment.
10
+
11
+ When enabled (and a page `contentId` is supplied), a description input + generate button appear
12
+ above the Emoji name field. Generating produces an emoji-style image via the Confluence header
13
+ image backend (Gemini 2.5 Flash Image, 1:1 aspect ratio). The generated image flows into the
14
+ existing upload form — reusing the same preview, Emoji name field (auto-populated with a slugified
15
+ shortname) and "Add emoji" button — and is uploaded through the existing `SiteEmojiResource` path.
16
+ - New optional `EmojiPicker` `contentId` prop threads the current page content id down to enable
17
+ the AI section.
18
+ - New analytics events for AI emoji generation (started/completed/failed).
19
+ - Register the `confluence_ai_generated_emojis` experiment in `@atlaskit/tmp-editor-statsig` so it
20
+ can be checked via `expValEquals`.
21
+
22
+ When the experiment is off (or no `contentId` is supplied), only the existing manual upload flow
23
+ is shown.
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies
28
+
3
29
  ## 71.6.6
4
30
 
5
31
  ### Patch Changes
@@ -52,6 +52,9 @@
52
52
  {
53
53
  "path": "../../../design-system/image/afm-cc/tsconfig.json"
54
54
  },
55
+ {
56
+ "path": "../../../design-system/logo/afm-cc/tsconfig.json"
57
+ },
55
58
  {
56
59
  "path": "../../../media/media-client/afm-cc/tsconfig.json"
57
60
  },
@@ -52,6 +52,9 @@
52
52
  {
53
53
  "path": "../../../design-system/image/afm-products/tsconfig.json"
54
54
  },
55
+ {
56
+ "path": "../../../design-system/logo/afm-products/tsconfig.json"
57
+ },
55
58
  {
56
59
  "path": "../../../media/media-client/afm-products/tsconfig.json"
57
60
  },
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.generateEmojiImage = void 0;
8
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
10
+ var _aiEmoji = require("../../util/ai-emoji");
11
+ var _logger = _interopRequireDefault(require("../../util/logger"));
12
+ /**
13
+ * Calls the Confluence "header image" AI backend to generate an emoji-style
14
+ * image (base64) from a text description.
15
+ */
16
+
17
+ var HEADER_IMAGE_GENERATION_ENDPOINT = '/gateway/api/assist/api/ai/v2/ai-feature/confluence-header-image-generation';
18
+
19
+ /** Emojis are square, so we always request a 1:1 image from the BE. */
20
+ var EMOJI_ASPECT_RATIO = '1:1';
21
+
22
+ /** Shape of the `ai_feature_output` envelope returned by the BE. */
23
+
24
+ /**
25
+ * Generate an emoji-style image and return its base64 image data.
26
+ *
27
+ * @throws Error if the request fails, the BE returns a body-level error, or no
28
+ * image data is returned.
29
+ */
30
+ var generateEmojiImage = exports.generateEmojiImage = /*#__PURE__*/function () {
31
+ var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(_ref) {
32
+ var contentId, prompt, wrappedPrompt, response, json, output;
33
+ return _regenerator.default.wrap(function (_context) {
34
+ while (1) switch (_context.prev = _context.next) {
35
+ case 0:
36
+ contentId = _ref.contentId, prompt = _ref.prompt;
37
+ wrappedPrompt = (0, _aiEmoji.wrapEmojiPrompt)(prompt);
38
+ _context.next = 1;
39
+ return fetch(HEADER_IMAGE_GENERATION_ENDPOINT, {
40
+ method: 'POST',
41
+ headers: {
42
+ 'content-type': 'application/json;charset=UTF-8',
43
+ 'X-Experience-Id': 'confluence-ai-first-creation',
44
+ 'X-Product': 'confluence'
45
+ },
46
+ body: JSON.stringify({
47
+ ai_feature_input: {
48
+ adfContent: wrappedPrompt,
49
+ contentId: contentId,
50
+ useRawPrompt: true,
51
+ // Emojis are square — request a 1:1 image from the BE.
52
+ aspectRatio: EMOJI_ASPECT_RATIO
53
+ }
54
+ })
55
+ });
56
+ case 1:
57
+ response = _context.sent;
58
+ if (response.ok) {
59
+ _context.next = 2;
60
+ break;
61
+ }
62
+ (0, _logger.default)('generateEmojiImage failed', response.status);
63
+ throw new Error("Emoji image generation failed with status ".concat(response.status));
64
+ case 2:
65
+ _context.next = 3;
66
+ return response.json();
67
+ case 3:
68
+ json = _context.sent;
69
+ output = json === null || json === void 0 ? void 0 : json.ai_feature_output;
70
+ if (output) {
71
+ _context.next = 4;
72
+ break;
73
+ }
74
+ throw new Error('Emoji image generation returned no content');
75
+ case 4:
76
+ if (!output.error) {
77
+ _context.next = 5;
78
+ break;
79
+ }
80
+ throw new Error(output.error.error_message || output.error.error_reason || 'Emoji image generation failed');
81
+ case 5:
82
+ if (output.imageData) {
83
+ _context.next = 6;
84
+ break;
85
+ }
86
+ throw new Error('Emoji image generation did not return image data');
87
+ case 6:
88
+ return _context.abrupt("return", {
89
+ imageData: output.imageData,
90
+ mediaFileId: output.mediaFileId
91
+ });
92
+ case 7:
93
+ case "end":
94
+ return _context.stop();
95
+ }
96
+ }, _callee);
97
+ }));
98
+ return function generateEmojiImage(_x) {
99
+ return _ref2.apply(this, arguments);
100
+ };
101
+ }();
@@ -0,0 +1,12 @@
1
+
2
+ ._zulp12x7{gap:var(--ds-space-075,6px)}
3
+ ._zulpu2gc{gap:var(--ds-space-100,8px)}
4
+ ._x3doia51{border-top:var(--ds-border-width,1px) solid var(--ds-border,#0b120e24)}._16jlkb7n{flex-grow:1}
5
+ ._19pku2gc{margin-top:var(--ds-space-100,8px)}
6
+ ._1e0c1txw{display:flex}
7
+ ._1o9zkb7n{flex-shrink:1}
8
+ ._2lx21bp4{flex-direction:column}
9
+ ._4cvr1h6o{align-items:center}
10
+ ._4cvr1y6m{align-items:flex-start}
11
+ ._ca0qutpp{padding-top:var(--ds-space-150,9pt)}
12
+ ._i0dlf1ug{flex-basis:0%}
@@ -0,0 +1,150 @@
1
+ /* CreateEmojiWithRovo.tsx generated by @compiled/babel-plugin v0.39.1 */
2
+ "use strict";
3
+
4
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
5
+ var _typeof = require("@babel/runtime/helpers/typeof");
6
+ Object.defineProperty(exports, "__esModule", {
7
+ value: true
8
+ });
9
+ exports.default = exports.createEmojiWithRovoTestId = exports.createEmojiWithRovoPromptTestId = exports.createEmojiWithRovoGenerateTestId = void 0;
10
+ require("./CreateEmojiWithRovo.compiled.css");
11
+ var _react = _interopRequireWildcard(require("react"));
12
+ var React = _react;
13
+ var _runtime = require("@compiled/react/runtime");
14
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
15
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
16
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
17
+ var _reactIntl = require("react-intl");
18
+ var _new = require("@atlaskit/button/new");
19
+ var _textfield = _interopRequireDefault(require("@atlaskit/textfield"));
20
+ var _arrowUp = _interopRequireDefault(require("@atlaskit/icon/core/arrow-up"));
21
+ var _logo = require("@atlaskit/logo");
22
+ var _compiled = require("@atlaskit/primitives/compiled");
23
+ var _generateEmojiImage = require("../../api/ai/generateEmojiImage");
24
+ var _aiEmoji = require("../../util/ai-emoji");
25
+ var _analytics = require("../../util/analytics");
26
+ var _i18n = require("../i18n");
27
+ var _EmojiErrorMessage = _interopRequireDefault(require("./EmojiErrorMessage"));
28
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t2 in e) "default" !== _t2 && {}.hasOwnProperty.call(e, _t2) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t2)) && (i.get || i.set) ? o(f, _t2, i) : f[_t2] = e[_t2]); return f; })(e, t); }
29
+ var createEmojiWithRovoTestId = exports.createEmojiWithRovoTestId = 'create-emoji-with-rovo';
30
+ var createEmojiWithRovoPromptTestId = exports.createEmojiWithRovoPromptTestId = 'create-emoji-with-rovo-prompt';
31
+ var createEmojiWithRovoGenerateTestId = exports.createEmojiWithRovoGenerateTestId = 'create-emoji-with-rovo-generate';
32
+ var sectionStyles = null;
33
+ var headerStyles = null;
34
+ var promptRowStyles = null;
35
+ var promptInputStyles = null;
36
+ var CreateEmojiWithRovo = function CreateEmojiWithRovo(props) {
37
+ var contentId = props.contentId,
38
+ fireAnalytics = props.fireAnalytics,
39
+ onEmojiGenerated = props.onEmojiGenerated;
40
+ var _useIntl = (0, _reactIntl.useIntl)(),
41
+ formatMessage = _useIntl.formatMessage;
42
+ var _useState = (0, _react.useState)(''),
43
+ _useState2 = (0, _slicedToArray2.default)(_useState, 2),
44
+ prompt = _useState2[0],
45
+ setPrompt = _useState2[1];
46
+ var _useState3 = (0, _react.useState)(false),
47
+ _useState4 = (0, _slicedToArray2.default)(_useState3, 2),
48
+ isGenerating = _useState4[0],
49
+ setIsGenerating = _useState4[1];
50
+ var _useState5 = (0, _react.useState)(false),
51
+ _useState6 = (0, _slicedToArray2.default)(_useState5, 2),
52
+ hasError = _useState6[0],
53
+ setHasError = _useState6[1];
54
+ var onPromptChange = (0, _react.useCallback)(function (event) {
55
+ setPrompt(event.target.value);
56
+ // Clear any previous error as soon as the user edits the prompt to retry.
57
+ setHasError(false);
58
+ }, []);
59
+ var onGenerate = (0, _react.useCallback)( /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
60
+ var trimmedPrompt, startTime, _yield$generateEmojiI, imageData, dataURL, suggestedName, _t;
61
+ return _regenerator.default.wrap(function (_context) {
62
+ while (1) switch (_context.prev = _context.next) {
63
+ case 0:
64
+ trimmedPrompt = prompt.trim();
65
+ if (!(!trimmedPrompt || isGenerating)) {
66
+ _context.next = 1;
67
+ break;
68
+ }
69
+ return _context.abrupt("return");
70
+ case 1:
71
+ setHasError(false);
72
+ setIsGenerating(true);
73
+ fireAnalytics === null || fireAnalytics === void 0 || fireAnalytics((0, _analytics.aiGenerationStartedEvent)({
74
+ promptLength: trimmedPrompt.length
75
+ }));
76
+ startTime = Date.now();
77
+ _context.prev = 2;
78
+ _context.next = 3;
79
+ return (0, _generateEmojiImage.generateEmojiImage)({
80
+ contentId: contentId,
81
+ prompt: trimmedPrompt
82
+ });
83
+ case 3:
84
+ _yield$generateEmojiI = _context.sent;
85
+ imageData = _yield$generateEmojiI.imageData;
86
+ dataURL = "data:image/png;base64,".concat(imageData);
87
+ suggestedName = (0, _aiEmoji.slugifyPrompt)(trimmedPrompt);
88
+ fireAnalytics === null || fireAnalytics === void 0 || fireAnalytics((0, _analytics.aiGenerationCompletedEvent)({
89
+ duration: Date.now() - startTime
90
+ }));
91
+ // Hand the generated image to the parent upload form.
92
+ onEmojiGenerated(dataURL, suggestedName);
93
+ _context.next = 5;
94
+ break;
95
+ case 4:
96
+ _context.prev = 4;
97
+ _t = _context["catch"](2);
98
+ setHasError(true);
99
+ fireAnalytics === null || fireAnalytics === void 0 || fireAnalytics((0, _analytics.aiGenerationFailedEvent)({
100
+ errorType: _t instanceof Error ? _t.message : 'generation_failed'
101
+ }));
102
+ case 5:
103
+ _context.prev = 5;
104
+ setIsGenerating(false);
105
+ return _context.finish(5);
106
+ case 6:
107
+ case "end":
108
+ return _context.stop();
109
+ }
110
+ }, _callee, null, [[2, 4, 5, 6]]);
111
+ })), [contentId, fireAnalytics, isGenerating, onEmojiGenerated, prompt]);
112
+ var generateDisabled = !prompt.trim() || isGenerating;
113
+ return /*#__PURE__*/React.createElement("div", {
114
+ "data-testid": createEmojiWithRovoTestId,
115
+ className: (0, _runtime.ax)(["_zulpu2gc _x3doia51 _1e0c1txw _2lx21bp4 _ca0qutpp _19pku2gc"])
116
+ }, /*#__PURE__*/React.createElement("div", {
117
+ className: (0, _runtime.ax)(["_zulp12x7 _1e0c1txw _4cvr1h6o"])
118
+ }, /*#__PURE__*/React.createElement(_logo.RovoIcon, {
119
+ appearance: "brand",
120
+ size: "xsmall",
121
+ label: formatMessage(_i18n.messages.createEmojiWithRovoTitle)
122
+ }), /*#__PURE__*/React.createElement(_compiled.Text, {
123
+ size: "medium",
124
+ weight: "bold"
125
+ }, /*#__PURE__*/React.createElement(_reactIntl.FormattedMessage, _i18n.messages.createEmojiWithRovoTitle))), /*#__PURE__*/React.createElement("div", {
126
+ className: (0, _runtime.ax)(["_zulpu2gc _1e0c1txw _4cvr1y6m"])
127
+ }, /*#__PURE__*/React.createElement("span", {
128
+ className: (0, _runtime.ax)(["_16jlkb7n _1o9zkb7n _i0dlf1ug"])
129
+ }, /*#__PURE__*/React.createElement(_textfield.default, {
130
+ value: prompt,
131
+ onChange: onPromptChange,
132
+ placeholder: formatMessage(_i18n.messages.createEmojiWithRovoPromptPlaceholder),
133
+ "aria-label": formatMessage(_i18n.messages.createEmojiWithRovoPromptAriaLabel),
134
+ isCompact: true,
135
+ isDisabled: isGenerating,
136
+ testId: createEmojiWithRovoPromptTestId
137
+ })), /*#__PURE__*/React.createElement(_new.IconButton, {
138
+ appearance: "primary",
139
+ icon: _arrowUp.default,
140
+ label: formatMessage(_i18n.messages.createEmojiWithRovoGenerateLabel),
141
+ isDisabled: generateDisabled,
142
+ isLoading: isGenerating,
143
+ onClick: onGenerate,
144
+ testId: createEmojiWithRovoGenerateTestId
145
+ })), hasError && /*#__PURE__*/React.createElement(_EmojiErrorMessage.default, {
146
+ errorStyle: "chooseFile",
147
+ message: /*#__PURE__*/React.createElement(_reactIntl.FormattedMessage, _i18n.messages.createEmojiWithRovoError)
148
+ }));
149
+ };
150
+ var _default = exports.default = CreateEmojiWithRovo;
@@ -326,7 +326,9 @@ var EmojiActions = exports.EmojiActions = function EmojiActions(props) {
326
326
  onUploadEmoji: onUploadEmoji,
327
327
  onFileChooserClicked: onFileChooserClicked,
328
328
  errorMessage: uploadErrorMessage,
329
- initialUploadName: initialUploadName
329
+ initialUploadName: initialUploadName,
330
+ contentId: props.contentId,
331
+ fireAnalytics: props.fireAnalytics
330
332
  })) : /*#__PURE__*/React.createElement("div", {
331
333
  className: (0, _runtime.ax)(["_16jlidpf _1o9zidpf _i0dl1wug _n7zl1uh4 _16qsjgpa"])
332
334
  }, /*#__PURE__*/React.createElement(_EmojiUploadPicker.default, {
@@ -334,7 +336,9 @@ var EmojiActions = exports.EmojiActions = function EmojiActions(props) {
334
336
  onUploadEmoji: onUploadEmoji,
335
337
  onFileChooserClicked: onFileChooserClicked,
336
338
  errorMessage: uploadErrorMessage,
337
- initialUploadName: initialUploadName
339
+ initialUploadName: initialUploadName,
340
+ contentId: props.contentId,
341
+ fireAnalytics: props.fireAnalytics
338
342
  }));
339
343
  }
340
344
  if (emojiToDelete) {
@@ -21,6 +21,7 @@ var _standardButton = _interopRequireDefault(require("@atlaskit/button/standard-
21
21
  var _compiled = require("@atlaskit/primitives/compiled");
22
22
  var _reactFocusLock = _interopRequireDefault(require("react-focus-lock"));
23
23
  var ImageUtil = _interopRequireWildcard(require("../../util/image"));
24
+ var _CreateEmojiWithRovo = _interopRequireDefault(require("./CreateEmojiWithRovo"));
24
25
  var _logger = _interopRequireDefault(require("../../util/logger"));
25
26
  var _i18n = require("../i18n");
26
27
  var _EmojiErrorMessage = _interopRequireDefault(require("./EmojiErrorMessage"));
@@ -29,6 +30,7 @@ var _FileChooser = _interopRequireDefault(require("./FileChooser"));
29
30
  var _internalTypes = require("./internal-types");
30
31
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
31
32
  var _featureGateJsClient = _interopRequireDefault(require("@atlaskit/feature-gate-js-client"));
33
+ var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
32
34
  var _new = _interopRequireDefault(require("@atlaskit/button/new"));
33
35
  var _browserApis = require("@atlaskit/browser-apis");
34
36
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t2 in e) "default" !== _t2 && {}.hasOwnProperty.call(e, _t2) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t2)) && (i.get || i.set) ? o(f, _t2, i) : f[_t2] = e[_t2]); return f; })(e, t); }
@@ -99,6 +101,7 @@ var ChooseEmojiFile = /*#__PURE__*/(0, _react.memo)(function (props) {
99
101
  nameErrorMessage = props.nameErrorMessage,
100
102
  previewImage = props.previewImage,
101
103
  uploadStatus = props.uploadStatus,
104
+ aiSection = props.aiSection,
102
105
  intl = props.intl;
103
106
  var formatMessage = intl.formatMessage;
104
107
  var disableChooser = !name;
@@ -147,7 +150,7 @@ var ChooseEmojiFile = /*#__PURE__*/(0, _react.memo)(function (props) {
147
150
  }, errorMessage && /*#__PURE__*/React.createElement(_EmojiErrorMessage.default, {
148
151
  errorStyle: "chooseFile",
149
152
  message: errorMessage
150
- }))), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("label", {
153
+ }))), aiSection, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("label", {
151
154
  htmlFor: "new-emoji-name-input",
152
155
  className: (0, _runtime.ax)(["_1w90azsu", "_11c8wadc _k48p1pd9"])
153
156
  }, /*#__PURE__*/React.createElement(_reactIntl.FormattedMessage, _i18n.messages.emojiNameLabel)), /*#__PURE__*/React.createElement(_textfield.default, {
@@ -245,6 +248,8 @@ var EmojiUploadPicker = function EmojiUploadPicker(props) {
245
248
  onUploadCancelled = props.onUploadCancelled,
246
249
  _props$disableFocusLo = props.disableFocusLock,
247
250
  disableFocusLock = _props$disableFocusLo === void 0 ? false : _props$disableFocusLo,
251
+ contentId = props.contentId,
252
+ fireAnalytics = props.fireAnalytics,
248
253
  intl = props.intl;
249
254
  var _useState = (0, _react.useState)(errorMessage ? _internalTypes.UploadStatus.Error : _internalTypes.UploadStatus.Waiting),
250
255
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
@@ -328,6 +333,18 @@ var EmojiUploadPicker = function EmojiUploadPicker(props) {
328
333
  var cancelChooseFile = (0, _react.useCallback)(function () {
329
334
  setPreviewImage(undefined);
330
335
  }, []);
336
+
337
+ // When the Rovo section generates an image, feed it into the existing upload
338
+ // form (same preview, name field and "Add emoji" button as a manual upload).
339
+ var onEmojiGenerated = (0, _react.useCallback)(function (dataURL, suggestedName) {
340
+ setFilename('rovo-emoji.png');
341
+ setPreviewImage(dataURL);
342
+ setChooseEmojiErrorMessage(undefined);
343
+ // Only auto-populate the name if the user hasn't already typed one.
344
+ setName(function (current) {
345
+ return current || sanitizeName(suggestedName);
346
+ });
347
+ }, []);
331
348
  var errorOnUpload = (0, _react.useCallback)(function (event) {
332
349
  (0, _logger.default)('File load error: ', event);
333
350
  setChooseEmojiErrorMessage( /*#__PURE__*/React.createElement(_reactIntl.FormattedMessage, _i18n.messages.emojiUploadFailed));
@@ -411,6 +428,17 @@ var EmojiUploadPicker = function EmojiUploadPicker(props) {
411
428
  onFileChooserClicked && onFileChooserClicked();
412
429
  };
413
430
  var isDuplicateNameError = errorMessage !== null && errorMessage !== undefined && isRefreshEmojiPickerEnabled();
431
+
432
+ // "Create an emoji with Rovo" AI generation section. Only rendered when the
433
+ // experiment is on AND a page content id is available (the image generation
434
+ // backend requires it). It is rendered inside ChooseEmojiFile (between the
435
+ // drop area and the Emoji name field) so the generated image reuses the
436
+ // single shared name field and "Add emoji" button.
437
+ var aiSection = contentId && (0, _expValEquals.expValEquals)('confluence_ai_generated_emojis', 'isEnabled', true) ? /*#__PURE__*/React.createElement(_CreateEmojiWithRovo.default, {
438
+ contentId: contentId,
439
+ fireAnalytics: fireAnalytics,
440
+ onEmojiGenerated: onEmojiGenerated
441
+ }) : null;
414
442
  var content = name && previewImage && !isRefreshEmojiPickerEnabled() ? /*#__PURE__*/React.createElement(_EmojiUploadPreview.default, {
415
443
  errorMessage: errorMessage,
416
444
  name: name,
@@ -429,6 +457,7 @@ var EmojiUploadPicker = function EmojiUploadPicker(props) {
429
457
  uploadStatus: uploadStatus,
430
458
  errorMessage: chooseEmojiErrorMessage,
431
459
  nameErrorMessage: isDuplicateNameError ? errorMessage : undefined,
460
+ aiSection: aiSection,
432
461
  intl: intl
433
462
  });
434
463
  return disableFocusLock || isRefreshEmojiPickerEnabled() ? content : /*#__PURE__*/React.createElement(_reactFocusLock.default, {
@@ -106,6 +106,31 @@ var messages = exports.messages = (0, _reactIntl.defineMessages)({
106
106
  defaultMessage: 'Add emoji',
107
107
  description: 'Label for the submit button in the custom emoji upload panel that saves the new emoji to the workspace.'
108
108
  },
109
+ createEmojiWithRovoTitle: {
110
+ id: 'fabric.emoji.ai.create.title',
111
+ defaultMessage: 'Create an emoji with Rovo',
112
+ description: 'Section heading for the AI emoji generation area where users describe an emoji and generate it with AI.'
113
+ },
114
+ createEmojiWithRovoPromptPlaceholder: {
115
+ id: 'fabric.emoji.ai.prompt.placeholder',
116
+ defaultMessage: 'Describe your emoji...',
117
+ description: 'Placeholder text for the input where the user describes the emoji they want the AI to generate.'
118
+ },
119
+ createEmojiWithRovoPromptAriaLabel: {
120
+ id: 'fabric.emoji.ai.prompt.ariaLabel',
121
+ defaultMessage: 'Describe the emoji you want to generate',
122
+ description: 'Accessible label for the AI emoji description input field.'
123
+ },
124
+ createEmojiWithRovoGenerateLabel: {
125
+ id: 'fabric.emoji.ai.generate.label',
126
+ defaultMessage: 'Generate',
127
+ description: 'Label for the button that triggers AI generation of the described emoji.'
128
+ },
129
+ createEmojiWithRovoError: {
130
+ id: 'fabric.emoji.ai.error',
131
+ defaultMessage: 'Something went wrong. Try a different description.',
132
+ description: 'Inline error message shown when AI emoji generation or upload fails, prompting the user to try again.'
133
+ },
109
134
  retryLabel: {
110
135
  id: 'fabric.emoji.retry.label',
111
136
  defaultMessage: 'Retry',
@@ -4,6 +4,7 @@
4
4
  ._2rkofajl{border-radius:var(--ds-radius-small,3px)}._16jlkb7n{flex-grow:1}
5
5
  ._16qs130s{box-shadow:var(--ds-shadow-overlay,0 8px 9pt #1e1f2126,0 0 1px #1e1f214f)}
6
6
  ._18m915vq{overflow-y:hidden}
7
+ ._18m91wug{overflow-y:auto}
7
8
  ._1bah1yb4{justify-content:space-between}
8
9
  ._1bsb10mj{width:var(--_gsvyy7)}
9
10
  ._1e0c1txw{display:flex}
@@ -27,11 +28,13 @@
27
28
  ._4t3ibqjm{height:310px}
28
29
  ._4t3iihnn{height:434px}
29
30
  ._4t3iixjv{height:375px}
31
+ ._4t3ikbql{height:450px}
30
32
  ._4t3iqbeb{height:339px}
31
33
  ._4t3iuxo9{height:var(--_19dn98e)}
32
34
  ._4t3ivixp{height:349px}
33
35
  ._4t3ixt2k{height:509px}
34
36
  ._bfhk1bhr{background-color:var(--ds-surface-overlay,#fff)}
35
37
  ._c71l1y6z{max-height:calc(80vh - 86px)}
38
+ ._c71l8k0t{max-height:calc(100vh - 86px)}
36
39
  ._i0dlf1ug{flex-basis:0%}
37
40
  ._kqswh2mm{position:relative}
@@ -41,6 +41,7 @@ var _useIsMounted = require("../../hooks/useIsMounted");
41
41
  var _i18n = require("../i18n");
42
42
  var _productivityColors = require("../../util/productivity-colors");
43
43
  var _hiddenEmojis = require("../../util/hidden-emojis");
44
+ var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
44
45
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
45
46
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
46
47
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -64,6 +65,17 @@ var heightOffset = 80;
64
65
  var emojiPicker = null;
65
66
  var emojiPickerNew = null;
66
67
  var emojiPickerWrapper = null;
68
+
69
+ // When the "Create an emoji with Rovo" section is shown in the upload panel the
70
+ // picker needs more vertical room than the fixed upload height. Make it tall
71
+ // enough to fit the whole section without scrolling, but still let it scroll as
72
+ // a safety net on very short viewports.
73
+ var emojiPickerWrapperScrollable = null;
74
+
75
+ // When the AI emoji experiment is on, grow the upload panel from its default
76
+ // height to 450px so the manual upload form + the slim "Create an emoji with
77
+ // Rovo" section are both visible without scrolling.
78
+ var withAiUploadHeight = null;
67
79
  var withPreviewHeight = {
68
80
  small: "_4t3ivixp _1tkegx0z",
69
81
  medium: "_4t3i2300 _1tke5x59",
@@ -96,6 +108,7 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
96
108
  onPickerRef = _ref.onPickerRef,
97
109
  hideToneSelector = _ref.hideToneSelector,
98
110
  createAnalyticsEvent = _ref.createAnalyticsEvent,
111
+ contentId = _ref.contentId,
99
112
  _ref$size = _ref.size,
100
113
  size = _ref$size === void 0 ? _constants.defaultEmojiPickerSize : _ref$size;
101
114
  var _useIntl = (0, _reactIntl.useIntl)(),
@@ -669,13 +682,17 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
669
682
  var showPreview = isRefreshEmojiPickerEnabled() ? !uploading : selectedEmoji && !uploading;
670
683
  var shouldRenderFooter = showPreview && !(emojiToDelete && isRefreshEmojiPickerEnabled()) && !(query && filteredEmojis.length === 0 && isRefreshEmojiPickerEnabled()) && (Boolean(selectedEmoji) || uploadEnabled || !isRefreshEmojiPickerEnabled());
671
684
  var useFooterSpaceForList = showPreview && !selectedEmoji && !uploadEnabled && !emojiToDelete && !(query && filteredEmojis.length === 0) && isRefreshEmojiPickerEnabled();
685
+
686
+ // When the AI emoji section is shown in the upload panel, grow the picker so
687
+ // the section isn't clipped by the fixed upload height.
688
+ var showAiUpload = uploading && !!contentId && (0, _expValEquals.expValEquals)('confluence_ai_generated_emojis', 'isEnabled', true);
672
689
  return /*#__PURE__*/React.createElement("div", {
673
690
  ref: setPickerRef,
674
691
  "data-emoji-picker-container": true,
675
692
  role: "dialog",
676
693
  "aria-label": formatMessage(_i18n.messages.emojiPickerTitle),
677
694
  "aria-modal": true,
678
- className: (0, _runtime.ax)([isRefreshEmojiPickerEnabled() ? "_19itahnd _2rko1mok _1e0c1txw _2lx21bp4 _1bah1yb4 _bfhk1bhr _16qs130s _4t3iuxo9 _1bsb10mj _1ul910mj _c71l1y6z _kqswh2mm" : "_19itahnd _2rkofajl _1e0c1txw _2lx21bp4 _1bah1yb4 _bfhk1bhr _16qs130s _4t3iuxo9 _1bsb10mj _1ul910mj _c71l1y6z _kqswh2mm", !!emojiToDelete && isRefreshEmojiPickerEnabled() ? withDeleteRefreshHeight[size] : uploading && isRefreshEmojiPickerEnabled() ? withUploadRefreshHeight[size] : query && filteredEmojis.length === 0 && isRefreshEmojiPickerEnabled() ? withNoResultsRefreshHeight[size] : showPreview ? withPreviewHeight[size] : withoutPreviewHeight[size]]),
695
+ className: (0, _runtime.ax)([isRefreshEmojiPickerEnabled() ? "_19itahnd _2rko1mok _1e0c1txw _2lx21bp4 _1bah1yb4 _bfhk1bhr _16qs130s _4t3iuxo9 _1bsb10mj _1ul910mj _c71l1y6z _kqswh2mm" : "_19itahnd _2rkofajl _1e0c1txw _2lx21bp4 _1bah1yb4 _bfhk1bhr _16qs130s _4t3iuxo9 _1bsb10mj _1ul910mj _c71l1y6z _kqswh2mm", !!emojiToDelete && isRefreshEmojiPickerEnabled() ? withDeleteRefreshHeight[size] : uploading && isRefreshEmojiPickerEnabled() ? withUploadRefreshHeight[size] : query && filteredEmojis.length === 0 && isRefreshEmojiPickerEnabled() ? withNoResultsRefreshHeight[size] : showPreview ? withPreviewHeight[size] : withoutPreviewHeight[size], showAiUpload && "_4t3ikbql _c71l8k0t"]),
679
696
  style: {
680
697
  "--_19dn98e": (0, _runtime.ix)("".concat(emojiPickerHeight, "px")),
681
698
  "--_gsvyy7": (0, _runtime.ix)("".concat(emojiPickerWidth, "px"))
@@ -685,7 +702,7 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
685
702
  onKeyPress: suppressKeyPress,
686
703
  onKeyUp: suppressKeyPress,
687
704
  onKeyDown: suppressKeyPress,
688
- className: (0, _runtime.ax)(["_16jlkb7n _1o9zkb7n _i0dlf1ug _1reo15vq _18m915vq _1e0c1txw _2lx21bp4 _1bah1yb4 _1tkeidpf"])
705
+ className: (0, _runtime.ax)(["_16jlkb7n _1o9zkb7n _i0dlf1ug _1reo15vq _18m915vq _1e0c1txw _2lx21bp4 _1bah1yb4 _1tkeidpf", showAiUpload && "_1reo15vq _18m91wug"])
689
706
  }, /*#__PURE__*/React.createElement(_CategorySelector.default, {
690
707
  activeCategoryId: (uploading || emojiToDelete) && isRefreshEmojiPickerEnabled() ? null : activeCategory,
691
708
  dynamicCategories: dynamicCategories,
@@ -722,7 +739,9 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
722
739
  onOpenUpload: onOpenUpload,
723
740
  size: size,
724
741
  activeCategoryId: activeCategory,
725
- useFooterSpaceForList: useFooterSpaceForList
742
+ useFooterSpaceForList: useFooterSpaceForList,
743
+ contentId: contentId,
744
+ fireAnalytics: fireAnalytics
726
745
  }), shouldRenderFooter && /*#__PURE__*/React.createElement(_EmojiPickerFooter.default, {
727
746
  selectedEmoji: selectedEmoji,
728
747
  uploadEnabled: uploadEnabled,
@@ -94,7 +94,9 @@ var EmojiPickerVirtualListInternal = exports.EmojiPickerVirtualListInternal = /*
94
94
  onProductivityColorSelected = props.onProductivityColorSelected,
95
95
  activeCategoryId = props.activeCategoryId,
96
96
  selectedProductivityColor = props.selectedProductivityColor,
97
- useFooterSpaceForList = props.useFooterSpaceForList;
97
+ useFooterSpaceForList = props.useFooterSpaceForList,
98
+ contentId = props.contentId,
99
+ fireAnalytics = props.fireAnalytics;
98
100
  var _useIntl = (0, _reactIntl.useIntl)(),
99
101
  formatMessage = _useIntl.formatMessage;
100
102
  var listRef = (0, _react.useRef)(null);
@@ -513,7 +515,9 @@ var EmojiPickerVirtualListInternal = exports.EmojiPickerVirtualListInternal = /*
513
515
  onProductivityColorSelected: isTeamojiExperimentEnabled ? onProductivityColorSelected : undefined,
514
516
  query: query,
515
517
  onChange: onSearch,
516
- resultsCount: visibleEmojis.length
518
+ resultsCount: visibleEmojis.length,
519
+ contentId: contentId,
520
+ fireAnalytics: fireAnalytics
517
521
  }), /*#__PURE__*/_react.default.createElement(_EmojiPickerListContext.EmojiPickerListContextProvider, {
518
522
  initialEmojisFocus: {
519
523
  rowIndex: 1,
@@ -29,7 +29,8 @@ var EmojiUploadComponent = function EmojiUploadComponent(props) {
29
29
  var emojiProvider = props.emojiProvider,
30
30
  createAnalyticsEvent = props.createAnalyticsEvent,
31
31
  onUploaderRef = props.onUploaderRef,
32
- disableFocusLock = props.disableFocusLock;
32
+ disableFocusLock = props.disableFocusLock,
33
+ contentId = props.contentId;
33
34
  var _useState = (0, _react.useState)(),
34
35
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
35
36
  uploadErrorMessage = _useState2[0],
@@ -131,7 +132,9 @@ var EmojiUploadComponent = function EmojiUploadComponent(props) {
131
132
  onUploadCancelled: onUploadCancelled,
132
133
  onUploadEmoji: onUploadEmoji,
133
134
  errorMessage: uploadErrorMessage ? /*#__PURE__*/React.createElement(_reactIntl.FormattedMessage, uploadErrorMessage) : null,
134
- disableFocusLock: disableFocusLock
135
+ disableFocusLock: disableFocusLock,
136
+ contentId: contentId,
137
+ fireAnalytics: fireAnalytics
135
138
  })));
136
139
  };
137
140
  var _default_1 = /*#__PURE__*/(0, _react.memo)(EmojiUploadComponent);