@atlaskit/renderer 133.0.0 → 133.2.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 (39) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/cjs/analytics/enums.js +1 -0
  3. package/dist/cjs/react/index.js +3 -0
  4. package/dist/cjs/react/nodes/codeBlock/codeBlock.js +5 -1
  5. package/dist/cjs/react/nodes/codeBlock/components/codeBlockButtonContainer.js +7 -1
  6. package/dist/cjs/react/nodes/codeBlock/components/codeBlockContainer.js +4 -0
  7. package/dist/cjs/react/nodes/codeBlock/components/codeBlockDownloadButton.js +133 -0
  8. package/dist/cjs/react/nodes/codeBlock/windowedCodeBlock.js +4 -1
  9. package/dist/cjs/react/nodes/emoji.js +1 -0
  10. package/dist/cjs/ui/Renderer/index.js +4 -1
  11. package/dist/es2019/analytics/enums.js +1 -0
  12. package/dist/es2019/react/index.js +3 -0
  13. package/dist/es2019/react/nodes/codeBlock/codeBlock.js +4 -1
  14. package/dist/es2019/react/nodes/codeBlock/components/codeBlockButtonContainer.js +7 -1
  15. package/dist/es2019/react/nodes/codeBlock/components/codeBlockContainer.js +4 -0
  16. package/dist/es2019/react/nodes/codeBlock/components/codeBlockDownloadButton.js +123 -0
  17. package/dist/es2019/react/nodes/codeBlock/windowedCodeBlock.js +4 -1
  18. package/dist/es2019/react/nodes/emoji.js +1 -0
  19. package/dist/es2019/ui/Renderer/index.js +4 -1
  20. package/dist/esm/analytics/enums.js +1 -0
  21. package/dist/esm/react/index.js +3 -0
  22. package/dist/esm/react/nodes/codeBlock/codeBlock.js +5 -1
  23. package/dist/esm/react/nodes/codeBlock/components/codeBlockButtonContainer.js +7 -1
  24. package/dist/esm/react/nodes/codeBlock/components/codeBlockContainer.js +4 -0
  25. package/dist/esm/react/nodes/codeBlock/components/codeBlockDownloadButton.js +125 -0
  26. package/dist/esm/react/nodes/codeBlock/windowedCodeBlock.js +4 -1
  27. package/dist/esm/react/nodes/emoji.js +1 -0
  28. package/dist/esm/ui/Renderer/index.js +4 -1
  29. package/dist/types/analytics/enums.d.ts +1 -0
  30. package/dist/types/react/index.d.ts +2 -0
  31. package/dist/types/react/nodes/codeBlock/codeBlock.d.ts +1 -0
  32. package/dist/types/react/nodes/codeBlock/components/codeBlockButtonContainer.d.ts +3 -1
  33. package/dist/types/react/nodes/codeBlock/components/codeBlockContainer.d.ts +1 -1
  34. package/dist/types/react/nodes/codeBlock/components/codeBlockDownloadButton.d.ts +14 -0
  35. package/dist/types/react/nodes/codeBlock/windowedCodeBlock.d.ts +1 -1
  36. package/dist/types/react/types.d.ts +1 -0
  37. package/dist/types/ui/Renderer/index.d.ts +1 -0
  38. package/dist/types/ui/renderer-props.d.ts +1 -0
  39. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @atlaskit/renderer
2
2
 
3
+ ## 133.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`1abdd6ae1f4aa`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/1abdd6ae1f4aa) -
8
+ Add renderUnicodeEmojiAsImage prop to render unicode emojis as images via OffscreenCanvas, with
9
+ override to text as alternative
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies
14
+
15
+ ## 133.1.0
16
+
17
+ ### Minor Changes
18
+
19
+ - [`19869b1832800`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/19869b1832800) -
20
+ Add download button to code block toolbar in Rovo Chat. When enabled via the
21
+ rovo_chat_code_block_download feature gate, users can download code snippets (HTML, Python,
22
+ Markdown, etc.) directly as a file with the correct extension (e.g. rovo-snippet.html,
23
+ rovo-snippet.py). Adds allowDownloadCodeBlock prop to ReactRenderer (opt-in, defaults to false).
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies
28
+
3
29
  ## 133.0.0
4
30
 
5
31
  ### Major Changes
@@ -57,6 +57,7 @@ var ACTION_SUBJECT_ID = exports.ACTION_SUBJECT_ID = /*#__PURE__*/function (ACTIO
57
57
  ACTION_SUBJECT_ID["HOVER_LABEL"] = "hoverLabel";
58
58
  ACTION_SUBJECT_ID["INLINE_COMMENT"] = "inlineComment";
59
59
  ACTION_SUBJECT_ID["CODEBLOCK_COPY"] = "codeBlockCopy";
60
+ ACTION_SUBJECT_ID["CODEBLOCK_DOWNLOAD"] = "codeBlockDownload";
60
61
  ACTION_SUBJECT_ID["CODEBLOCK_WRAP"] = "codeBlockWrap";
61
62
  return ACTION_SUBJECT_ID;
62
63
  }({});
