@atlaskit/renderer 132.7.0 → 133.1.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.
- package/CHANGELOG.md +48 -0
- package/dist/cjs/analytics/enums.js +1 -0
- package/dist/cjs/react/index.js +3 -0
- package/dist/cjs/react/nodes/codeBlock/codeBlock.js +5 -1
- package/dist/cjs/react/nodes/codeBlock/components/codeBlockButtonContainer.js +7 -1
- package/dist/cjs/react/nodes/codeBlock/components/codeBlockContainer.js +4 -0
- package/dist/cjs/react/nodes/codeBlock/components/codeBlockDownloadButton.js +133 -0
- package/dist/cjs/react/nodes/codeBlock/windowedCodeBlock.js +4 -1
- package/dist/cjs/react/nodes/layoutColumn-compiled.js +3 -1
- package/dist/cjs/react/nodes/layoutColumn-emotion.js +3 -1
- package/dist/cjs/react/utils/use-select-all-trap.js +4 -4
- package/dist/cjs/steps/index.js +4 -4
- package/dist/cjs/ui/Renderer/ErrorBoundary.js +4 -4
- package/dist/cjs/ui/Renderer/index.js +4 -1
- package/dist/cjs/ui/annotations/element/mark.js +5 -3
- package/dist/es2019/analytics/enums.js +1 -0
- package/dist/es2019/react/index.js +3 -0
- package/dist/es2019/react/nodes/codeBlock/codeBlock.js +4 -1
- package/dist/es2019/react/nodes/codeBlock/components/codeBlockButtonContainer.js +7 -1
- package/dist/es2019/react/nodes/codeBlock/components/codeBlockContainer.js +4 -0
- package/dist/es2019/react/nodes/codeBlock/components/codeBlockDownloadButton.js +123 -0
- package/dist/es2019/react/nodes/codeBlock/windowedCodeBlock.js +4 -1
- package/dist/es2019/react/nodes/layoutColumn-compiled.js +3 -1
- package/dist/es2019/react/nodes/layoutColumn-emotion.js +3 -1
- package/dist/es2019/react/utils/use-select-all-trap.js +5 -4
- package/dist/es2019/steps/index.js +5 -4
- package/dist/es2019/ui/Renderer/ErrorBoundary.js +5 -4
- package/dist/es2019/ui/Renderer/index.js +4 -1
- package/dist/es2019/ui/annotations/element/mark.js +5 -3
- package/dist/esm/analytics/enums.js +1 -0
- package/dist/esm/react/index.js +3 -0
- package/dist/esm/react/nodes/codeBlock/codeBlock.js +5 -1
- package/dist/esm/react/nodes/codeBlock/components/codeBlockButtonContainer.js +7 -1
- package/dist/esm/react/nodes/codeBlock/components/codeBlockContainer.js +4 -0
- package/dist/esm/react/nodes/codeBlock/components/codeBlockDownloadButton.js +125 -0
- package/dist/esm/react/nodes/codeBlock/windowedCodeBlock.js +4 -1
- package/dist/esm/react/nodes/layoutColumn-compiled.js +3 -1
- package/dist/esm/react/nodes/layoutColumn-emotion.js +3 -1
- package/dist/esm/react/utils/use-select-all-trap.js +5 -4
- package/dist/esm/steps/index.js +5 -4
- package/dist/esm/ui/Renderer/ErrorBoundary.js +5 -4
- package/dist/esm/ui/Renderer/index.js +4 -1
- package/dist/esm/ui/annotations/element/mark.js +5 -3
- package/dist/types/analytics/enums.d.ts +1 -0
- package/dist/types/react/index.d.ts +2 -0
- package/dist/types/react/nodes/codeBlock/codeBlock.d.ts +1 -0
- package/dist/types/react/nodes/codeBlock/components/codeBlockButtonContainer.d.ts +3 -1
- package/dist/types/react/nodes/codeBlock/components/codeBlockContainer.d.ts +1 -1
- package/dist/types/react/nodes/codeBlock/components/codeBlockDownloadButton.d.ts +14 -0
- package/dist/types/react/nodes/codeBlock/windowedCodeBlock.d.ts +1 -1
- package/dist/types/react/types.d.ts +1 -0
- package/dist/types/ui/Renderer/index.d.ts +1 -0
- package/dist/types/ui/renderer-props.d.ts +1 -0
- package/package.json +56 -56
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# @atlaskit/renderer
|
|
2
2
|
|
|
3
|
+
## 133.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`19869b1832800`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/19869b1832800) -
|
|
8
|
+
Add download button to code block toolbar in Rovo Chat. When enabled via the
|
|
9
|
+
rovo_chat_code_block_download feature gate, users can download code snippets (HTML, Python,
|
|
10
|
+
Markdown, etc.) directly as a file with the correct extension (e.g. rovo-snippet.html,
|
|
11
|
+
rovo-snippet.py). Adds allowDownloadCodeBlock prop to ReactRenderer (opt-in, defaults to false).
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies
|
|
16
|
+
|
|
17
|
+
## 133.0.0
|
|
18
|
+
|
|
19
|
+
### Major Changes
|
|
20
|
+
|
|
21
|
+
- [`f2dc9097319f0`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/f2dc9097319f0) - ###
|
|
22
|
+
Dropped support for _legacy_ Typescript 4 types. **Typescript 5 is now the new minimum**.
|
|
23
|
+
|
|
24
|
+
Removes the `typesVersions` property and `dist/types-ts4.5` directory from the dist.
|
|
25
|
+
|
|
26
|
+
Types are now exclusively via the `"types": "dist/types/index.d.ts"` property.
|
|
27
|
+
|
|
28
|
+
```diff
|
|
29
|
+
- "typesVersions": {
|
|
30
|
+
- ">=4.5 <4.9": {
|
|
31
|
+
- "*": [
|
|
32
|
+
- "dist/types-ts4.5/*",
|
|
33
|
+
- "dist/types-ts4.5/index.d.ts"
|
|
34
|
+
- ]
|
|
35
|
+
- }
|
|
36
|
+
- },
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Minor Changes
|
|
40
|
+
|
|
41
|
+
- [`458ee17c2c8ee`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/458ee17c2c8ee) -
|
|
42
|
+
Register layout column vertical alignment rendering experiment and gate renderer layout column
|
|
43
|
+
vertical alignment styles behind it
|
|
44
|
+
|
|
45
|
+
### Patch Changes
|
|
46
|
+
|
|
47
|
+
- [`eb0de97776ce5`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/eb0de97776ce5) -
|
|
48
|
+
cleanup to prefer static regex as part of ees019
|
|
49
|
+
- Updated dependencies
|
|
50
|
+
|
|
3
51
|
## 132.7.0
|
|
4
52
|
|
|
5
53
|
### Minor 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
|
}({});
|
package/dist/cjs/react/index.js
CHANGED
|
@@ -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
|
-
},
|
|
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,
|
|
@@ -28,6 +28,8 @@ var multipleWrappedImagesStyle = null;
|
|
|
28
28
|
var clearNextSiblingBlockMarkMarginTopStyle = null;
|
|
29
29
|
var LayoutSectionCompiled = exports.LayoutSectionCompiled = function LayoutSectionCompiled(props) {
|
|
30
30
|
var isLayoutColumnMenuEnabled = (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_layout_column_menu', 'isEnabled', true);
|
|
31
|
+
// Pure rollout gate: no A/B exposure analysis is planned for this rendering switch.
|
|
32
|
+
var isLayoutColumnValignRenderingEnabled = (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_layout_column_valign_rendering', 'isEnabled', true);
|
|
31
33
|
return /*#__PURE__*/_react.default.createElement("div", {
|
|
32
34
|
"data-layout-column": true,
|
|
33
35
|
"data-column-width": props.width,
|
|
@@ -37,7 +39,7 @@ var LayoutSectionCompiled = exports.LayoutSectionCompiled = function LayoutSecti
|
|
|
37
39
|
style: {
|
|
38
40
|
flexBasis: "".concat(props.width, "%")
|
|
39
41
|
},
|
|
40
|
-
className: (0, _runtime.ax)([isLayoutColumnMenuEnabled && props.valign === 'middle' && "_1e0c1txw _2lx21bp4 _1bah1h6o", isLayoutColumnMenuEnabled && props.valign === 'bottom' && "_1e0c1txw _2lx21bp4 _1bahesu3", (0, _platformFeatureFlags.fg)('platform_editor_fix_media_in_renderer') && "_1tihidpf _12kpidpf"])
|
|
42
|
+
className: (0, _runtime.ax)([(isLayoutColumnValignRenderingEnabled || isLayoutColumnMenuEnabled) && props.valign === 'middle' && "_1e0c1txw _2lx21bp4 _1bah1h6o", (isLayoutColumnValignRenderingEnabled || isLayoutColumnMenuEnabled) && props.valign === 'bottom' && "_1e0c1txw _2lx21bp4 _1bahesu3", (0, _platformFeatureFlags.fg)('platform_editor_fix_media_in_renderer') && "_1tihidpf _12kpidpf"])
|
|
41
43
|
}, /*#__PURE__*/_react.default.createElement(_ui.WidthProvider, null, /*#__PURE__*/_react.default.createElement("div", {
|
|
42
44
|
className: (0, _runtime.ax)(["_1skbgrf3", "_19segrf3 _1ki1grf3 _bmdegrf3 _166hgrf3 _7g1ogrf3 _sk2jgrf3 _hgeogrf3"])
|
|
43
45
|
}), props.children));
|
|
@@ -58,6 +58,8 @@ var clearNextSiblingBlockMarkMarginTopStyle = (0, _react2.css)((0, _defineProper
|
|
|
58
58
|
}));
|
|
59
59
|
var LayoutSectionEmotion = exports.LayoutSectionEmotion = function LayoutSectionEmotion(props) {
|
|
60
60
|
var isLayoutColumnMenuEnabled = (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_layout_column_menu', 'isEnabled', true);
|
|
61
|
+
// Pure rollout gate: no A/B exposure analysis is planned for this rendering switch.
|
|
62
|
+
var isLayoutColumnValignRenderingEnabled = (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_layout_column_valign_rendering', 'isEnabled', true);
|
|
61
63
|
return (0, _react2.jsx)("div", {
|
|
62
64
|
"data-layout-column": true,
|
|
63
65
|
"data-column-width": props.width,
|
|
@@ -69,7 +71,7 @@ var LayoutSectionEmotion = exports.LayoutSectionEmotion = function LayoutSection
|
|
|
69
71
|
},
|
|
70
72
|
css: [
|
|
71
73
|
// Keep separate: Compiled crashes on ternary/object lookup here.
|
|
72
|
-
isLayoutColumnMenuEnabled && props.valign === 'middle' && verticalAlignMiddleStyles, isLayoutColumnMenuEnabled && props.valign === 'bottom' && verticalAlignBottomStyles, (0, _platformFeatureFlags.fg)('platform_editor_fix_media_in_renderer') && multipleWrappedImagesStyle]
|
|
74
|
+
(isLayoutColumnValignRenderingEnabled || isLayoutColumnMenuEnabled) && props.valign === 'middle' && verticalAlignMiddleStyles, (isLayoutColumnValignRenderingEnabled || isLayoutColumnMenuEnabled) && props.valign === 'bottom' && verticalAlignBottomStyles, (0, _platformFeatureFlags.fg)('platform_editor_fix_media_in_renderer') && multipleWrappedImagesStyle]
|
|
73
75
|
}, (0, _react2.jsx)(_ui.WidthProvider, null, (0, _react2.jsx)("div", {
|
|
74
76
|
css: [clearNextSiblingMarginTopStyle, clearNextSiblingBlockMarkMarginTopStyle]
|
|
75
77
|
}), props.children));
|
|
@@ -9,16 +9,16 @@ var _analytics = require("@atlaskit/editor-common/analytics");
|
|
|
9
9
|
var _react = _interopRequireDefault(require("react"));
|
|
10
10
|
var _analyticsContext = _interopRequireDefault(require("../../analytics/analyticsContext"));
|
|
11
11
|
var _elementSelection = require("./element-selection");
|
|
12
|
+
// Ignored via go/ees005
|
|
13
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
14
|
+
var MAC_PLATFORM_REGEX = /Mac/;
|
|
12
15
|
var useSelectAllTrap = exports.useSelectAllTrap = function useSelectAllTrap() {
|
|
13
16
|
var _React$useContext = _react.default.useContext(_analyticsContext.default),
|
|
14
17
|
fireAnalyticsEvent = _React$useContext.fireAnalyticsEvent;
|
|
15
18
|
var ref = _react.default.useRef(null);
|
|
16
19
|
var clicked = _react.default.useRef(false);
|
|
17
20
|
var caught = _react.default.useRef();
|
|
18
|
-
|
|
19
|
-
// Ignored via go/ees005
|
|
20
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
21
|
-
var mac = typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false;
|
|
21
|
+
var mac = typeof navigator !== 'undefined' ? MAC_PLATFORM_REGEX.test(navigator.platform) : false;
|
|
22
22
|
var onKeyDown = _react.default.useCallback(function (e) {
|
|
23
23
|
var _e$target, _e$target$matches;
|
|
24
24
|
var el = ref.current;
|
package/dist/cjs/steps/index.js
CHANGED
|
@@ -13,6 +13,9 @@ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
|
13
13
|
var _transform = require("@atlaskit/editor-prosemirror/transform");
|
|
14
14
|
/* eslint-disable jsdoc/require-jsdoc -- internal step helpers */
|
|
15
15
|
|
|
16
|
+
// Ignored via go/ees005
|
|
17
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
18
|
+
var MEDIA_OR_MEDIA_SINGLE_REGEX = /media|mediaSingle/;
|
|
16
19
|
function getStartPos(element) {
|
|
17
20
|
return parseInt(element.dataset.rendererStartPos || '-1', 10);
|
|
18
21
|
}
|
|
@@ -231,10 +234,7 @@ function getPosFromRange(range) {
|
|
|
231
234
|
var possibleMediaOrMediaSingleElement = findParent(startContainer);
|
|
232
235
|
|
|
233
236
|
// Video hover targets return media single, not media, thus, the extra check in condition.
|
|
234
|
-
var isMediaOrMediaSingle = possibleMediaOrMediaSingleElement &&
|
|
235
|
-
// Ignored via go/ees005
|
|
236
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
237
|
-
/media|mediaSingle/.test(getNodeType(possibleMediaOrMediaSingleElement) || '');
|
|
237
|
+
var isMediaOrMediaSingle = possibleMediaOrMediaSingleElement && MEDIA_OR_MEDIA_SINGLE_REGEX.test(getNodeType(possibleMediaOrMediaSingleElement) || '');
|
|
238
238
|
if (isMediaOrMediaSingle) {
|
|
239
239
|
var pos;
|
|
240
240
|
var mediaSingleElement = getNodeType(possibleMediaOrMediaSingleElement) === 'mediaSingle' ? possibleMediaOrMediaSingleElement : findMediaParent(possibleMediaOrMediaSingleElement);
|
|
@@ -21,6 +21,9 @@ var _uuid = _interopRequireDefault(require("uuid"));
|
|
|
21
21
|
function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
|
|
22
22
|
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
23
23
|
// Ignored via go/ees005
|
|
24
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
25
|
+
var FAILED_TO_EXECUTE_REGEX = /Failed to execute.*on 'Node'.*/;
|
|
26
|
+
// Ignored via go/ees005
|
|
24
27
|
// eslint-disable-next-line @repo/internal/react/no-class-components
|
|
25
28
|
var ErrorBoundary = exports.ErrorBoundary = /*#__PURE__*/function (_React$Component) {
|
|
26
29
|
function ErrorBoundary() {
|
|
@@ -78,10 +81,7 @@ var ErrorBoundary = exports.ErrorBoundary = /*#__PURE__*/function (_React$Compon
|
|
|
78
81
|
(0, _monitoring.logException)(error, {
|
|
79
82
|
location: 'renderer'
|
|
80
83
|
});
|
|
81
|
-
|
|
82
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
83
|
-
var pattern = /Failed to execute.*on 'Node'.*/;
|
|
84
|
-
var matchesPattern = pattern.test(error.message);
|
|
84
|
+
var matchesPattern = FAILED_TO_EXECUTE_REGEX.test(error.message);
|
|
85
85
|
if (matchesPattern) {
|
|
86
86
|
this.fireAnalyticsEvent({
|
|
87
87
|
action: _analytics.ACTION.CAUGHT_DOM_ERROR,
|
|
@@ -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 = "
|
|
75
|
+
var packageVersion = "133.0.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,
|
|
@@ -88,10 +88,12 @@ var markStylesWithCommentsPanel = (0, _react2.css)((0, _defineProperty2.default)
|
|
|
88
88
|
boxShadow: "var(--ds-shadow-overlay, 0px 8px 12px #1E1F2126, 0px 0px 1px #1E1F214f)"
|
|
89
89
|
}
|
|
90
90
|
}));
|
|
91
|
+
|
|
92
|
+
// Ignored via go/ees005
|
|
93
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
94
|
+
var MOBILE_USER_AGENT_REGEX = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
|
|
91
95
|
var isMobile = function isMobile() {
|
|
92
|
-
|
|
93
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
94
|
-
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
96
|
+
return MOBILE_USER_AGENT_REGEX.test(navigator.userAgent);
|
|
95
97
|
};
|
|
96
98
|
var accessibilityStylesNew = (0, _react2.css)({
|
|
97
99
|
'&::before, &::after': {
|
|
@@ -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
|
-
},
|
|
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
|