@atlaskit/editor-plugin-code-block-advanced 1.0.2 → 1.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 +21 -0
- package/afm-cc/tsconfig.json +6 -0
- package/afm-jira/tsconfig.json +6 -0
- package/afm-post-office/tsconfig.json +6 -0
- package/dist/cjs/nodeviews/codeBlockAdvanced.js +29 -26
- package/dist/cjs/nodeviews/codemirrorSync/syncCMWithPM.js +0 -1
- package/dist/cjs/nodeviews/extensions/keymap/index.js +23 -3
- package/dist/cjs/nodeviews/extensions/manageSelectionMarker.js +28 -0
- package/dist/cjs/nodeviews/extensions/prosemirrorDecorations.js +134 -0
- package/dist/cjs/ui/theme.js +20 -2
- package/dist/es2019/nodeviews/codeBlockAdvanced.js +27 -27
- package/dist/es2019/nodeviews/codemirrorSync/syncCMWithPM.js +0 -1
- package/dist/es2019/nodeviews/extensions/keymap/index.js +23 -3
- package/dist/es2019/nodeviews/extensions/manageSelectionMarker.js +22 -0
- package/dist/es2019/nodeviews/extensions/prosemirrorDecorations.js +99 -0
- package/dist/es2019/ui/theme.js +62 -2
- package/dist/esm/nodeviews/codeBlockAdvanced.js +30 -27
- package/dist/esm/nodeviews/codemirrorSync/syncCMWithPM.js +0 -1
- package/dist/esm/nodeviews/extensions/keymap/index.js +23 -3
- package/dist/esm/nodeviews/extensions/manageSelectionMarker.js +22 -0
- package/dist/esm/nodeviews/extensions/prosemirrorDecorations.js +127 -0
- package/dist/esm/ui/theme.js +20 -2
- package/dist/types/codeBlockAdvancedPluginType.d.ts +9 -1
- package/dist/types/nodeviews/codeBlockAdvanced.d.ts +10 -5
- package/dist/types/nodeviews/extensions/keymap/index.d.ts +2 -1
- package/dist/types/nodeviews/extensions/manageSelectionMarker.d.ts +10 -0
- package/dist/types/nodeviews/extensions/prosemirrorDecorations.d.ts +20 -0
- package/dist/types-ts4.5/codeBlockAdvancedPluginType.d.ts +5 -1
- package/dist/types-ts4.5/nodeviews/codeBlockAdvanced.d.ts +10 -5
- package/dist/types-ts4.5/nodeviews/extensions/keymap/index.d.ts +2 -1
- package/dist/types-ts4.5/nodeviews/extensions/manageSelectionMarker.d.ts +10 -0
- package/dist/types-ts4.5/nodeviews/extensions/prosemirrorDecorations.d.ts +20 -0
- package/package.json +10 -8
- package/src/codeBlockAdvancedPluginType.ts +9 -1
- package/src/nodeviews/codeBlockAdvanced.ts +31 -30
- package/src/nodeviews/codemirrorSync/syncCMWithPM.ts +0 -1
- package/src/nodeviews/extensions/keymap/index.ts +31 -1
- package/src/nodeviews/extensions/manageSelectionMarker.ts +28 -0
- package/src/nodeviews/extensions/prosemirrorDecorations.ts +148 -0
- package/src/ui/theme.ts +64 -0
- package/tsconfig.app.json +6 -0
- package/dist/cjs/nodeviews/extensions/bidiCharWarning.js +0 -83
- package/dist/cjs/nodeviews/extensions/copyButtonDecorations.js +0 -22
- package/dist/es2019/nodeviews/extensions/bidiCharWarning.js +0 -53
- package/dist/es2019/nodeviews/extensions/copyButtonDecorations.js +0 -16
- package/dist/esm/nodeviews/extensions/bidiCharWarning.js +0 -77
- package/dist/esm/nodeviews/extensions/copyButtonDecorations.js +0 -16
- package/dist/types/nodeviews/extensions/bidiCharWarning.d.ts +0 -8
- package/dist/types/nodeviews/extensions/copyButtonDecorations.d.ts +0 -1
- package/dist/types-ts4.5/nodeviews/extensions/bidiCharWarning.d.ts +0 -8
- package/dist/types-ts4.5/nodeviews/extensions/copyButtonDecorations.d.ts +0 -1
- package/src/nodeviews/extensions/bidiCharWarning.ts +0 -72
- package/src/nodeviews/extensions/copyButtonDecorations.ts +0 -15
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defaultKeymap, indentWithTab } from '@codemirror/commands';
|
|
2
2
|
import { keymap as cmKeymap } from '@codemirror/view';
|
|
3
|
+
import { browser } from '@atlaskit/editor-common/browser';
|
|
3
4
|
import { exitCode } from '@atlaskit/editor-prosemirror/commands';
|
|
4
5
|
import { undo, redo } from '@atlaskit/editor-prosemirror/history';
|
|
5
6
|
import { backspaceKeymap } from './backspace';
|
|
@@ -9,14 +10,16 @@ export const keymapExtension = ({
|
|
|
9
10
|
getNode,
|
|
10
11
|
getPos,
|
|
11
12
|
selectCodeBlockNode,
|
|
12
|
-
onMaybeNodeSelection
|
|
13
|
+
onMaybeNodeSelection,
|
|
14
|
+
customFindReplace
|
|
13
15
|
}) => {
|
|
14
16
|
return cmKeymap.of(codeBlockKeymap({
|
|
15
17
|
view,
|
|
16
18
|
getNode,
|
|
17
19
|
getPos,
|
|
18
20
|
selectCodeBlockNode,
|
|
19
|
-
onMaybeNodeSelection
|
|
21
|
+
onMaybeNodeSelection,
|
|
22
|
+
customFindReplace
|
|
20
23
|
}));
|
|
21
24
|
};
|
|
22
25
|
const codeBlockKeymap = ({
|
|
@@ -24,7 +27,8 @@ const codeBlockKeymap = ({
|
|
|
24
27
|
getNode,
|
|
25
28
|
getPos,
|
|
26
29
|
selectCodeBlockNode,
|
|
27
|
-
onMaybeNodeSelection
|
|
30
|
+
onMaybeNodeSelection,
|
|
31
|
+
customFindReplace
|
|
28
32
|
}) => {
|
|
29
33
|
return [{
|
|
30
34
|
key: 'ArrowUp',
|
|
@@ -74,6 +78,22 @@ const codeBlockKeymap = ({
|
|
|
74
78
|
selectCodeBlockNode,
|
|
75
79
|
onMaybeNodeSelection
|
|
76
80
|
})
|
|
81
|
+
}, {
|
|
82
|
+
key: 'Ctrl-f',
|
|
83
|
+
mac: 'Cmd-f',
|
|
84
|
+
run: () => {
|
|
85
|
+
// Pass synthetic event to prosemirror
|
|
86
|
+
if (customFindReplace) {
|
|
87
|
+
view.dispatchEvent(new KeyboardEvent('keydown', {
|
|
88
|
+
key: 'f',
|
|
89
|
+
code: 'KeyF',
|
|
90
|
+
metaKey: browser.mac ? true : false,
|
|
91
|
+
ctrlKey: browser.mac ? false : true
|
|
92
|
+
}));
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
77
97
|
}, {
|
|
78
98
|
key: 'Ctrl-Enter',
|
|
79
99
|
run: () => {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { EditorView as CodeMirror } from '@codemirror/view';
|
|
2
|
+
/**
|
|
3
|
+
* Hides selection marker decoration when focused on codemirror editor and re-enables on blur
|
|
4
|
+
*
|
|
5
|
+
* @param api
|
|
6
|
+
* @returns CodeMirror Extension
|
|
7
|
+
*/
|
|
8
|
+
export const manageSelectionMarker = api => {
|
|
9
|
+
let decoHide;
|
|
10
|
+
return CodeMirror.focusChangeEffect.of((_state, focusing) => {
|
|
11
|
+
if (focusing) {
|
|
12
|
+
var _api$selectionMarker;
|
|
13
|
+
api === null || api === void 0 ? void 0 : (_api$selectionMarker = api.selectionMarker) === null || _api$selectionMarker === void 0 ? void 0 : _api$selectionMarker.actions.queueHideDecoration(hideDecoration => {
|
|
14
|
+
decoHide = hideDecoration;
|
|
15
|
+
});
|
|
16
|
+
} else {
|
|
17
|
+
var _decoHide;
|
|
18
|
+
(_decoHide = decoHide) === null || _decoHide === void 0 ? void 0 : _decoHide();
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
});
|
|
22
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { ViewPlugin, WidgetType, Decoration as CodeMirrorDecoration } from '@codemirror/view';
|
|
2
|
+
class PMWidget extends WidgetType {
|
|
3
|
+
constructor(toDOMElement) {
|
|
4
|
+
super();
|
|
5
|
+
this.toDOMElement = toDOMElement;
|
|
6
|
+
}
|
|
7
|
+
toDOM() {
|
|
8
|
+
return this.toDOMElement;
|
|
9
|
+
}
|
|
10
|
+
ignoreEvent() {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// This type is not exposed publically by ProseMirror but we need it to map to CodeMirror
|
|
16
|
+
// See: https://github.com/ProseMirror/prosemirror-view/blob/master/src/decoration.ts
|
|
17
|
+
|
|
18
|
+
// This type is not exposed publically by ProseMirror but we need it to map to CodeMirror
|
|
19
|
+
// See: https://github.com/ProseMirror/prosemirror-view/blob/master/src/decoration.ts
|
|
20
|
+
|
|
21
|
+
// This type is not exposed publically by ProseMirror but we need it to map to CodeMirror
|
|
22
|
+
// See: https://github.com/ProseMirror/prosemirror-view/blob/master/src/decoration.ts
|
|
23
|
+
function isExtendedDecoration(decoration) {
|
|
24
|
+
return decoration.inline !== undefined && decoration.widget !== undefined && decoration.type !== undefined;
|
|
25
|
+
}
|
|
26
|
+
const getHTMLElement = (toDOM, view, getPos) => {
|
|
27
|
+
if (toDOM instanceof Function) {
|
|
28
|
+
const element = toDOM(view, getPos);
|
|
29
|
+
return element instanceof HTMLElement ? element : undefined;
|
|
30
|
+
} else if (toDOM instanceof HTMLElement) {
|
|
31
|
+
return toDOM;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const mapPMDecorationToCMDecoration = (decoration, view, getPos) => {
|
|
35
|
+
if (!isExtendedDecoration(decoration)) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
if (decoration.inline) {
|
|
39
|
+
const markDecoration = CodeMirrorDecoration.mark({
|
|
40
|
+
attributes: decoration.type.attrs
|
|
41
|
+
});
|
|
42
|
+
return markDecoration.range(decoration.from, decoration.to);
|
|
43
|
+
} else if (decoration.widget) {
|
|
44
|
+
var _decoration$type;
|
|
45
|
+
const toDOM = getHTMLElement(decoration === null || decoration === void 0 ? void 0 : (_decoration$type = decoration.type) === null || _decoration$type === void 0 ? void 0 : _decoration$type.toDOM, view, getPos);
|
|
46
|
+
if (!toDOM) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
const widgetDecoration = CodeMirrorDecoration.widget({
|
|
50
|
+
widget: new PMWidget(toDOM),
|
|
51
|
+
side: decoration.type.side
|
|
52
|
+
});
|
|
53
|
+
return widgetDecoration.range(decoration.from, decoration.to);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
function isDefined(value) {
|
|
57
|
+
return value !== undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Creates CodeMirror versions of the decorations provided by ProseMirror.
|
|
62
|
+
*
|
|
63
|
+
* Inline ProseMirror decorations -> Mark CodeMirror decorations
|
|
64
|
+
* Widget ProseMirror decorations -> Widget CodeMirror decorations
|
|
65
|
+
*
|
|
66
|
+
* This way any decorations applied in ProseMirror land should automatically be supported
|
|
67
|
+
* by the CodeMirror editor
|
|
68
|
+
*
|
|
69
|
+
* @param updateDecorationsEffect Facet for the prosemirror decorations
|
|
70
|
+
* @returns CodeMirror extension
|
|
71
|
+
*/
|
|
72
|
+
export const prosemirrorDecorationPlugin = (updateDecorationsEffect, editorView, getPos) => ViewPlugin.fromClass(class {
|
|
73
|
+
constructor(view) {
|
|
74
|
+
this.decorations = this.updateDecorations(view);
|
|
75
|
+
}
|
|
76
|
+
updateDecorations(view) {
|
|
77
|
+
const {
|
|
78
|
+
from,
|
|
79
|
+
to
|
|
80
|
+
} = view.viewport;
|
|
81
|
+
const innnerDecorations = view.state.facet(updateDecorationsEffect);
|
|
82
|
+
const allDecorations = [];
|
|
83
|
+
innnerDecorations === null || innnerDecorations === void 0 ? void 0 : innnerDecorations.map(source => {
|
|
84
|
+
source === null || source === void 0 ? void 0 : source.forEachSet(set => {
|
|
85
|
+
const decorations = set.find(from, to)
|
|
86
|
+
// Do not render the code block line decorations
|
|
87
|
+
.filter(dec => dec.spec.type !== 'decorationWidgetType');
|
|
88
|
+
allDecorations.push(...decorations);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
const cmDecorations = allDecorations.sort((a, b) => a.from < b.from ? -1 : 1).map(decoration => mapPMDecorationToCMDecoration(decoration, editorView, getPos)).filter(isDefined);
|
|
92
|
+
return CodeMirrorDecoration.set(cmDecorations);
|
|
93
|
+
}
|
|
94
|
+
update(update) {
|
|
95
|
+
this.decorations = this.updateDecorations(update.view);
|
|
96
|
+
}
|
|
97
|
+
}, {
|
|
98
|
+
decorations: v => v.decorations
|
|
99
|
+
});
|
package/dist/es2019/ui/theme.js
CHANGED
|
@@ -5,6 +5,7 @@ export const cmTheme = CodeMirror.theme({
|
|
|
5
5
|
padding: '0',
|
|
6
6
|
marginTop: "var(--ds-space-100, 8px)",
|
|
7
7
|
marginBottom: "var(--ds-space-100, 8px)",
|
|
8
|
+
// eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
|
|
8
9
|
fontSize: '0.875rem',
|
|
9
10
|
// Custom syntax styling to match existing styling
|
|
10
11
|
// eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
|
|
@@ -31,7 +32,11 @@ export const cmTheme = CodeMirror.theme({
|
|
|
31
32
|
// eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
|
|
32
33
|
lineHeight: 'unset',
|
|
33
34
|
fontFamily: "var(--ds-font-family-code, ui-monospace, Menlo, \"Segoe UI Mono\", \"Ubuntu Mono\", monospace)",
|
|
34
|
-
borderRadius: "var(--ds-border-radius, 4px)"
|
|
35
|
+
borderRadius: "var(--ds-border-radius, 4px)",
|
|
36
|
+
backgroundImage: overflowShadow({
|
|
37
|
+
leftCoverWidth: "var(--ds-space-300, 24px)"
|
|
38
|
+
}),
|
|
39
|
+
backgroundAttachment: 'local, local, local, local, scroll, scroll, scroll, scroll'
|
|
35
40
|
},
|
|
36
41
|
'&.cm-focused .cm-cursor': {
|
|
37
42
|
borderLeftColor: "var(--ds-text, #172B4D)"
|
|
@@ -47,4 +52,59 @@ export const cmTheme = CodeMirror.theme({
|
|
|
47
52
|
paddingRight: "var(--ds-space-0, 0px)",
|
|
48
53
|
minWidth: 'unset'
|
|
49
54
|
}
|
|
50
|
-
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Copied directly from `packages/editor/editor-shared-styles/src/overflow-shadow/overflow-shadow.ts`
|
|
59
|
+
* `CodeMirror` does not support emotion styling so this has been re-created.
|
|
60
|
+
*/
|
|
61
|
+
function overflowShadow({
|
|
62
|
+
leftCoverWidth,
|
|
63
|
+
rightCoverWidth
|
|
64
|
+
}) {
|
|
65
|
+
const width = "var(--ds-space-100, 8px)";
|
|
66
|
+
const leftCoverWidthResolved = leftCoverWidth || width;
|
|
67
|
+
const rightCoverWidthResolved = rightCoverWidth || width;
|
|
68
|
+
return `
|
|
69
|
+
linear-gradient(
|
|
70
|
+
to right,
|
|
71
|
+
${"var(--ds-background-neutral, #091E420F)"} ${leftCoverWidthResolved},
|
|
72
|
+
transparent ${leftCoverWidthResolved}
|
|
73
|
+
),
|
|
74
|
+
linear-gradient(
|
|
75
|
+
to right,
|
|
76
|
+
${"var(--ds-surface-raised, #FFFFFF)"} ${leftCoverWidthResolved},
|
|
77
|
+
transparent ${leftCoverWidthResolved}
|
|
78
|
+
),
|
|
79
|
+
linear-gradient(
|
|
80
|
+
to left,
|
|
81
|
+
${"var(--ds-background-neutral, #091E420F)"} ${rightCoverWidthResolved},
|
|
82
|
+
transparent ${rightCoverWidthResolved}
|
|
83
|
+
),
|
|
84
|
+
linear-gradient(
|
|
85
|
+
to left,
|
|
86
|
+
${"var(--ds-surface-raised, #FFFFFF)"} ${rightCoverWidthResolved},
|
|
87
|
+
transparent ${rightCoverWidthResolved}
|
|
88
|
+
),
|
|
89
|
+
linear-gradient(
|
|
90
|
+
to left,
|
|
91
|
+
${"var(--ds-shadow-overflow-spread, #091e4229)"} 0,
|
|
92
|
+
${"var(--ds-UNSAFE-transparent, transparent)"} ${width}
|
|
93
|
+
),
|
|
94
|
+
linear-gradient(
|
|
95
|
+
to left,
|
|
96
|
+
${"var(--ds-shadow-overflow-perimeter, #091e421f)"} 0,
|
|
97
|
+
${"var(--ds-UNSAFE-transparent, transparent)"} ${width}
|
|
98
|
+
),
|
|
99
|
+
linear-gradient(
|
|
100
|
+
to right,
|
|
101
|
+
${"var(--ds-shadow-overflow-spread, #091e4229)"} 0,
|
|
102
|
+
${"var(--ds-UNSAFE-transparent, transparent)"} ${width}
|
|
103
|
+
),
|
|
104
|
+
linear-gradient(
|
|
105
|
+
to right,
|
|
106
|
+
${"var(--ds-shadow-overflow-perimeter, #091e421f)"} 0,
|
|
107
|
+
${"var(--ds-UNSAFE-transparent, transparent)"} ${width}
|
|
108
|
+
)
|
|
109
|
+
`;
|
|
110
|
+
}
|
|
@@ -4,7 +4,7 @@ import _createClass from "@babel/runtime/helpers/createClass";
|
|
|
4
4
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
5
5
|
import { closeBrackets } from '@codemirror/autocomplete';
|
|
6
6
|
import { syntaxHighlighting, bracketMatching } from '@codemirror/language';
|
|
7
|
-
import { Compartment, EditorSelection } from '@codemirror/state';
|
|
7
|
+
import { Compartment, EditorSelection, Facet } from '@codemirror/state';
|
|
8
8
|
import { EditorView as CodeMirror, lineNumbers, gutters } from '@codemirror/view';
|
|
9
9
|
import { isCodeBlockWordWrapEnabled } from '@atlaskit/editor-common/code-block';
|
|
10
10
|
import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
|
|
@@ -12,13 +12,12 @@ import { highlightStyle } from '../ui/syntaxHighlightingTheme';
|
|
|
12
12
|
import { cmTheme } from '../ui/theme';
|
|
13
13
|
import { syncCMWithPM } from './codemirrorSync/syncCMWithPM';
|
|
14
14
|
import { updateCMSelection } from './codemirrorSync/updateCMSelection';
|
|
15
|
-
import { bidiCharWarningExtension } from './extensions/bidiCharWarning';
|
|
16
|
-
import { copyButtonDecorations } from './extensions/copyButtonDecorations';
|
|
17
15
|
import { keymapExtension } from './extensions/keymap';
|
|
16
|
+
import { manageSelectionMarker } from './extensions/manageSelectionMarker';
|
|
17
|
+
import { prosemirrorDecorationPlugin } from './extensions/prosemirrorDecorations';
|
|
18
18
|
import { LanguageLoader } from './languages/loader';
|
|
19
19
|
// Based on: https://prosemirror.net/examples/codemirror/
|
|
20
20
|
var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/max-params
|
|
22
21
|
function CodeBlockAdvancedNodeView(node, view, getPos, config) {
|
|
23
22
|
var _config$api,
|
|
24
23
|
_this = this,
|
|
@@ -28,8 +27,9 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
|
|
|
28
27
|
_defineProperty(this, "lineWrappingCompartment", new Compartment());
|
|
29
28
|
_defineProperty(this, "languageCompartment", new Compartment());
|
|
30
29
|
_defineProperty(this, "readOnlyCompartment", new Compartment());
|
|
31
|
-
_defineProperty(this, "
|
|
30
|
+
_defineProperty(this, "pmDecorationsCompartment", new Compartment());
|
|
32
31
|
_defineProperty(this, "maybeTryingToReachNodeSelection", false);
|
|
32
|
+
_defineProperty(this, "pmFacet", Facet.define());
|
|
33
33
|
_defineProperty(this, "wordWrappingEnabled", false);
|
|
34
34
|
this.node = node;
|
|
35
35
|
this.view = view;
|
|
@@ -44,13 +44,6 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
|
|
|
44
44
|
this.cleanupDisabledState = (_config$api2 = config.api) === null || _config$api2 === void 0 || (_config$api2 = _config$api2.editorDisabled) === null || _config$api2 === void 0 ? void 0 : _config$api2.sharedState.onChange(function () {
|
|
45
45
|
_this.updateReadonlyState();
|
|
46
46
|
});
|
|
47
|
-
this.cleanupCopyButtonDecoration = (_config$api3 = config.api) === null || _config$api3 === void 0 || (_config$api3 = _config$api3.codeBlock) === null || _config$api3 === void 0 ? void 0 : _config$api3.sharedState.onChange(function (_ref) {
|
|
48
|
-
var nextSharedState = _ref.nextSharedState,
|
|
49
|
-
prevSharedState = _ref.prevSharedState;
|
|
50
|
-
if ((nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.copyButtonHoverNode) !== (prevSharedState === null || prevSharedState === void 0 ? void 0 : prevSharedState.copyButtonHoverNode)) {
|
|
51
|
-
_this.addCopyButtonDecoration(nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.copyButtonHoverNode);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
47
|
this.languageLoader = new LanguageLoader(function (lang) {
|
|
55
48
|
_this.updating = true;
|
|
56
49
|
_this.cm.dispatch({
|
|
@@ -60,12 +53,13 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
|
|
|
60
53
|
});
|
|
61
54
|
this.cm = new CodeMirror({
|
|
62
55
|
doc: this.node.textContent,
|
|
63
|
-
extensions: [].concat(_toConsumableArray(config.extensions), [this.lineWrappingCompartment.of([]), this.languageCompartment.of([]), this.
|
|
56
|
+
extensions: [].concat(_toConsumableArray(config.extensions), [this.lineWrappingCompartment.of([]), this.languageCompartment.of([]), this.pmDecorationsCompartment.of([]), keymapExtension({
|
|
64
57
|
view: view,
|
|
65
58
|
getPos: getPos,
|
|
66
59
|
getNode: getNode,
|
|
67
60
|
selectCodeBlockNode: this.selectCodeBlockNode.bind(this),
|
|
68
|
-
onMaybeNodeSelection: onMaybeNodeSelection
|
|
61
|
+
onMaybeNodeSelection: onMaybeNodeSelection,
|
|
62
|
+
customFindReplace: Boolean((_config$api3 = config.api) === null || _config$api3 === void 0 ? void 0 : _config$api3.findReplace)
|
|
69
63
|
}), cmTheme, syntaxHighlighting(highlightStyle), bracketMatching(), lineNumbers(),
|
|
70
64
|
// Explicitly disable "sticky" positioning on line numbers to match
|
|
71
65
|
// Renderer behaviour
|
|
@@ -75,7 +69,7 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
|
|
|
75
69
|
return _this.forwardUpdate(update);
|
|
76
70
|
}), this.readOnlyCompartment.of(CodeMirror.editable.of(this.view.editable)), closeBrackets(), CodeMirror.editorAttributes.of({
|
|
77
71
|
class: 'code-block'
|
|
78
|
-
}),
|
|
72
|
+
}), manageSelectionMarker(config.api), prosemirrorDecorationPlugin(this.pmFacet, view, getPos)])
|
|
79
73
|
});
|
|
80
74
|
|
|
81
75
|
// The editor's outer node is our DOM representation
|
|
@@ -89,9 +83,8 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
|
|
|
89
83
|
return _createClass(CodeBlockAdvancedNodeView, [{
|
|
90
84
|
key: "destroy",
|
|
91
85
|
value: function destroy() {
|
|
92
|
-
var _this$cleanupDisabled
|
|
86
|
+
var _this$cleanupDisabled;
|
|
93
87
|
(_this$cleanupDisabled = this.cleanupDisabledState) === null || _this$cleanupDisabled === void 0 || _this$cleanupDisabled.call(this);
|
|
94
|
-
(_this$cleanupCopyButt = this.cleanupCopyButtonDecoration) === null || _this$cleanupCopyButt === void 0 || _this$cleanupCopyButt.call(this);
|
|
95
88
|
}
|
|
96
89
|
}, {
|
|
97
90
|
key: "forwardUpdate",
|
|
@@ -148,15 +141,6 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
|
|
|
148
141
|
this.view.dispatch(tr);
|
|
149
142
|
}
|
|
150
143
|
}
|
|
151
|
-
}, {
|
|
152
|
-
key: "addCopyButtonDecoration",
|
|
153
|
-
value: function addCopyButtonDecoration(node) {
|
|
154
|
-
this.updating = true;
|
|
155
|
-
this.cm.dispatch({
|
|
156
|
-
effects: [this.copyDecoCompartment.reconfigure(node && node === this.node ? copyButtonDecorations : [])]
|
|
157
|
-
});
|
|
158
|
-
this.updating = false;
|
|
159
|
-
}
|
|
160
144
|
}, {
|
|
161
145
|
key: "updateWordWrap",
|
|
162
146
|
value: function updateWordWrap(node) {
|
|
@@ -171,7 +155,7 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
|
|
|
171
155
|
}
|
|
172
156
|
}, {
|
|
173
157
|
key: "update",
|
|
174
|
-
value: function update(node) {
|
|
158
|
+
value: function update(node, _, innerDecorations) {
|
|
175
159
|
var _this2 = this;
|
|
176
160
|
this.maybeTryingToReachNodeSelection = false;
|
|
177
161
|
if (node.type !== this.node.type) {
|
|
@@ -190,8 +174,27 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
|
|
|
190
174
|
_this2.cm.dispatch(tr);
|
|
191
175
|
_this2.updating = false;
|
|
192
176
|
});
|
|
177
|
+
this.updateProseMirrorDecorations(innerDecorations);
|
|
193
178
|
return true;
|
|
194
179
|
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Updates a facet which stores information on the prosemirror decorations
|
|
183
|
+
*
|
|
184
|
+
* This then gets translated to codemirror decorations in `prosemirrorDecorationPlugin`
|
|
185
|
+
*/
|
|
186
|
+
}, {
|
|
187
|
+
key: "updateProseMirrorDecorations",
|
|
188
|
+
value: function updateProseMirrorDecorations(decorationSource) {
|
|
189
|
+
this.updating = true;
|
|
190
|
+
var computedFacet = this.pmFacet.compute([], function () {
|
|
191
|
+
return decorationSource;
|
|
192
|
+
});
|
|
193
|
+
this.cm.dispatch({
|
|
194
|
+
effects: this.pmDecorationsCompartment.reconfigure(computedFacet)
|
|
195
|
+
});
|
|
196
|
+
this.updating = false;
|
|
197
|
+
}
|
|
195
198
|
}, {
|
|
196
199
|
key: "stopEvent",
|
|
197
200
|
value: function stopEvent(e) {
|
|
@@ -17,7 +17,6 @@ export var syncCMWithPM = function syncCMWithPM(_ref) {
|
|
|
17
17
|
var pmSel = view.state.selection;
|
|
18
18
|
if (update.docChanged || pmSel.from !== selFrom || pmSel.to !== selTo) {
|
|
19
19
|
var tr = view.state.tr;
|
|
20
|
-
// eslint-disable-next-line @typescript-eslint/max-params
|
|
21
20
|
update.changes.iterChanges(function (fromA, toA, fromB, toB, text) {
|
|
22
21
|
if (text.length) {
|
|
23
22
|
tr.replaceWith(offset + fromA, offset + toA, view.state.schema.text(text.toString()));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
2
2
|
import { defaultKeymap, indentWithTab } from '@codemirror/commands';
|
|
3
3
|
import { keymap as cmKeymap } from '@codemirror/view';
|
|
4
|
+
import { browser } from '@atlaskit/editor-common/browser';
|
|
4
5
|
import { exitCode } from '@atlaskit/editor-prosemirror/commands';
|
|
5
6
|
import { undo, redo } from '@atlaskit/editor-prosemirror/history';
|
|
6
7
|
import { backspaceKeymap } from './backspace';
|
|
@@ -10,13 +11,15 @@ export var keymapExtension = function keymapExtension(_ref) {
|
|
|
10
11
|
getNode = _ref.getNode,
|
|
11
12
|
getPos = _ref.getPos,
|
|
12
13
|
selectCodeBlockNode = _ref.selectCodeBlockNode,
|
|
13
|
-
onMaybeNodeSelection = _ref.onMaybeNodeSelection
|
|
14
|
+
onMaybeNodeSelection = _ref.onMaybeNodeSelection,
|
|
15
|
+
customFindReplace = _ref.customFindReplace;
|
|
14
16
|
return cmKeymap.of(codeBlockKeymap({
|
|
15
17
|
view: view,
|
|
16
18
|
getNode: getNode,
|
|
17
19
|
getPos: getPos,
|
|
18
20
|
selectCodeBlockNode: selectCodeBlockNode,
|
|
19
|
-
onMaybeNodeSelection: onMaybeNodeSelection
|
|
21
|
+
onMaybeNodeSelection: onMaybeNodeSelection,
|
|
22
|
+
customFindReplace: customFindReplace
|
|
20
23
|
}));
|
|
21
24
|
};
|
|
22
25
|
var codeBlockKeymap = function codeBlockKeymap(_ref2) {
|
|
@@ -24,7 +27,8 @@ var codeBlockKeymap = function codeBlockKeymap(_ref2) {
|
|
|
24
27
|
getNode = _ref2.getNode,
|
|
25
28
|
getPos = _ref2.getPos,
|
|
26
29
|
selectCodeBlockNode = _ref2.selectCodeBlockNode,
|
|
27
|
-
onMaybeNodeSelection = _ref2.onMaybeNodeSelection
|
|
30
|
+
onMaybeNodeSelection = _ref2.onMaybeNodeSelection,
|
|
31
|
+
customFindReplace = _ref2.customFindReplace;
|
|
28
32
|
return [{
|
|
29
33
|
key: 'ArrowUp',
|
|
30
34
|
run: function run(cm) {
|
|
@@ -81,6 +85,22 @@ var codeBlockKeymap = function codeBlockKeymap(_ref2) {
|
|
|
81
85
|
onMaybeNodeSelection: onMaybeNodeSelection
|
|
82
86
|
});
|
|
83
87
|
}
|
|
88
|
+
}, {
|
|
89
|
+
key: 'Ctrl-f',
|
|
90
|
+
mac: 'Cmd-f',
|
|
91
|
+
run: function run() {
|
|
92
|
+
// Pass synthetic event to prosemirror
|
|
93
|
+
if (customFindReplace) {
|
|
94
|
+
view.dispatchEvent(new KeyboardEvent('keydown', {
|
|
95
|
+
key: 'f',
|
|
96
|
+
code: 'KeyF',
|
|
97
|
+
metaKey: browser.mac ? true : false,
|
|
98
|
+
ctrlKey: browser.mac ? false : true
|
|
99
|
+
}));
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
84
104
|
}, {
|
|
85
105
|
key: 'Ctrl-Enter',
|
|
86
106
|
run: function run() {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { EditorView as CodeMirror } from '@codemirror/view';
|
|
2
|
+
/**
|
|
3
|
+
* Hides selection marker decoration when focused on codemirror editor and re-enables on blur
|
|
4
|
+
*
|
|
5
|
+
* @param api
|
|
6
|
+
* @returns CodeMirror Extension
|
|
7
|
+
*/
|
|
8
|
+
export var manageSelectionMarker = function manageSelectionMarker(api) {
|
|
9
|
+
var decoHide;
|
|
10
|
+
return CodeMirror.focusChangeEffect.of(function (_state, focusing) {
|
|
11
|
+
if (focusing) {
|
|
12
|
+
var _api$selectionMarker;
|
|
13
|
+
api === null || api === void 0 || (_api$selectionMarker = api.selectionMarker) === null || _api$selectionMarker === void 0 || _api$selectionMarker.actions.queueHideDecoration(function (hideDecoration) {
|
|
14
|
+
decoHide = hideDecoration;
|
|
15
|
+
});
|
|
16
|
+
} else {
|
|
17
|
+
var _decoHide;
|
|
18
|
+
(_decoHide = decoHide) === null || _decoHide === void 0 || _decoHide();
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
});
|
|
22
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
2
|
+
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
3
|
+
import _createClass from "@babel/runtime/helpers/createClass";
|
|
4
|
+
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
|
|
5
|
+
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
|
|
6
|
+
import _inherits from "@babel/runtime/helpers/inherits";
|
|
7
|
+
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
|
|
8
|
+
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
|
|
9
|
+
import { ViewPlugin, WidgetType, Decoration as CodeMirrorDecoration } from '@codemirror/view';
|
|
10
|
+
var PMWidget = /*#__PURE__*/function (_WidgetType) {
|
|
11
|
+
function PMWidget(toDOMElement) {
|
|
12
|
+
var _this;
|
|
13
|
+
_classCallCheck(this, PMWidget);
|
|
14
|
+
_this = _callSuper(this, PMWidget);
|
|
15
|
+
_this.toDOMElement = toDOMElement;
|
|
16
|
+
return _this;
|
|
17
|
+
}
|
|
18
|
+
_inherits(PMWidget, _WidgetType);
|
|
19
|
+
return _createClass(PMWidget, [{
|
|
20
|
+
key: "toDOM",
|
|
21
|
+
value: function toDOM() {
|
|
22
|
+
return this.toDOMElement;
|
|
23
|
+
}
|
|
24
|
+
}, {
|
|
25
|
+
key: "ignoreEvent",
|
|
26
|
+
value: function ignoreEvent() {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}]);
|
|
30
|
+
}(WidgetType); // This type is not exposed publically by ProseMirror but we need it to map to CodeMirror
|
|
31
|
+
// See: https://github.com/ProseMirror/prosemirror-view/blob/master/src/decoration.ts
|
|
32
|
+
// This type is not exposed publically by ProseMirror but we need it to map to CodeMirror
|
|
33
|
+
// See: https://github.com/ProseMirror/prosemirror-view/blob/master/src/decoration.ts
|
|
34
|
+
// This type is not exposed publically by ProseMirror but we need it to map to CodeMirror
|
|
35
|
+
// See: https://github.com/ProseMirror/prosemirror-view/blob/master/src/decoration.ts
|
|
36
|
+
function isExtendedDecoration(decoration) {
|
|
37
|
+
return decoration.inline !== undefined && decoration.widget !== undefined && decoration.type !== undefined;
|
|
38
|
+
}
|
|
39
|
+
var getHTMLElement = function getHTMLElement(toDOM, view, getPos) {
|
|
40
|
+
if (toDOM instanceof Function) {
|
|
41
|
+
var element = toDOM(view, getPos);
|
|
42
|
+
return element instanceof HTMLElement ? element : undefined;
|
|
43
|
+
} else if (toDOM instanceof HTMLElement) {
|
|
44
|
+
return toDOM;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var mapPMDecorationToCMDecoration = function mapPMDecorationToCMDecoration(decoration, view, getPos) {
|
|
48
|
+
if (!isExtendedDecoration(decoration)) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
if (decoration.inline) {
|
|
52
|
+
var markDecoration = CodeMirrorDecoration.mark({
|
|
53
|
+
attributes: decoration.type.attrs
|
|
54
|
+
});
|
|
55
|
+
return markDecoration.range(decoration.from, decoration.to);
|
|
56
|
+
} else if (decoration.widget) {
|
|
57
|
+
var _decoration$type;
|
|
58
|
+
var toDOM = getHTMLElement(decoration === null || decoration === void 0 || (_decoration$type = decoration.type) === null || _decoration$type === void 0 ? void 0 : _decoration$type.toDOM, view, getPos);
|
|
59
|
+
if (!toDOM) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
var widgetDecoration = CodeMirrorDecoration.widget({
|
|
63
|
+
widget: new PMWidget(toDOM),
|
|
64
|
+
side: decoration.type.side
|
|
65
|
+
});
|
|
66
|
+
return widgetDecoration.range(decoration.from, decoration.to);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
function isDefined(value) {
|
|
70
|
+
return value !== undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Creates CodeMirror versions of the decorations provided by ProseMirror.
|
|
75
|
+
*
|
|
76
|
+
* Inline ProseMirror decorations -> Mark CodeMirror decorations
|
|
77
|
+
* Widget ProseMirror decorations -> Widget CodeMirror decorations
|
|
78
|
+
*
|
|
79
|
+
* This way any decorations applied in ProseMirror land should automatically be supported
|
|
80
|
+
* by the CodeMirror editor
|
|
81
|
+
*
|
|
82
|
+
* @param updateDecorationsEffect Facet for the prosemirror decorations
|
|
83
|
+
* @returns CodeMirror extension
|
|
84
|
+
*/
|
|
85
|
+
export var prosemirrorDecorationPlugin = function prosemirrorDecorationPlugin(updateDecorationsEffect, editorView, getPos) {
|
|
86
|
+
return ViewPlugin.fromClass( /*#__PURE__*/function () {
|
|
87
|
+
function _class(view) {
|
|
88
|
+
_classCallCheck(this, _class);
|
|
89
|
+
this.decorations = this.updateDecorations(view);
|
|
90
|
+
}
|
|
91
|
+
return _createClass(_class, [{
|
|
92
|
+
key: "updateDecorations",
|
|
93
|
+
value: function updateDecorations(view) {
|
|
94
|
+
var _view$viewport = view.viewport,
|
|
95
|
+
from = _view$viewport.from,
|
|
96
|
+
to = _view$viewport.to;
|
|
97
|
+
var innnerDecorations = view.state.facet(updateDecorationsEffect);
|
|
98
|
+
var allDecorations = [];
|
|
99
|
+
innnerDecorations === null || innnerDecorations === void 0 || innnerDecorations.map(function (source) {
|
|
100
|
+
source === null || source === void 0 || source.forEachSet(function (set) {
|
|
101
|
+
var decorations = set.find(from, to)
|
|
102
|
+
// Do not render the code block line decorations
|
|
103
|
+
.filter(function (dec) {
|
|
104
|
+
return dec.spec.type !== 'decorationWidgetType';
|
|
105
|
+
});
|
|
106
|
+
allDecorations.push.apply(allDecorations, _toConsumableArray(decorations));
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
var cmDecorations = allDecorations.sort(function (a, b) {
|
|
110
|
+
return a.from < b.from ? -1 : 1;
|
|
111
|
+
}).map(function (decoration) {
|
|
112
|
+
return mapPMDecorationToCMDecoration(decoration, editorView, getPos);
|
|
113
|
+
}).filter(isDefined);
|
|
114
|
+
return CodeMirrorDecoration.set(cmDecorations);
|
|
115
|
+
}
|
|
116
|
+
}, {
|
|
117
|
+
key: "update",
|
|
118
|
+
value: function update(_update) {
|
|
119
|
+
this.decorations = this.updateDecorations(_update.view);
|
|
120
|
+
}
|
|
121
|
+
}]);
|
|
122
|
+
}(), {
|
|
123
|
+
decorations: function decorations(v) {
|
|
124
|
+
return v.decorations;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
};
|
package/dist/esm/ui/theme.js
CHANGED
|
@@ -5,6 +5,7 @@ export var cmTheme = CodeMirror.theme({
|
|
|
5
5
|
padding: '0',
|
|
6
6
|
marginTop: "var(--ds-space-100, 8px)",
|
|
7
7
|
marginBottom: "var(--ds-space-100, 8px)",
|
|
8
|
+
// eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
|
|
8
9
|
fontSize: '0.875rem',
|
|
9
10
|
// Custom syntax styling to match existing styling
|
|
10
11
|
// eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
|
|
@@ -31,7 +32,11 @@ export var cmTheme = CodeMirror.theme({
|
|
|
31
32
|
// eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
|
|
32
33
|
lineHeight: 'unset',
|
|
33
34
|
fontFamily: "var(--ds-font-family-code, ui-monospace, Menlo, \"Segoe UI Mono\", \"Ubuntu Mono\", monospace)",
|
|
34
|
-
borderRadius: "var(--ds-border-radius, 4px)"
|
|
35
|
+
borderRadius: "var(--ds-border-radius, 4px)",
|
|
36
|
+
backgroundImage: overflowShadow({
|
|
37
|
+
leftCoverWidth: "var(--ds-space-300, 24px)"
|
|
38
|
+
}),
|
|
39
|
+
backgroundAttachment: 'local, local, local, local, scroll, scroll, scroll, scroll'
|
|
35
40
|
},
|
|
36
41
|
'&.cm-focused .cm-cursor': {
|
|
37
42
|
borderLeftColor: "var(--ds-text, #172B4D)"
|
|
@@ -47,4 +52,17 @@ export var cmTheme = CodeMirror.theme({
|
|
|
47
52
|
paddingRight: "var(--ds-space-0, 0px)",
|
|
48
53
|
minWidth: 'unset'
|
|
49
54
|
}
|
|
50
|
-
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Copied directly from `packages/editor/editor-shared-styles/src/overflow-shadow/overflow-shadow.ts`
|
|
59
|
+
* `CodeMirror` does not support emotion styling so this has been re-created.
|
|
60
|
+
*/
|
|
61
|
+
function overflowShadow(_ref) {
|
|
62
|
+
var leftCoverWidth = _ref.leftCoverWidth,
|
|
63
|
+
rightCoverWidth = _ref.rightCoverWidth;
|
|
64
|
+
var width = "var(--ds-space-100, 8px)";
|
|
65
|
+
var leftCoverWidthResolved = leftCoverWidth || width;
|
|
66
|
+
var rightCoverWidthResolved = rightCoverWidth || width;
|
|
67
|
+
return "\n linear-gradient(\n to right,\n ".concat("var(--ds-background-neutral, #091E420F)", " ", leftCoverWidthResolved, ",\n transparent ").concat(leftCoverWidthResolved, "\n ),\n linear-gradient(\n to right,\n ", "var(--ds-surface-raised, #FFFFFF)", " ").concat(leftCoverWidthResolved, ",\n transparent ").concat(leftCoverWidthResolved, "\n ),\n linear-gradient(\n to left,\n ", "var(--ds-background-neutral, #091E420F)", " ").concat(rightCoverWidthResolved, ",\n transparent ").concat(rightCoverWidthResolved, "\n ),\n linear-gradient(\n to left,\n ", "var(--ds-surface-raised, #FFFFFF)", " ").concat(rightCoverWidthResolved, ",\n transparent ").concat(rightCoverWidthResolved, "\n ),\n linear-gradient(\n to left,\n ", "var(--ds-shadow-overflow-spread, #091e4229)", " 0,\n ", "var(--ds-UNSAFE-transparent, transparent)", " ").concat(width, "\n ),\n linear-gradient(\n to left,\n ", "var(--ds-shadow-overflow-perimeter, #091e421f)", " 0,\n ", "var(--ds-UNSAFE-transparent, transparent)", " ").concat(width, "\n ),\n linear-gradient(\n to right,\n ", "var(--ds-shadow-overflow-spread, #091e4229)", " 0,\n ", "var(--ds-UNSAFE-transparent, transparent)", " ").concat(width, "\n ),\n linear-gradient(\n to right,\n ", "var(--ds-shadow-overflow-perimeter, #091e421f)", " 0,\n ", "var(--ds-UNSAFE-transparent, transparent)", " ").concat(width, "\n )\n");
|
|
68
|
+
}
|