@@ -59,6 +59,7 @@ var ReactSerializer = exports.default = /*#__PURE__*/function () {
59
59
  */
60
60
  (0, _defineProperty2.default)(this, "expandHeadingIds", []);
61
61
  (0, _defineProperty2.default)(this, "allowCopyToClipboard", false);
62
+ (0, _defineProperty2.default)(this, "allowDownloadCodeBlock", false);
62
63
  (0, _defineProperty2.default)(this, "allowWrapCodeBlock", false);
63
64
  (0, _defineProperty2.default)(this, "allowPlaceholderText", true);
64
65
  (0, _defineProperty2.default)(this, "allowCustomPanels", false);
@@ -197,6 +198,7 @@ var ReactSerializer = exports.default = /*#__PURE__*/function () {
197
198
  this.disableActions = init.disableActions;
198
199
  this.allowHeadingAnchorLinks = init.allowHeadingAnchorLinks;
199
200
  this.allowCopyToClipboard = init.allowCopyToClipboard;
201
+ this.allowDownloadCodeBlock = init.allowDownloadCodeBlock;
200
202
  this.allowWrapCodeBlock = init.allowWrapCodeBlock;
201
203
  this.allowPlaceholderText = init.allowPlaceholderText;
202
204
  this.allowCustomPanels = init.allowCustomPanels;
@@ -663,6 +665,7 @@ var ReactSerializer = exports.default = /*#__PURE__*/function () {
663
665
  content: node.content ? node.content.toJSON() : undefined,
664
666
  allowHeadingAnchorLinks: this.allowHeadingAnchorLinks,
665
667
  allowCopyToClipboard: this.allowCopyToClipboard,
668
+ allowDownloadCodeBlock: this.allowDownloadCodeBlock,
666
669
  allowWrapCodeBlock: this.allowWrapCodeBlock,
667
670
  allowPlaceholderText: this.allowPlaceholderText,
668
671
  rendererAppearance: this.appearance,
@@ -28,6 +28,8 @@ function CodeBlock(props) {
28
28
  allowCopyToClipboard = _props$allowCopyToCli === void 0 ? false : _props$allowCopyToCli,
29
29
  _props$allowWrapCodeB = props.allowWrapCodeBlock,
30
30
  allowWrapCodeBlock = _props$allowWrapCodeB === void 0 ? false : _props$allowWrapCodeB,
31
+ _props$allowDownloadC = props.allowDownloadCodeBlock,
32
+ allowDownloadCodeBlock = _props$allowDownloadC === void 0 ? false : _props$allowDownloadC,
31
33
  codeBidiWarningTooltipEnabled = props.codeBidiWarningTooltipEnabled,
32
34
  _props$hideLineNumber = props.hideLineNumbers,
33
35
  hideLineNumbers = _props$hideLineNumber === void 0 ? false : _props$hideLineNumber,
@@ -43,7 +45,9 @@ function CodeBlock(props) {
43
45
  setWrapLongLines = _useState2[1];
44
46
  return (0, _react2.jsx)(_codeBlockContainer.default, {
45
47
  allowCopyToClipboard: allowCopyToClipboard,
46
- allowWrapCodeBlock: allowWrapCodeBlock
48
+ allowWrapCodeBlock: allowWrapCodeBlock,
49
+ allowDownloadCodeBlock: allowDownloadCodeBlock,
50
+ language: language
47
51
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
48
52
  ,
49
53
  className: className,
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.default = void 0;
8
8
  var _react = require("@emotion/react");
9
9
  var _codeBlockCopyButton = _interopRequireDefault(require("./codeBlockCopyButton"));
10
+ var _codeBlockDownloadButton = _interopRequireDefault(require("./codeBlockDownloadButton"));
10
11
  var _codeBlockWrapButton = _interopRequireDefault(require("./codeBlockWrapButton"));
11
12
  /**
12
13
  * @jsxRuntime classic
@@ -55,7 +56,9 @@ var codeBlockButtonsStyle = (0, _react.css)({
55
56
  });
56
57
  var CodeBlockButtonContainer = function CodeBlockButtonContainer(_ref) {
57
58
  var allowCopyToClipboard = _ref.allowCopyToClipboard,
59
+ allowDownloadCodeBlock = _ref.allowDownloadCodeBlock,
58
60
  allowWrapCodeBlock = _ref.allowWrapCodeBlock,
61
+ language = _ref.language,
59
62
  setWrapLongLines = _ref.setWrapLongLines,
60
63
  text = _ref.text,
61
64
  wrapLongLines = _ref.wrapLongLines;
@@ -63,7 +66,10 @@ var CodeBlockButtonContainer = function CodeBlockButtonContainer(_ref) {
63
66
  css: codeBlockButtonsWrapper
64
67
  }, (0, _react.jsx)("div", {
65
68
  css: codeBlockButtonsStyle
66
- }, allowWrapCodeBlock && (0, _react.jsx)(_codeBlockWrapButton.default, {
69
+ }, allowDownloadCodeBlock && (0, _react.jsx)(_codeBlockDownloadButton.default, {
70
+ content: text,
71
+ language: language !== null && language !== void 0 ? language : null
72
+ }), allowWrapCodeBlock && (0, _react.jsx)(_codeBlockWrapButton.default, {
67
73
  setWrapLongLines: setWrapLongLines,
68
74
  wrapLongLines: wrapLongLines
69
75
  }), allowCopyToClipboard && (0, _react.jsx)(_codeBlockCopyButton.default, {
@@ -48,9 +48,11 @@ var denseModeOverrides = (0, _react.css)((0, _defineProperty2.default)({}, "".co
48
48
  }));
49
49
  var CodeBlockContainer = function CodeBlockContainer(_ref) {
50
50
  var allowCopyToClipboard = _ref.allowCopyToClipboard,
51
+ allowDownloadCodeBlock = _ref.allowDownloadCodeBlock,
51
52
  allowWrapCodeBlock = _ref.allowWrapCodeBlock,
52
53
  children = _ref.children,
53
54
  className = _ref.className,
55
+ language = _ref.language,
54
56
  localId = _ref.localId,
55
57
  setWrapLongLines = _ref.setWrapLongLines,
56
58
  text = _ref.text,
@@ -62,7 +64,9 @@ var CodeBlockContainer = function CodeBlockContainer(_ref) {
62
64
  css: [codeBlockStyleOverrides, ((0, _expValEquals.expValEquals)('confluence_compact_text_format', 'isEnabled', true) || (0, _expValEquals.expValEquals)('cc_editor_ai_content_mode', 'variant', 'test') && (0, _platformFeatureFlags.fg)('platform_editor_content_mode_button_mvp')) && denseModeOverrides]
63
65
  }, (0, _react.jsx)(_codeBlockButtonContainer.default, {
64
66
  allowCopyToClipboard: allowCopyToClipboard,
67
+ allowDownloadCodeBlock: allowDownloadCodeBlock,
65
68
  allowWrapCodeBlock: allowWrapCodeBlock,
69
+ language: language,
66
70
  setWrapLongLines: setWrapLongLines,
67
71
  text: text,
68
72
  wrapLongLines: wrapLongLines
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = void 0;
8
+ var _reactIntl = require("react-intl");
9
+ var _download = _interopRequireDefault(require("@atlaskit/icon/core/download"));
10
+ var _react = require("@emotion/react");
11
+ var _new = require("@atlaskit/button/new");
12
+ var _tooltip = _interopRequireDefault(require("@atlaskit/tooltip"));
13
+ var _messages = require("@atlaskit/editor-common/messages");
14
+ var _analyticsContext = _interopRequireDefault(require("../../../../analytics/analyticsContext"));
15
+ var _enums = require("../../../../analytics/enums");
16
+ /**
17
+ * @jsxRuntime classic
18
+ * @jsx jsx
19
+ */
20
+
21
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
22
+
23
+ /**
24
+ * NOTE: The language-to-extension mapping logic below is intentionally duplicated in
25
+ * platform/packages/ai-mate/conversation-assistant-utils/src/downloadCodeBlock.ts.
26
+ * This is because @atlaskit/renderer cannot import from @atlassian/conversation-assistant-utils
27
+ * (cross-package dependency not allowed). If you update the logic here, update it there too.
28
+ *
29
+ * Maps ADF code block language names to file extensions.
30
+ * Inlined here to avoid cross-package dependencies on ai-mate packages.
31
+ */
32
+ var languageToExtension = {
33
+ bash: 'sh',
34
+ c: 'c',
35
+ cpp: 'cpp',
36
+ css: 'css',
37
+ csv: 'csv',
38
+ go: 'go',
39
+ htm: 'html',
40
+ html: 'html',
41
+ java: 'java',
42
+ javascript: 'js',
43
+ js: 'js',
44
+ json: 'json',
45
+ jsx: 'jsx',
46
+ less: 'less',
47
+ markdown: 'md',
48
+ md: 'md',
49
+ python: 'py',
50
+ ruby: 'rb',
51
+ rust: 'rs',
52
+ scss: 'scss',
53
+ sh: 'sh',
54
+ shell: 'sh',
55
+ sql: 'sql',
56
+ ts: 'ts',
57
+ tsx: 'tsx',
58
+ typescript: 'ts',
59
+ xml: 'xml',
60
+ yaml: 'yaml',
61
+ yml: 'yaml'
62
+ };
63
+ var getFileExtension = function getFileExtension(language) {
64
+ var _languageToExtension$;
65
+ if (!language) {
66
+ return 'txt';
67
+ }
68
+ return (_languageToExtension$ = languageToExtension[language.toLowerCase()]) !== null && _languageToExtension$ !== void 0 ? _languageToExtension$ : 'txt';
69
+ };
70
+ var triggerDownload = function triggerDownload(content, language) {
71
+ // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage
72
+ if (typeof document === 'undefined') {
73
+ return;
74
+ }
75
+ var extension = getFileExtension(language);
76
+ var filename = "rovo-snippet.".concat(extension);
77
+ // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage
78
+ var doc = document;
79
+ var blob = new Blob([content], {
80
+ type: 'text/plain'
81
+ });
82
+ var url = URL.createObjectURL(blob);
83
+ var anchor = doc.createElement('a');
84
+ anchor.href = url;
85
+ anchor.download = filename;
86
+ anchor.style.display = 'none';
87
+ doc.body.appendChild(anchor);
88
+ anchor.dispatchEvent(new MouseEvent('click', {
89
+ bubbles: false,
90
+ cancelable: true,
91
+ view: window
92
+ }));
93
+ doc.body.removeChild(anchor);
94
+ // Defer revocation to avoid race condition in Safari/Firefox
95
+ setTimeout(function () {
96
+ return URL.revokeObjectURL(url);
97
+ }, 0);
98
+ };
99
+ var DownloadButton = function DownloadButton(_ref) {
100
+ var content = _ref.content,
101
+ language = _ref.language,
102
+ intl = _ref.intl;
103
+ var tooltip = intl.formatMessage(_messages.codeBlockButtonMessages.downloadCodeBlock);
104
+ return (0, _react.jsx)(_analyticsContext.default.Consumer, null, function (_ref2) {
105
+ var fireAnalyticsEvent = _ref2.fireAnalyticsEvent;
106
+ return (0, _react.jsx)("span", null, (0, _react.jsx)(_tooltip.default, {
107
+ content: tooltip,
108
+ hideTooltipOnClick: false,
109
+ position: "top"
110
+ }, (0, _react.jsx)("div", null, (0, _react.jsx)(_new.IconButton, {
111
+ appearance: "subtle",
112
+ label: tooltip,
113
+ icon: _download.default
114
+ // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
115
+ ,
116
+ onClick: function onClick(event) {
117
+ fireAnalyticsEvent({
118
+ // @ts-expect-error - Type 'ACTION.CLICKED' is not assignable to type 'ACTION.CLICKED | ACTION.MEDIA_LINK_TRANSFORMED | ACTION.STARTED | ACTION.TOGGLE_EXPAND | ACTION.UNSUPPORTED_CONTENT_ENCOUNTERED | ACTION.VISITED | ACTION.RENDERED | ACTION.INVALID_PROSEMIRROR_DOCUMENT | ACTION.CRASHED | ... 6 more ... | AnnotationActionType'.
119
+ // This error was introduced after upgrading to TypeScript 5
120
+ action: _enums.ACTION.CLICKED,
121
+ actionSubject: _enums.ACTION_SUBJECT.BUTTON,
122
+ actionSubjectId: _enums.ACTION_SUBJECT_ID.CODEBLOCK_DOWNLOAD,
123
+ eventType: _enums.EVENT_TYPE.UI
124
+ });
125
+ triggerDownload(content, language);
126
+ event.stopPropagation();
127
+ },
128
+ spacing: "compact"
129
+ }))));
130
+ });
131
+ };
132
+ var _default_1 = (0, _reactIntl.injectIntl)(DownloadButton);
133
+ var _default = exports.default = _default_1;
@@ -50,6 +50,7 @@ var WindowedCodeBlock = function WindowedCodeBlock(_ref2) {
50
50
  var text = _ref2.text,
51
51
  language = _ref2.language,
52
52
  allowCopyToClipboard = _ref2.allowCopyToClipboard,
53
+ allowDownloadCodeBlock = _ref2.allowDownloadCodeBlock,
53
54
  _ref2$allowWrapCodeBl = _ref2.allowWrapCodeBlock,
54
55
  allowWrapCodeBlock = _ref2$allowWrapCodeBl === void 0 ? false : _ref2$allowWrapCodeBl,
55
56
  codeBidiWarningTooltipEnabled = _ref2.codeBidiWarningTooltipEnabled,
@@ -84,7 +85,9 @@ var WindowedCodeBlock = function WindowedCodeBlock(_ref2) {
84
85
  fallback: memoizedLightWeightCodeBlock
85
86
  }, (0, _react2.jsx)(_codeBlockContainer.default, {
86
87
  allowCopyToClipboard: allowCopyToClipboard,
87
- allowWrapCodeBlock: allowWrapCodeBlock
88
+ allowWrapCodeBlock: allowWrapCodeBlock,
89
+ allowDownloadCodeBlock: allowDownloadCodeBlock,
90
+ language: language
88
91
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
89
92
  ,
90
93
  className: className,
@@ -92,6 +92,7 @@ var EmojiNode = /*#__PURE__*/function (_PureComponent) {
92
92
  shortName: shortName
93
93
  }),
94
94
  editorEmoji: true,
95
+ renderUnicodeEmojiAsImage: false,
95
96
  onEmojiLoadSuccess: resourceConfig === null || resourceConfig === void 0 ? void 0 : resourceConfig.onEmojiLoadSuccess,
96
97
  onEmojiLoadFail: resourceConfig === null || resourceConfig === void 0 ? void 0 : resourceConfig.onEmojiLoadFail
97
98
  });
@@ -72,7 +72,7 @@ var DEGRADED_SEVERITY_THRESHOLD = exports.DEGRADED_SEVERITY_THRESHOLD = 3000;
72
72
  var TABLE_INFO_TIMEOUT = 10000;
73
73
  var RENDER_EVENT_SAMPLE_RATE = 0.2;
74
74
  var packageName = "@atlaskit/renderer";
75
- var packageVersion = "132.7.0";
75
+ var packageVersion = "133.1.0";
76
76
  var setAsQueryContainerStyles = (0, _react2.css)({
77
77
  containerName: 'ak-renderer-wrapper',
78
78
  containerType: 'inline-size'
@@ -263,6 +263,7 @@ var RendererFunctionalComponent = exports.RendererFunctionalComponent = function
263
263
  extensionViewportSizes: props.extensionViewportSizes,
264
264
  getExtensionHeight: props.getExtensionHeight,
265
265
  allowCopyToClipboard: props.allowCopyToClipboard,
266
+ allowDownloadCodeBlock: props.allowDownloadCodeBlock,
266
267
  allowWrapCodeBlock: props.allowWrapCodeBlock,
267
268
  allowCustomPanels: props.allowCustomPanels,
268
269
  allowAnnotations: props.allowAnnotations,
@@ -482,6 +483,7 @@ var RendererFunctionalComponent = exports.RendererFunctionalComponent = function
482
483
  allowNestedHeaderLinks: (0, _links.isNestedHeaderLinksEnabled)(props.allowHeadingAnchorLinks),
483
484
  allowColumnSorting: props.allowColumnSorting,
484
485
  allowCopyToClipboard: props.allowCopyToClipboard,
486
+ allowDownloadCodeBlock: props.allowDownloadCodeBlock,
485
487
  allowWrapCodeBlock: props.allowWrapCodeBlock,
486
488
  allowCustomPanels: props.allowCustomPanels,
487
489
  allowPlaceholderText: props.allowPlaceholderText,
@@ -521,6 +523,7 @@ var RendererFunctionalComponent = exports.RendererFunctionalComponent = function
521
523
  appearance: props.appearance,
522
524
  contentMode: props.contentMode || 'standard',
523
525
  allowCopyToClipboard: props.allowCopyToClipboard,
526
+ allowDownloadCodeBlock: props.allowDownloadCodeBlock,
524
527
  allowWrapCodeBlock: props.allowWrapCodeBlock,
525
528
  allowPlaceholderText: props.allowPlaceholderText,
526
529
  allowColumnSorting: props.allowColumnSorting,
@@ -51,6 +51,7 @@ export let ACTION_SUBJECT_ID = /*#__PURE__*/function (ACTION_SUBJECT_ID) {
51
51
  ACTION_SUBJECT_ID["HOVER_LABEL"] = "hoverLabel";
52
52
  ACTION_SUBJECT_ID["INLINE_COMMENT"] = "inlineComment";
53
53
  ACTION_SUBJECT_ID["CODEBLOCK_COPY"] = "codeBlockCopy";
54
+ ACTION_SUBJECT_ID["CODEBLOCK_DOWNLOAD"] = "codeBlockDownload";
54
55
  ACTION_SUBJECT_ID["CODEBLOCK_WRAP"] = "codeBlockWrap";
55
56
  return ACTION_SUBJECT_ID;
56
57
  }({});
@@ -43,6 +43,7 @@ export default class ReactSerializer {
43
43
  */
44
44
  _defineProperty(this, "expandHeadingIds", []);
45
45
  _defineProperty(this, "allowCopyToClipboard", false);
46
+ _defineProperty(this, "allowDownloadCodeBlock", false);
46
47
  _defineProperty(this, "allowWrapCodeBlock", false);
47
48
  _defineProperty(this, "allowPlaceholderText", true);
48
49
  _defineProperty(this, "allowCustomPanels", false);
@@ -184,6 +185,7 @@ export default class ReactSerializer {
184
185
  this.disableActions = init.disableActions;
185
186
  this.allowHeadingAnchorLinks = init.allowHeadingAnchorLinks;
186
187
  this.allowCopyToClipboard = init.allowCopyToClipboard;
188
+ this.allowDownloadCodeBlock = init.allowDownloadCodeBlock;
187
189
  this.allowWrapCodeBlock = init.allowWrapCodeBlock;
188
190
  this.allowPlaceholderText = init.allowPlaceholderText;
189
191
  this.allowCustomPanels = init.allowCustomPanels;
@@ -593,6 +595,7 @@ export default class ReactSerializer {
593
595
  content: node.content ? node.content.toJSON() : undefined,
594
596
  allowHeadingAnchorLinks: this.allowHeadingAnchorLinks,
595
597
  allowCopyToClipboard: this.allowCopyToClipboard,
598
+ allowDownloadCodeBlock: this.allowDownloadCodeBlock,
596
599
  allowWrapCodeBlock: this.allowWrapCodeBlock,
597
600
  allowPlaceholderText: this.allowPlaceholderText,
598
601
  rendererAppearance: this.appearance,
@@ -17,6 +17,7 @@ function CodeBlock(props) {
17
17
  language,
18
18
  allowCopyToClipboard = false,
19
19
  allowWrapCodeBlock = false,
20
+ allowDownloadCodeBlock = false,
20
21
  codeBidiWarningTooltipEnabled,
21
22
  hideLineNumbers = false,
22
23
  localId,
@@ -27,7 +28,9 @@ function CodeBlock(props) {
27
28
  const [wrapLongLines, setWrapLongLines] = useState(() => expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true) && Boolean(wrap));
28
29
  return jsx(CodeBlockContainer, {
29
30
  allowCopyToClipboard: allowCopyToClipboard,
30
- allowWrapCodeBlock: allowWrapCodeBlock
31
+ allowWrapCodeBlock: allowWrapCodeBlock,
32
+ allowDownloadCodeBlock: allowDownloadCodeBlock,
33
+ language: language
31
34
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
32
35
  ,
33
36
  className: className,
@@ -5,6 +5,7 @@
5
5
  /* eslint-disable @typescript-eslint/consistent-type-imports, @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766; jsx required at runtime for @jsxRuntime classic */
6
6
  import { jsx, css } from '@emotion/react';
7
7
  import CopyButton from './codeBlockCopyButton';
8
+ import DownloadButton from './codeBlockDownloadButton';
8
9
  import CodeWrapButton from './codeBlockWrapButton';
9
10
  const codeBlockButtonsWrapper = css({
10
11
  position: 'sticky',
@@ -47,7 +48,9 @@ const codeBlockButtonsStyle = css({
47
48
  });
48
49
  const CodeBlockButtonContainer = ({
49
50
  allowCopyToClipboard,
51
+ allowDownloadCodeBlock,
50
52
  allowWrapCodeBlock,
53
+ language,
51
54
  setWrapLongLines,
52
55
  text,
53
56
  wrapLongLines
@@ -56,7 +59,10 @@ const CodeBlockButtonContainer = ({
56
59
  css: codeBlockButtonsWrapper
57
60
  }, jsx("div", {
58
61
  css: codeBlockButtonsStyle
59
- }, allowWrapCodeBlock && jsx(CodeWrapButton, {
62
+ }, allowDownloadCodeBlock && jsx(DownloadButton, {
63
+ content: text,
64
+ language: language !== null && language !== void 0 ? language : null
65
+ }), allowWrapCodeBlock && jsx(CodeWrapButton, {
60
66
  setWrapLongLines: setWrapLongLines,
61
67
  wrapLongLines: wrapLongLines
62
68
  }), allowCopyToClipboard && jsx(CopyButton, {
@@ -76,9 +76,11 @@ const denseModeOverrides = css({
76
76
  });
77
77
  const CodeBlockContainer = ({
78
78
  allowCopyToClipboard,
79
+ allowDownloadCodeBlock,
79
80
  allowWrapCodeBlock,
80
81
  children,
81
82
  className,
83
+ language,
82
84
  localId,
83
85
  setWrapLongLines,
84
86
  text,
@@ -91,7 +93,9 @@ const CodeBlockContainer = ({
91
93
  css: [codeBlockStyleOverrides, (expValEquals('confluence_compact_text_format', 'isEnabled', true) || expValEquals('cc_editor_ai_content_mode', 'variant', 'test') && fg('platform_editor_content_mode_button_mvp')) && denseModeOverrides]
92
94
  }, jsx(CodeBlockButtonContainer, {
93
95
  allowCopyToClipboard: allowCopyToClipboard,
96
+ allowDownloadCodeBlock: allowDownloadCodeBlock,
94
97
  allowWrapCodeBlock: allowWrapCodeBlock,
98
+ language: language,
95
99
  setWrapLongLines: setWrapLongLines,
96
100
  text: text,
97
101
  wrapLongLines: wrapLongLines
@@ -0,0 +1,123 @@
1
+ /**
2
+ * @jsxRuntime classic
3
+ * @jsx jsx
4
+ */
5
+
6
+ import { injectIntl } from 'react-intl';
7
+ import DownloadIcon from '@atlaskit/icon/core/download';
8
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
9
+ import { jsx } from '@emotion/react';
10
+ import { IconButton } from '@atlaskit/button/new';
11
+ import Tooltip from '@atlaskit/tooltip';
12
+ import { codeBlockButtonMessages } from '@atlaskit/editor-common/messages';
13
+ import AnalyticsContext from '../../../../analytics/analyticsContext';
14
+ import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '../../../../analytics/enums';
15
+ /**
16
+ * NOTE: The language-to-extension mapping logic below is intentionally duplicated in
17
+ * platform/packages/ai-mate/conversation-assistant-utils/src/downloadCodeBlock.ts.
18
+ * This is because @atlaskit/renderer cannot import from @atlassian/conversation-assistant-utils
19
+ * (cross-package dependency not allowed). If you update the logic here, update it there too.
20
+ *
21
+ * Maps ADF code block language names to file extensions.
22
+ * Inlined here to avoid cross-package dependencies on ai-mate packages.
23
+ */
24
+ const languageToExtension = {
25
+ bash: 'sh',
26
+ c: 'c',
27
+ cpp: 'cpp',
28
+ css: 'css',
29
+ csv: 'csv',
30
+ go: 'go',
31
+ htm: 'html',
32
+ html: 'html',
33
+ java: 'java',
34
+ javascript: 'js',
35
+ js: 'js',
36
+ json: 'json',
37
+ jsx: 'jsx',
38
+ less: 'less',
39
+ markdown: 'md',
40
+ md: 'md',
41
+ python: 'py',
42
+ ruby: 'rb',
43
+ rust: 'rs',
44
+ scss: 'scss',
45
+ sh: 'sh',
46
+ shell: 'sh',
47
+ sql: 'sql',
48
+ ts: 'ts',
49
+ tsx: 'tsx',
50
+ typescript: 'ts',
51
+ xml: 'xml',
52
+ yaml: 'yaml',
53
+ yml: 'yaml'
54
+ };
55
+ const getFileExtension = language => {
56
+ var _languageToExtension$;
57
+ if (!language) {
58
+ return 'txt';
59
+ }
60
+ return (_languageToExtension$ = languageToExtension[language.toLowerCase()]) !== null && _languageToExtension$ !== void 0 ? _languageToExtension$ : 'txt';
61
+ };
62
+ const triggerDownload = (content, language) => {
63
+ // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage
64
+ if (typeof document === 'undefined') {
65
+ return;
66
+ }
67
+ const extension = getFileExtension(language);
68
+ const filename = `rovo-snippet.${extension}`;
69
+ // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage
70
+ const doc = document;
71
+ const blob = new Blob([content], {
72
+ type: 'text/plain'
73
+ });
74
+ const url = URL.createObjectURL(blob);
75
+ const anchor = doc.createElement('a');
76
+ anchor.href = url;
77
+ anchor.download = filename;
78
+ anchor.style.display = 'none';
79
+ doc.body.appendChild(anchor);
80
+ anchor.dispatchEvent(new MouseEvent('click', {
81
+ bubbles: false,
82
+ cancelable: true,
83
+ view: window
84
+ }));
85
+ doc.body.removeChild(anchor);
86
+ // Defer revocation to avoid race condition in Safari/Firefox
87
+ setTimeout(() => URL.revokeObjectURL(url), 0);
88
+ };
89
+ const DownloadButton = ({
90
+ content,
91
+ language,
92
+ intl
93
+ }) => {
94
+ const tooltip = intl.formatMessage(codeBlockButtonMessages.downloadCodeBlock);
95
+ return jsx(AnalyticsContext.Consumer, null, ({
96
+ fireAnalyticsEvent
97
+ }) => jsx("span", null, jsx(Tooltip, {
98
+ content: tooltip,
99
+ hideTooltipOnClick: false,
100
+ position: "top"
101
+ }, jsx("div", null, jsx(IconButton, {
102
+ appearance: "subtle",
103
+ label: tooltip,
104
+ icon: DownloadIcon
105
+ // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
106
+ ,
107
+ onClick: event => {
108
+ fireAnalyticsEvent({
109
+ // @ts-expect-error - Type 'ACTION.CLICKED' is not assignable to type 'ACTION.CLICKED | ACTION.MEDIA_LINK_TRANSFORMED | ACTION.STARTED | ACTION.TOGGLE_EXPAND | ACTION.UNSUPPORTED_CONTENT_ENCOUNTERED | ACTION.VISITED | ACTION.RENDERED | ACTION.INVALID_PROSEMIRROR_DOCUMENT | ACTION.CRASHED | ... 6 more ... | AnnotationActionType'.
110
+ // This error was introduced after upgrading to TypeScript 5
111
+ action: ACTION.CLICKED,
112
+ actionSubject: ACTION_SUBJECT.BUTTON,
113
+ actionSubjectId: ACTION_SUBJECT_ID.CODEBLOCK_DOWNLOAD,
114
+ eventType: EVENT_TYPE.UI
115
+ });
116
+ triggerDownload(content, language);
117
+ event.stopPropagation();
118
+ },
119
+ spacing: "compact"
120
+ })))));
121
+ };
122
+ const _default_1 = injectIntl(DownloadButton);
123
+ export default _default_1;
@@ -22,6 +22,7 @@ const WindowedCodeBlock = ({
22
22
  text,
23
23
  language,
24
24
  allowCopyToClipboard,
25
+ allowDownloadCodeBlock,
25
26
  allowWrapCodeBlock = false,
26
27
  codeBidiWarningTooltipEnabled,
27
28
  hideLineNumbers = false,
@@ -52,7 +53,9 @@ const WindowedCodeBlock = ({
52
53
  fallback: memoizedLightWeightCodeBlock
53
54
  }, jsx(CodeBlockContainer, {
54
55
  allowCopyToClipboard: allowCopyToClipboard,
55
- allowWrapCodeBlock: allowWrapCodeBlock
56
+ allowWrapCodeBlock: allowWrapCodeBlock,
57
+ allowDownloadCodeBlock: allowDownloadCodeBlock,
58
+ language: language
56
59
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
57
60
  ,
58
61
  className: className,
@@ -79,6 +79,7 @@ class EmojiNode extends PureComponent {
79
79
  shortName
80
80
  }),
81
81
  editorEmoji: true,
82
+ renderUnicodeEmojiAsImage: false,
82
83
  onEmojiLoadSuccess: resourceConfig === null || resourceConfig === void 0 ? void 0 : resourceConfig.onEmojiLoadSuccess,
83
84
  onEmojiLoadFail: resourceConfig === null || resourceConfig === void 0 ? void 0 : resourceConfig.onEmojiLoadFail
84
85
  });
@@ -58,7 +58,7 @@ export const DEGRADED_SEVERITY_THRESHOLD = 3000;
58
58
  const TABLE_INFO_TIMEOUT = 10000;
59
59
  const RENDER_EVENT_SAMPLE_RATE = 0.2;
60
60
  const packageName = "@atlaskit/renderer";
61
- const packageVersion = "132.7.0";
61
+ const packageVersion = "133.1.0";
62
62
  const setAsQueryContainerStyles = css({
63
63
  containerName: 'ak-renderer-wrapper',
64
64
  containerType: 'inline-size'
@@ -255,6 +255,7 @@ export const RendererFunctionalComponent = props => {
255
255
  extensionViewportSizes: props.extensionViewportSizes,
256
256
  getExtensionHeight: props.getExtensionHeight,
257
257
  allowCopyToClipboard: props.allowCopyToClipboard,
258
+ allowDownloadCodeBlock: props.allowDownloadCodeBlock,
258
259
  allowWrapCodeBlock: props.allowWrapCodeBlock,
259
260
  allowCustomPanels: props.allowCustomPanels,
260
261
  allowAnnotations: props.allowAnnotations,
@@ -472,6 +473,7 @@ export const RendererFunctionalComponent = props => {
472
473
  allowNestedHeaderLinks: isNestedHeaderLinksEnabled(props.allowHeadingAnchorLinks),
473
474
  allowColumnSorting: props.allowColumnSorting,
474
475
  allowCopyToClipboard: props.allowCopyToClipboard,
476
+ allowDownloadCodeBlock: props.allowDownloadCodeBlock,
475
477
  allowWrapCodeBlock: props.allowWrapCodeBlock,
476
478
  allowCustomPanels: props.allowCustomPanels,
477
479
  allowPlaceholderText: props.allowPlaceholderText,
@@ -509,6 +511,7 @@ export const RendererFunctionalComponent = props => {
509
511
  appearance: props.appearance,
510
512
  contentMode: props.contentMode || 'standard',
511
513
  allowCopyToClipboard: props.allowCopyToClipboard,
514
+ allowDownloadCodeBlock: props.allowDownloadCodeBlock,
512
515
  allowWrapCodeBlock: props.allowWrapCodeBlock,
513
516
  allowPlaceholderText: props.allowPlaceholderText,
514
517
  allowColumnSorting: props.allowColumnSorting,
@@ -51,6 +51,7 @@ export var ACTION_SUBJECT_ID = /*#__PURE__*/function (ACTION_SUBJECT_ID) {
51
51
  ACTION_SUBJECT_ID["HOVER_LABEL"] = "hoverLabel";
52
52
  ACTION_SUBJECT_ID["INLINE_COMMENT"] = "inlineComment";
53
53
  ACTION_SUBJECT_ID["CODEBLOCK_COPY"] = "codeBlockCopy";
54
+ ACTION_SUBJECT_ID["CODEBLOCK_DOWNLOAD"] = "codeBlockDownload";
54
55
  ACTION_SUBJECT_ID["CODEBLOCK_WRAP"] = "codeBlockWrap";
55
56
  return ACTION_SUBJECT_ID;
56
57
  }({});
@@ -52,6 +52,7 @@ var ReactSerializer = /*#__PURE__*/function () {
52
52
  */
53
53
  _defineProperty(this, "expandHeadingIds", []);
54
54
  _defineProperty(this, "allowCopyToClipboard", false);
55
+ _defineProperty(this, "allowDownloadCodeBlock", false);
55
56
  _defineProperty(this, "allowWrapCodeBlock", false);
56
57
  _defineProperty(this, "allowPlaceholderText", true);
57
58
  _defineProperty(this, "allowCustomPanels", false);
@@ -190,6 +191,7 @@ var ReactSerializer = /*#__PURE__*/function () {
190
191
  this.disableActions = init.disableActions;
191
192
  this.allowHeadingAnchorLinks = init.allowHeadingAnchorLinks;
192
193
  this.allowCopyToClipboard = init.allowCopyToClipboard;
194
+ this.allowDownloadCodeBlock = init.allowDownloadCodeBlock;
193
195
  this.allowWrapCodeBlock = init.allowWrapCodeBlock;
194
196
  this.allowPlaceholderText = init.allowPlaceholderText;
195
197
  this.allowCustomPanels = init.allowCustomPanels;
@@ -656,6 +658,7 @@ var ReactSerializer = /*#__PURE__*/function () {
656
658
  content: node.content ? node.content.toJSON() : undefined,
657
659
  allowHeadingAnchorLinks: this.allowHeadingAnchorLinks,
658
660
  allowCopyToClipboard: this.allowCopyToClipboard,
661
+ allowDownloadCodeBlock: this.allowDownloadCodeBlock,
659
662
  allowWrapCodeBlock: this.allowWrapCodeBlock,
660
663
  allowPlaceholderText: this.allowPlaceholderText,
661
664
  rendererAppearance: this.appearance,
@@ -19,6 +19,8 @@ function CodeBlock(props) {
19
19
  allowCopyToClipboard = _props$allowCopyToCli === void 0 ? false : _props$allowCopyToCli,
20
20
  _props$allowWrapCodeB = props.allowWrapCodeBlock,
21
21
  allowWrapCodeBlock = _props$allowWrapCodeB === void 0 ? false : _props$allowWrapCodeB,
22
+ _props$allowDownloadC = props.allowDownloadCodeBlock,
23
+ allowDownloadCodeBlock = _props$allowDownloadC === void 0 ? false : _props$allowDownloadC,
22
24
  codeBidiWarningTooltipEnabled = props.codeBidiWarningTooltipEnabled,
23
25
  _props$hideLineNumber = props.hideLineNumbers,
24
26
  hideLineNumbers = _props$hideLineNumber === void 0 ? false : _props$hideLineNumber,
@@ -34,7 +36,9 @@ function CodeBlock(props) {
34
36
  setWrapLongLines = _useState2[1];
35
37
  return jsx(CodeBlockContainer, {
36
38
  allowCopyToClipboard: allowCopyToClipboard,
37
- allowWrapCodeBlock: allowWrapCodeBlock
39
+ allowWrapCodeBlock: allowWrapCodeBlock,
40
+ allowDownloadCodeBlock: allowDownloadCodeBlock,
41
+ language: language
38
42
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
39
43
  ,
40
44
  className: className,
@@ -5,6 +5,7 @@
5
5
  /* eslint-disable @typescript-eslint/consistent-type-imports, @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766; jsx required at runtime for @jsxRuntime classic */
6
6
  import { jsx, css } from '@emotion/react';
7
7
  import CopyButton from './codeBlockCopyButton';
8
+ import DownloadButton from './codeBlockDownloadButton';
8
9
  import CodeWrapButton from './codeBlockWrapButton';
9
10
  var codeBlockButtonsWrapper = css({
10
11
  position: 'sticky',
@@ -47,7 +48,9 @@ var codeBlockButtonsStyle = css({
47
48
  });
48
49
  var CodeBlockButtonContainer = function CodeBlockButtonContainer(_ref) {
49
50
  var allowCopyToClipboard = _ref.allowCopyToClipboard,
51
+ allowDownloadCodeBlock = _ref.allowDownloadCodeBlock,
50
52
  allowWrapCodeBlock = _ref.allowWrapCodeBlock,
53
+ language = _ref.language,
51
54
  setWrapLongLines = _ref.setWrapLongLines,
52
55
  text = _ref.text,
53
56
  wrapLongLines = _ref.wrapLongLines;
@@ -55,7 +58,10 @@ var CodeBlockButtonContainer = function CodeBlockButtonContainer(_ref) {
55
58
  css: codeBlockButtonsWrapper
56
59
  }, jsx("div", {
57
60
  css: codeBlockButtonsStyle
58
- }, allowWrapCodeBlock && jsx(CodeWrapButton, {
61
+ }, allowDownloadCodeBlock && jsx(DownloadButton, {
62
+ content: text,
63
+ language: language !== null && language !== void 0 ? language : null
64
+ }), allowWrapCodeBlock && jsx(CodeWrapButton, {
59
65
  setWrapLongLines: setWrapLongLines,
60
66
  wrapLongLines: wrapLongLines
61
67
  }), allowCopyToClipboard && jsx(CopyButton, {
@@ -40,9 +40,11 @@ var denseModeOverrides = css(_defineProperty({}, "".concat(CodeBlockSharedCssCla
40
40
  }));
41
41
  var CodeBlockContainer = function CodeBlockContainer(_ref) {
42
42
  var allowCopyToClipboard = _ref.allowCopyToClipboard,
43
+ allowDownloadCodeBlock = _ref.allowDownloadCodeBlock,
43
44
  allowWrapCodeBlock = _ref.allowWrapCodeBlock,
44
45
  children = _ref.children,
45
46
  className = _ref.className,
47
+ language = _ref.language,
46
48
  localId = _ref.localId,
47
49
  setWrapLongLines = _ref.setWrapLongLines,
48
50
  text = _ref.text,
@@ -54,7 +56,9 @@ var CodeBlockContainer = function CodeBlockContainer(_ref) {
54
56
  css: [codeBlockStyleOverrides, (expValEquals('confluence_compact_text_format', 'isEnabled', true) || expValEquals('cc_editor_ai_content_mode', 'variant', 'test') && fg('platform_editor_content_mode_button_mvp')) && denseModeOverrides]
55
57
  }, jsx(CodeBlockButtonContainer, {
56
58
  allowCopyToClipboard: allowCopyToClipboard,
59
+ allowDownloadCodeBlock: allowDownloadCodeBlock,
57
60
  allowWrapCodeBlock: allowWrapCodeBlock,
61
+ language: language,
58
62
  setWrapLongLines: setWrapLongLines,
59
63
  text: text,
60
64
  wrapLongLines: wrapLongLines
@@ -0,0 +1,125 @@
1
+ /**
2
+ * @jsxRuntime classic
3
+ * @jsx jsx
4
+ */
5
+
6
+ import { injectIntl } from 'react-intl';
7
+ import DownloadIcon from '@atlaskit/icon/core/download';
8
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
9
+ import { jsx } from '@emotion/react';
10
+ import { IconButton } from '@atlaskit/button/new';
11
+ import Tooltip from '@atlaskit/tooltip';
12
+ import { codeBlockButtonMessages } from '@atlaskit/editor-common/messages';
13
+ import AnalyticsContext from '../../../../analytics/analyticsContext';
14
+ import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '../../../../analytics/enums';
15
+ /**
16
+ * NOTE: The language-to-extension mapping logic below is intentionally duplicated in
17
+ * platform/packages/ai-mate/conversation-assistant-utils/src/downloadCodeBlock.ts.
18
+ * This is because @atlaskit/renderer cannot import from @atlassian/conversation-assistant-utils
19
+ * (cross-package dependency not allowed). If you update the logic here, update it there too.
20
+ *
21
+ * Maps ADF code block language names to file extensions.
22
+ * Inlined here to avoid cross-package dependencies on ai-mate packages.
23
+ */
24
+ var languageToExtension = {
25
+ bash: 'sh',
26
+ c: 'c',
27
+ cpp: 'cpp',
28
+ css: 'css',
29
+ csv: 'csv',
30
+ go: 'go',
31
+ htm: 'html',
32
+ html: 'html',
33
+ java: 'java',
34
+ javascript: 'js',
35
+ js: 'js',
36
+ json: 'json',
37
+ jsx: 'jsx',
38
+ less: 'less',
39
+ markdown: 'md',
40
+ md: 'md',
41
+ python: 'py',
42
+ ruby: 'rb',
43
+ rust: 'rs',
44
+ scss: 'scss',
45
+ sh: 'sh',
46
+ shell: 'sh',
47
+ sql: 'sql',
48
+ ts: 'ts',
49
+ tsx: 'tsx',
50
+ typescript: 'ts',
51
+ xml: 'xml',
52
+ yaml: 'yaml',
53
+ yml: 'yaml'
54
+ };
55
+ var getFileExtension = function getFileExtension(language) {
56
+ var _languageToExtension$;
57
+ if (!language) {
58
+ return 'txt';
59
+ }
60
+ return (_languageToExtension$ = languageToExtension[language.toLowerCase()]) !== null && _languageToExtension$ !== void 0 ? _languageToExtension$ : 'txt';
61
+ };
62
+ var triggerDownload = function triggerDownload(content, language) {
63
+ // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage
64
+ if (typeof document === 'undefined') {
65
+ return;
66
+ }
67
+ var extension = getFileExtension(language);
68
+ var filename = "rovo-snippet.".concat(extension);
69
+ // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage
70
+ var doc = document;
71
+ var blob = new Blob([content], {
72
+ type: 'text/plain'
73
+ });
74
+ var url = URL.createObjectURL(blob);
75
+ var anchor = doc.createElement('a');
76
+ anchor.href = url;
77
+ anchor.download = filename;
78
+ anchor.style.display = 'none';
79
+ doc.body.appendChild(anchor);
80
+ anchor.dispatchEvent(new MouseEvent('click', {
81
+ bubbles: false,
82
+ cancelable: true,
83
+ view: window
84
+ }));
85
+ doc.body.removeChild(anchor);
86
+ // Defer revocation to avoid race condition in Safari/Firefox
87
+ setTimeout(function () {
88
+ return URL.revokeObjectURL(url);
89
+ }, 0);
90
+ };
91
+ var DownloadButton = function DownloadButton(_ref) {
92
+ var content = _ref.content,
93
+ language = _ref.language,
94
+ intl = _ref.intl;
95
+ var tooltip = intl.formatMessage(codeBlockButtonMessages.downloadCodeBlock);
96
+ return jsx(AnalyticsContext.Consumer, null, function (_ref2) {
97
+ var fireAnalyticsEvent = _ref2.fireAnalyticsEvent;
98
+ return jsx("span", null, jsx(Tooltip, {
99
+ content: tooltip,
100
+ hideTooltipOnClick: false,
101
+ position: "top"
102
+ }, jsx("div", null, jsx(IconButton, {
103
+ appearance: "subtle",
104
+ label: tooltip,
105
+ icon: DownloadIcon
106
+ // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
107
+ ,
108
+ onClick: function onClick(event) {
109
+ fireAnalyticsEvent({
110
+ // @ts-expect-error - Type 'ACTION.CLICKED' is not assignable to type 'ACTION.CLICKED | ACTION.MEDIA_LINK_TRANSFORMED | ACTION.STARTED | ACTION.TOGGLE_EXPAND | ACTION.UNSUPPORTED_CONTENT_ENCOUNTERED | ACTION.VISITED | ACTION.RENDERED | ACTION.INVALID_PROSEMIRROR_DOCUMENT | ACTION.CRASHED | ... 6 more ... | AnnotationActionType'.
111
+ // This error was introduced after upgrading to TypeScript 5
112
+ action: ACTION.CLICKED,
113
+ actionSubject: ACTION_SUBJECT.BUTTON,
114
+ actionSubjectId: ACTION_SUBJECT_ID.CODEBLOCK_DOWNLOAD,
115
+ eventType: EVENT_TYPE.UI
116
+ });
117
+ triggerDownload(content, language);
118
+ event.stopPropagation();
119
+ },
120
+ spacing: "compact"
121
+ }))));
122
+ });
123
+ };
124
+ var _default_1 = injectIntl(DownloadButton);
125
+ export default _default_1;
@@ -41,6 +41,7 @@ var WindowedCodeBlock = function WindowedCodeBlock(_ref2) {
41
41
  var text = _ref2.text,
42
42
  language = _ref2.language,
43
43
  allowCopyToClipboard = _ref2.allowCopyToClipboard,
44
+ allowDownloadCodeBlock = _ref2.allowDownloadCodeBlock,
44
45
  _ref2$allowWrapCodeBl = _ref2.allowWrapCodeBlock,
45
46
  allowWrapCodeBlock = _ref2$allowWrapCodeBl === void 0 ? false : _ref2$allowWrapCodeBl,
46
47
  codeBidiWarningTooltipEnabled = _ref2.codeBidiWarningTooltipEnabled,
@@ -75,7 +76,9 @@ var WindowedCodeBlock = function WindowedCodeBlock(_ref2) {
75
76
  fallback: memoizedLightWeightCodeBlock
76
77
  }, jsx(CodeBlockContainer, {
77
78
  allowCopyToClipboard: allowCopyToClipboard,
78
- allowWrapCodeBlock: allowWrapCodeBlock
79
+ allowWrapCodeBlock: allowWrapCodeBlock,
80
+ allowDownloadCodeBlock: allowDownloadCodeBlock,
81
+ language: language
79
82
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
80
83
  ,
81
84
  className: className,
@@ -87,6 +87,7 @@ var EmojiNode = /*#__PURE__*/function (_PureComponent) {
87
87
  shortName: shortName
88
88
  }),
89
89
  editorEmoji: true,
90
+ renderUnicodeEmojiAsImage: false,
90
91
  onEmojiLoadSuccess: resourceConfig === null || resourceConfig === void 0 ? void 0 : resourceConfig.onEmojiLoadSuccess,
91
92
  onEmojiLoadFail: resourceConfig === null || resourceConfig === void 0 ? void 0 : resourceConfig.onEmojiLoadFail
92
93
  });
@@ -63,7 +63,7 @@ export var DEGRADED_SEVERITY_THRESHOLD = 3000;
63
63
  var TABLE_INFO_TIMEOUT = 10000;
64
64
  var RENDER_EVENT_SAMPLE_RATE = 0.2;
65
65
  var packageName = "@atlaskit/renderer";
66
- var packageVersion = "132.7.0";
66
+ var packageVersion = "133.1.0";
67
67
  var setAsQueryContainerStyles = css({
68
68
  containerName: 'ak-renderer-wrapper',
69
69
  containerType: 'inline-size'
@@ -254,6 +254,7 @@ export var RendererFunctionalComponent = function RendererFunctionalComponent(pr
254
254
  extensionViewportSizes: props.extensionViewportSizes,
255
255
  getExtensionHeight: props.getExtensionHeight,
256
256
  allowCopyToClipboard: props.allowCopyToClipboard,
257
+ allowDownloadCodeBlock: props.allowDownloadCodeBlock,
257
258
  allowWrapCodeBlock: props.allowWrapCodeBlock,
258
259
  allowCustomPanels: props.allowCustomPanels,
259
260
  allowAnnotations: props.allowAnnotations,
@@ -473,6 +474,7 @@ export var RendererFunctionalComponent = function RendererFunctionalComponent(pr
473
474
  allowNestedHeaderLinks: isNestedHeaderLinksEnabled(props.allowHeadingAnchorLinks),
474
475
  allowColumnSorting: props.allowColumnSorting,
475
476
  allowCopyToClipboard: props.allowCopyToClipboard,
477
+ allowDownloadCodeBlock: props.allowDownloadCodeBlock,
476
478
  allowWrapCodeBlock: props.allowWrapCodeBlock,
477
479
  allowCustomPanels: props.allowCustomPanels,
478
480
  allowPlaceholderText: props.allowPlaceholderText,
@@ -512,6 +514,7 @@ export var RendererFunctionalComponent = function RendererFunctionalComponent(pr
512
514
  appearance: props.appearance,
513
515
  contentMode: props.contentMode || 'standard',
514
516
  allowCopyToClipboard: props.allowCopyToClipboard,
517
+ allowDownloadCodeBlock: props.allowDownloadCodeBlock,
515
518
  allowWrapCodeBlock: props.allowWrapCodeBlock,
516
519
  allowPlaceholderText: props.allowPlaceholderText,
517
520
  allowColumnSorting: props.allowColumnSorting,
@@ -48,6 +48,7 @@ export declare enum ACTION_SUBJECT_ID {
48
48
  HOVER_LABEL = "hoverLabel",
49
49
  INLINE_COMMENT = "inlineComment",
50
50
  CODEBLOCK_COPY = "codeBlockCopy",
51
+ CODEBLOCK_DOWNLOAD = "codeBlockDownload",
51
52
  CODEBLOCK_WRAP = "codeBlockWrap"
52
53
  }
53
54
  export type AEP<Action, ActionSubject, ActionSubjectID, Attributes, EventType> = {
@@ -16,6 +16,7 @@ export interface ReactSerializerInit {
16
16
  allowColumnSorting?: boolean;
17
17
  allowCopyToClipboard?: boolean;
18
18
  allowCustomPanels?: boolean;
19
+ allowDownloadCodeBlock?: boolean;
19
20
  allowFixedColumnWidthOption?: boolean;
20
21
  allowHeadingAnchorLinks?: HeadingAnchorLinksProps;
21
22
  allowMediaLinking?: boolean;
@@ -85,6 +86,7 @@ export default class ReactSerializer implements Serializer<JSX.Element> {
85
86
  private allowHeadingAnchorLinks?;
86
87
  private allowColumnSorting?;
87
88
  private allowCopyToClipboard?;
89
+ private allowDownloadCodeBlock?;
88
90
  private allowWrapCodeBlock?;
89
91
  private allowPlaceholderText?;
90
92
  private allowCustomPanels?;
@@ -7,6 +7,7 @@ import type { WithIntlProps, WrappedComponentProps } from 'react-intl';
7
7
  import type { SupportedLanguages } from '@atlaskit/code';
8
8
  export interface Props {
9
9
  allowCopyToClipboard?: boolean;
10
+ allowDownloadCodeBlock?: boolean;
10
11
  allowWrapCodeBlock?: boolean;
11
12
  className?: string;
12
13
  codeBidiWarningTooltipEnabled: boolean;
@@ -6,10 +6,12 @@ import { jsx } from '@emotion/react';
6
6
  import type { Dispatch, SetStateAction } from 'react';
7
7
  export interface CodeBlockButtonContainerProps {
8
8
  allowCopyToClipboard?: boolean;
9
+ allowDownloadCodeBlock?: boolean;
9
10
  allowWrapCodeBlock?: boolean;
11
+ language?: string | null;
10
12
  setWrapLongLines: Dispatch<SetStateAction<boolean>>;
11
13
  text: string;
12
14
  wrapLongLines: boolean;
13
15
  }
14
- declare const CodeBlockButtonContainer: ({ allowCopyToClipboard, allowWrapCodeBlock, setWrapLongLines, text, wrapLongLines, }: CodeBlockButtonContainerProps) => jsx.JSX.Element;
16
+ declare const CodeBlockButtonContainer: ({ allowCopyToClipboard, allowDownloadCodeBlock, allowWrapCodeBlock, language, setWrapLongLines, text, wrapLongLines, }: CodeBlockButtonContainerProps) => jsx.JSX.Element;
15
17
  export default CodeBlockButtonContainer;
@@ -10,5 +10,5 @@ interface ContainerProps extends CodeBlockButtonContainerProps {
10
10
  className?: string;
11
11
  localId?: string;
12
12
  }
13
- declare const CodeBlockContainer: ({ allowCopyToClipboard, allowWrapCodeBlock, children, className, localId, setWrapLongLines, text, wrapLongLines, }: ContainerProps) => jsx.JSX.Element;
13
+ declare const CodeBlockContainer: ({ allowCopyToClipboard, allowDownloadCodeBlock, allowWrapCodeBlock, children, className, language, localId, setWrapLongLines, text, wrapLongLines, }: ContainerProps) => jsx.JSX.Element;
14
14
  export default CodeBlockContainer;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @jsxRuntime classic
3
+ * @jsx jsx
4
+ */
5
+ import type { ComponentType, FC } from 'react';
6
+ import type { WithIntlProps, WrappedComponentProps } from 'react-intl';
7
+ type Props = {
8
+ content: string;
9
+ language: string | null;
10
+ };
11
+ declare const _default_1: FC<WithIntlProps<Props & WrappedComponentProps>> & {
12
+ WrappedComponent: ComponentType<Props & WrappedComponentProps>;
13
+ };
14
+ export default _default_1;
@@ -1,4 +1,4 @@
1
1
  import { jsx } from '@emotion/react';
2
2
  import type { Props as CodeBlockProps } from './codeBlock';
3
- declare const WindowedCodeBlock: ({ text, language, allowCopyToClipboard, allowWrapCodeBlock, codeBidiWarningTooltipEnabled, hideLineNumbers, className: rootClassName, wrap, }: CodeBlockProps) => jsx.JSX.Element;
3
+ declare const WindowedCodeBlock: ({ text, language, allowCopyToClipboard, allowDownloadCodeBlock, allowWrapCodeBlock, codeBidiWarningTooltipEnabled, hideLineNumbers, className: rootClassName, wrap, }: CodeBlockProps) => jsx.JSX.Element;
4
4
  export default WindowedCodeBlock;
@@ -18,6 +18,7 @@ export interface NodeMeta {
18
18
  [key: string]: any;
19
19
  allowCopyToClipboard?: boolean;
20
20
  allowCustomPanels?: boolean;
21
+ allowDownloadCodeBlock?: boolean;
21
22
  allowHeadingAnchorLinks?: HeadingAnchorLinksProps;
22
23
  allowPlaceholderText?: boolean;
23
24
  allowWrapCodeBlock?: boolean;
@@ -28,6 +28,7 @@ export type RendererWrapperProps = {
28
28
  allowColumnSorting?: boolean;
29
29
  allowCopyToClipboard?: boolean;
30
30
  allowCustomPanels?: boolean;
31
+ allowDownloadCodeBlock?: boolean;
31
32
  allowNestedHeaderLinks: boolean;
32
33
  allowPlaceholderText?: boolean;
33
34
  allowRendererContainerStyles?: boolean;
@@ -35,6 +35,7 @@ export interface RendererProps {
35
35
  allowColumnSorting?: boolean;
36
36
  allowCopyToClipboard?: boolean;
37
37
  allowCustomPanels?: boolean;
38
+ allowDownloadCodeBlock?: boolean;
38
39
  allowFixedColumnWidthOption?: boolean;
39
40
  allowHeadingAnchorLinks?: HeadingAnchorLinksProps;
40
41
  allowPlaceholderText?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/renderer",
3
- "version": "133.0.0",
3
+ "version": "133.2.0",
4
4
  "description": "Renderer component",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -44,7 +44,7 @@
44
44
  "@atlaskit/editor-prosemirror": "^8.0.0",
45
45
  "@atlaskit/editor-shared-styles": "^4.0.0",
46
46
  "@atlaskit/editor-smart-link-draggable": "^1.0.0",
47
- "@atlaskit/emoji": "^71.0.0",
47
+ "@atlaskit/emoji": "^71.1.0",
48
48
  "@atlaskit/feature-gate-js-client": "^6.0.0",
49
49
  "@atlaskit/icon": "^36.0.0",
50
50
  "@atlaskit/link": "^4.0.0",
@@ -66,7 +66,7 @@
66
66
  "@atlaskit/status": "^5.0.0",
67
67
  "@atlaskit/task-decision": "^21.0.0",
68
68
  "@atlaskit/theme": "^26.0.0",
69
- "@atlaskit/tmp-editor-statsig": "^104.0.0",
69
+ "@atlaskit/tmp-editor-statsig": "^105.0.0",
70
70
  "@atlaskit/tokens": "^14.0.0",
71
71
  "@atlaskit/tooltip": "^23.0.0",
72
72
  "@atlaskit/visually-hidden": "^4.0.0",
@@ -80,7 +80,7 @@
80
80
  "uuid": "^3.1.0"
81
81
  },
82
82
  "peerDependencies": {
83
- "@atlaskit/editor-common": "^116.0.0",
83
+ "@atlaskit/editor-common": "^116.2.0",
84
84
  "@atlaskit/link-provider": "^5.0.0",
85
85
  "@atlaskit/media-core": "^38.0.0",
86
86
  "react": "^18.2.0",