@opensumi/ide-markdown 2.21.10 → 2.21.11-rc-1671795849.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/package.json +10 -9
- package/src/browser/contribution.ts +38 -0
- package/src/browser/editor.markdown.tsx +61 -0
- package/src/browser/index.ts +16 -0
- package/src/browser/mardown.style.ts +178 -0
- package/src/browser/markdown-widget.tsx +51 -0
- package/src/browser/markdown.service.ts +118 -0
- package/src/common/index.ts +13 -0
- package/src/index.ts +2 -0
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opensumi/ide-markdown",
|
|
3
|
-
"version": "2.21.
|
|
3
|
+
"version": "2.21.11-rc-1671795849.0",
|
|
4
4
|
"files": [
|
|
5
|
-
"lib"
|
|
5
|
+
"lib",
|
|
6
|
+
"src"
|
|
6
7
|
],
|
|
7
8
|
"license": "MIT",
|
|
8
9
|
"main": "lib/index.js",
|
|
@@ -16,15 +17,15 @@
|
|
|
16
17
|
"url": "git@github.com:opensumi/core.git"
|
|
17
18
|
},
|
|
18
19
|
"dependencies": {
|
|
19
|
-
"@opensumi/ide-core-common": "2.21.
|
|
20
|
-
"@opensumi/ide-core-node": "2.21.
|
|
20
|
+
"@opensumi/ide-core-common": "2.21.11-rc-1671795849.0",
|
|
21
|
+
"@opensumi/ide-core-node": "2.21.11-rc-1671795849.0"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
23
|
-
"@opensumi/ide-components": "2.21.
|
|
24
|
-
"@opensumi/ide-core-browser": "2.21.
|
|
24
|
+
"@opensumi/ide-components": "2.21.11-rc-1671795849.0",
|
|
25
|
+
"@opensumi/ide-core-browser": "2.21.11-rc-1671795849.0",
|
|
25
26
|
"@opensumi/ide-dev-tool": "^1.3.1",
|
|
26
|
-
"@opensumi/ide-editor": "2.21.
|
|
27
|
-
"@opensumi/ide-webview": "2.21.
|
|
27
|
+
"@opensumi/ide-editor": "2.21.11-rc-1671795849.0",
|
|
28
|
+
"@opensumi/ide-webview": "2.21.11-rc-1671795849.0"
|
|
28
29
|
},
|
|
29
|
-
"gitHead": "
|
|
30
|
+
"gitHead": "ce22ef324609bcb9b915be8d205ec696c4579df8"
|
|
30
31
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Autowired } from '@opensumi/di';
|
|
2
|
+
import { PreferenceService } from '@opensumi/ide-core-browser';
|
|
3
|
+
import { Domain, localize, Schemes } from '@opensumi/ide-core-common';
|
|
4
|
+
import { BrowserEditorContribution, EditorComponentRegistry } from '@opensumi/ide-editor/lib/browser';
|
|
5
|
+
|
|
6
|
+
import { IMarkdownService } from '../common';
|
|
7
|
+
|
|
8
|
+
import { MarkdownEditorComponent } from './editor.markdown';
|
|
9
|
+
|
|
10
|
+
export const MARKDOWN_EDITOR_COMPONENT_ID = 'MARKDOWN_EDITOR_COMPONENT_ID';
|
|
11
|
+
|
|
12
|
+
@Domain(BrowserEditorContribution)
|
|
13
|
+
export class EmbeddedMarkdownEditorContribution implements BrowserEditorContribution {
|
|
14
|
+
@Autowired(IMarkdownService)
|
|
15
|
+
markdownService: IMarkdownService;
|
|
16
|
+
|
|
17
|
+
@Autowired(PreferenceService)
|
|
18
|
+
preferenceService: PreferenceService;
|
|
19
|
+
|
|
20
|
+
registerEditorComponent(componentRegistry: EditorComponentRegistry) {
|
|
21
|
+
componentRegistry.registerEditorComponent({
|
|
22
|
+
uid: MARKDOWN_EDITOR_COMPONENT_ID,
|
|
23
|
+
component: MarkdownEditorComponent,
|
|
24
|
+
scheme: Schemes.file,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
componentRegistry.registerEditorComponentResolver(Schemes.file, (resource, results) => {
|
|
28
|
+
if (resource.uri.path.ext === '.md') {
|
|
29
|
+
results.push({
|
|
30
|
+
type: 'component',
|
|
31
|
+
componentId: MARKDOWN_EDITOR_COMPONENT_ID,
|
|
32
|
+
title: localize('editorOpenType.preview'),
|
|
33
|
+
weight: this.preferenceService.get<boolean>('application.preferMarkdownPreview') ? 10 : -1,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { useInjectable, Disposable, CancellationTokenSource, Emitter } from '@opensumi/ide-core-browser';
|
|
4
|
+
import { ReactEditorComponent, IEditorDocumentModelService } from '@opensumi/ide-editor/lib/browser';
|
|
5
|
+
|
|
6
|
+
import { IMarkdownService } from '../common';
|
|
7
|
+
|
|
8
|
+
export const MarkdownEditorComponent: ReactEditorComponent<any> = ({ resource }) => {
|
|
9
|
+
let container: HTMLElement | null = null;
|
|
10
|
+
const markdownService: IMarkdownService = useInjectable(IMarkdownService);
|
|
11
|
+
const documentService: IEditorDocumentModelService = useInjectable(IEditorDocumentModelService);
|
|
12
|
+
|
|
13
|
+
React.useEffect(() => {
|
|
14
|
+
if (container) {
|
|
15
|
+
const disposer = new Disposable();
|
|
16
|
+
const cancellation = new CancellationTokenSource();
|
|
17
|
+
disposer.addDispose({
|
|
18
|
+
dispose: () => {
|
|
19
|
+
cancellation.cancel();
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
documentService.createModelReference(resource.uri, 'markdown-preview').then((documentRef) => {
|
|
23
|
+
if (cancellation.token.isCancellationRequested) {
|
|
24
|
+
if (documentRef) {
|
|
25
|
+
documentRef.dispose();
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const onUpdate = new Emitter<string>();
|
|
30
|
+
disposer.addDispose(onUpdate);
|
|
31
|
+
disposer.addDispose(
|
|
32
|
+
documentRef.instance.getMonacoModel().onDidChangeContent((e) => {
|
|
33
|
+
onUpdate.fire(documentRef.instance.getText());
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
if (container) {
|
|
37
|
+
// container可能已不存在
|
|
38
|
+
markdownService
|
|
39
|
+
.previewMarkdownInContainer(documentRef.instance.getText(), container!, cancellation.token, onUpdate.event)
|
|
40
|
+
.then((r) => {
|
|
41
|
+
disposer.addDispose(r);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
disposer.addDispose(documentRef);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return () => {
|
|
48
|
+
disposer.dispose();
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div
|
|
55
|
+
ref={(el) => {
|
|
56
|
+
container = el;
|
|
57
|
+
}}
|
|
58
|
+
style={{ height: '100%' }}
|
|
59
|
+
></div>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Provider, Injectable } from '@opensumi/di';
|
|
2
|
+
import { BrowserModule } from '@opensumi/ide-core-browser';
|
|
3
|
+
|
|
4
|
+
import { IMarkdownService } from '../common';
|
|
5
|
+
|
|
6
|
+
import { MarkdownServiceImpl } from './markdown.service';
|
|
7
|
+
export { Markdown } from './markdown-widget';
|
|
8
|
+
@Injectable()
|
|
9
|
+
export class MarkdownModule extends BrowserModule {
|
|
10
|
+
providers: Provider[] = [
|
|
11
|
+
{
|
|
12
|
+
token: IMarkdownService,
|
|
13
|
+
useClass: MarkdownServiceImpl,
|
|
14
|
+
},
|
|
15
|
+
];
|
|
16
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
export const markdownCss = `
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
4
|
+
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
6
|
+
|
|
7
|
+
body {
|
|
8
|
+
padding: 10px 20px;
|
|
9
|
+
line-height: 22px;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
img {
|
|
13
|
+
max-width: 100%;
|
|
14
|
+
max-height: 100%;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
a {
|
|
18
|
+
text-decoration: none;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
a:hover {
|
|
22
|
+
text-decoration: underline;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
a:focus,
|
|
26
|
+
input:focus,
|
|
27
|
+
select:focus,
|
|
28
|
+
textarea:focus {
|
|
29
|
+
outline: 1px solid -webkit-focus-ring-color;
|
|
30
|
+
outline-offset: -1px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
hr {
|
|
34
|
+
border: 0;
|
|
35
|
+
height: 2px;
|
|
36
|
+
border-bottom: 2px solid;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
h1 {
|
|
40
|
+
padding-bottom: 0.3em;
|
|
41
|
+
line-height: 1.2;
|
|
42
|
+
border-bottom-width: 1px;
|
|
43
|
+
border-bottom-style: solid;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
h1, h2, h3 {
|
|
47
|
+
font-weight: normal;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
table {
|
|
51
|
+
border-collapse: collapse;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
table > thead > tr > th {
|
|
55
|
+
text-align: left;
|
|
56
|
+
border-bottom: 1px solid;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
table > thead > tr > th,
|
|
60
|
+
table > thead > tr > td,
|
|
61
|
+
table > tbody > tr > th,
|
|
62
|
+
table > tbody > tr > td {
|
|
63
|
+
padding: 5px 10px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
table > tbody > tr + tr > td {
|
|
67
|
+
border-top: 1px solid;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
blockquote {
|
|
71
|
+
margin: 0 7px 0 5px;
|
|
72
|
+
padding: 0 16px 0 10px;
|
|
73
|
+
border-left-width: 5px;
|
|
74
|
+
border-left-style: solid;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
code {
|
|
78
|
+
font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback";
|
|
79
|
+
font-size: 14px;
|
|
80
|
+
line-height: 19px;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.mac code {
|
|
84
|
+
font-size: 12px;
|
|
85
|
+
line-height: 18px;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
code > div {
|
|
89
|
+
padding: 16px;
|
|
90
|
+
border-radius: 3px;
|
|
91
|
+
overflow: auto;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
#scroll-to-top {
|
|
95
|
+
position: fixed;
|
|
96
|
+
width: 40px;
|
|
97
|
+
height: 40px;
|
|
98
|
+
right: 25px;
|
|
99
|
+
bottom: 77px;
|
|
100
|
+
background-color:#444444;
|
|
101
|
+
border-radius: 50%;
|
|
102
|
+
cursor: pointer;
|
|
103
|
+
box-shadow: 1px 1px 1px rgba(0,0,0,.25);
|
|
104
|
+
outline: none;
|
|
105
|
+
display: flex;
|
|
106
|
+
justify-content: center;
|
|
107
|
+
align-items: center;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
#scroll-to-top:hover {
|
|
111
|
+
background-color:#007acc;
|
|
112
|
+
box-shadow: 2px 2px 2px rgba(0,0,0,.25);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
body.vscode-light #scroll-to-top {
|
|
116
|
+
background-color: #949494;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
body.vscode-high-contrast #scroll-to-top:hover {
|
|
120
|
+
background-color: #007acc;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
body.vscode-high-contrast #scroll-to-top {
|
|
124
|
+
background-color: black;
|
|
125
|
+
border: 2px solid #6fc3df;
|
|
126
|
+
box-shadow: none;
|
|
127
|
+
}
|
|
128
|
+
body.vscode-high-contrast #scroll-to-top:hover {
|
|
129
|
+
background-color: #007acc;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
#scroll-to-top span.icon::before {
|
|
133
|
+
content: "";
|
|
134
|
+
display: block;
|
|
135
|
+
/* Chevron up icon */
|
|
136
|
+
background:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxNiAxNiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTYgMTY7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDojRkZGRkZGO30KCS5zdDF7ZmlsbDpub25lO30KPC9zdHlsZT4KPHRpdGxlPnVwY2hldnJvbjwvdGl0bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04LDUuMWwtNy4zLDcuM0wwLDExLjZsOC04bDgsOGwtMC43LDAuN0w4LDUuMXoiLz4KPHJlY3QgY2xhc3M9InN0MSIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2Ii8+Cjwvc3ZnPgo=');
|
|
137
|
+
width: 16px;
|
|
138
|
+
height: 16px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Theming */
|
|
142
|
+
.vscode-light code > div {
|
|
143
|
+
background-color: rgba(220, 220, 220, 0.4);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.vscode-dark code > div {
|
|
147
|
+
background-color: rgba(10, 10, 10, 0.4);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.vscode-high-contrast code > div {
|
|
151
|
+
background-color: rgb(0, 0, 0);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.vscode-high-contrast h1 {
|
|
155
|
+
border-color: rgb(0, 0, 0);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.vscode-light table > thead > tr > th {
|
|
159
|
+
border-color: rgba(0, 0, 0, 0.69);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.vscode-dark table > thead > tr > th {
|
|
163
|
+
border-color: rgba(255, 255, 255, 0.69);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.vscode-light h1,
|
|
167
|
+
.vscode-light hr,
|
|
168
|
+
.vscode-light table > tbody > tr + tr > td {
|
|
169
|
+
border-color: rgba(0, 0, 0, 0.18);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.vscode-dark h1,
|
|
173
|
+
.vscode-dark hr,
|
|
174
|
+
.vscode-dark table > tbody > tr + tr > td {
|
|
175
|
+
border-color: rgba(255, 255, 255, 0.18);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
`;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { useInjectable, Disposable, CancellationTokenSource, URI } from '@opensumi/ide-core-browser';
|
|
4
|
+
|
|
5
|
+
import { IMarkdownService } from '../common';
|
|
6
|
+
|
|
7
|
+
export const Markdown = ({
|
|
8
|
+
content,
|
|
9
|
+
onLoaded,
|
|
10
|
+
onLinkClick,
|
|
11
|
+
}: {
|
|
12
|
+
content: string;
|
|
13
|
+
onLoaded?: () => void;
|
|
14
|
+
onLinkClick?: (uri: URI) => void;
|
|
15
|
+
}) => {
|
|
16
|
+
let container: HTMLElement | null = null;
|
|
17
|
+
const markdownService: IMarkdownService = useInjectable(IMarkdownService);
|
|
18
|
+
|
|
19
|
+
React.useEffect(() => {
|
|
20
|
+
if (container) {
|
|
21
|
+
const disposer = new Disposable();
|
|
22
|
+
const cancellation = new CancellationTokenSource();
|
|
23
|
+
disposer.addDispose({
|
|
24
|
+
dispose: () => {
|
|
25
|
+
cancellation.cancel();
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
markdownService
|
|
29
|
+
.previewMarkdownInContainer(content, container!, cancellation.token, undefined, onLinkClick)
|
|
30
|
+
.then((r) => {
|
|
31
|
+
disposer.addDispose(r);
|
|
32
|
+
if (onLoaded) {
|
|
33
|
+
onLoaded();
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return () => {
|
|
38
|
+
disposer.dispose();
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}, [content]);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div
|
|
45
|
+
ref={(el) => {
|
|
46
|
+
container = el;
|
|
47
|
+
}}
|
|
48
|
+
style={{ height: '100%' }}
|
|
49
|
+
></div>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Injectable, Autowired } from '@opensumi/di';
|
|
2
|
+
import { parseMarkdown } from '@opensumi/ide-components/lib/utils';
|
|
3
|
+
import { IDisposable, Disposable, CancellationToken, Event, URI, IOpenerService } from '@opensumi/ide-core-browser';
|
|
4
|
+
import { HttpOpener } from '@opensumi/ide-core-browser/lib/opener/http-opener';
|
|
5
|
+
import { IWebviewService } from '@opensumi/ide-webview';
|
|
6
|
+
|
|
7
|
+
import { IMarkdownService } from '../common';
|
|
8
|
+
|
|
9
|
+
import { markdownCss } from './mardown.style';
|
|
10
|
+
|
|
11
|
+
@Injectable()
|
|
12
|
+
export class MarkdownServiceImpl implements IMarkdownService {
|
|
13
|
+
@Autowired(IWebviewService)
|
|
14
|
+
webviewService: IWebviewService;
|
|
15
|
+
|
|
16
|
+
@Autowired(IOpenerService)
|
|
17
|
+
private readonly openerService: IOpenerService;
|
|
18
|
+
|
|
19
|
+
// 检测下支持的协议,以防打开内部协议
|
|
20
|
+
private isSupportedLink(uri: URI) {
|
|
21
|
+
return HttpOpener.standardSupportedLinkSchemes.has(uri.scheme);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async previewMarkdownInContainer(
|
|
25
|
+
content: string,
|
|
26
|
+
container: HTMLElement,
|
|
27
|
+
cancellationToken: CancellationToken,
|
|
28
|
+
onUpdate?: Event<string>,
|
|
29
|
+
onLinkClick?: (uri: URI) => void,
|
|
30
|
+
): Promise<IDisposable> {
|
|
31
|
+
const body = await this.getBody(content);
|
|
32
|
+
if (cancellationToken.isCancellationRequested) {
|
|
33
|
+
return new Disposable();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const disposer = new Disposable();
|
|
37
|
+
const webviewElement = this.webviewService.createWebview({
|
|
38
|
+
enableFindWidget: true,
|
|
39
|
+
localResourceRoots: [],
|
|
40
|
+
});
|
|
41
|
+
webviewElement.appendTo(container);
|
|
42
|
+
webviewElement.setContent(body);
|
|
43
|
+
|
|
44
|
+
disposer.addDispose(
|
|
45
|
+
webviewElement.onDidClickLink((link) => {
|
|
46
|
+
if (!link) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// Whitelist supported schemes for links
|
|
50
|
+
if (this.isSupportedLink(link)) {
|
|
51
|
+
this.openerService.open(link);
|
|
52
|
+
}
|
|
53
|
+
if (onLinkClick) {
|
|
54
|
+
onLinkClick(link);
|
|
55
|
+
}
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
disposer.addDispose(webviewElement);
|
|
59
|
+
if (onUpdate) {
|
|
60
|
+
disposer.addDispose(
|
|
61
|
+
onUpdate(async (content) => {
|
|
62
|
+
const body = await this.getBody(content);
|
|
63
|
+
if (!cancellationToken.isCancellationRequested) {
|
|
64
|
+
webviewElement.setContent(body);
|
|
65
|
+
}
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return disposer;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async getBody(content: string): Promise<string> {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
parseMarkdown(content, undefined, (err, result) => {
|
|
76
|
+
if (err) {
|
|
77
|
+
reject(err);
|
|
78
|
+
}
|
|
79
|
+
resolve(removeEmbeddedSVGs(renderBody(result)));
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function renderBody(body: string): string {
|
|
86
|
+
return `<!DOCTYPE html>
|
|
87
|
+
<html>
|
|
88
|
+
<head>
|
|
89
|
+
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
|
|
90
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src https: data:; media-src https:; script-src 'none'; style-src 'unsafe-inline'; child-src 'none'; frame-src 'none';">
|
|
91
|
+
<style>
|
|
92
|
+
${markdownCss}
|
|
93
|
+
</style>
|
|
94
|
+
</head>
|
|
95
|
+
<body>
|
|
96
|
+
<a id="scroll-to-top" role="button" aria-label="scroll to top" href="#"><span class="icon"></span></a>
|
|
97
|
+
${body}
|
|
98
|
+
</body>
|
|
99
|
+
</html>`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function removeEmbeddedSVGs(documentContent: string): string {
|
|
103
|
+
const newDocument = new DOMParser().parseFromString(documentContent, 'text/html');
|
|
104
|
+
|
|
105
|
+
// remove all inline svgs
|
|
106
|
+
const allSVGs = newDocument.documentElement.querySelectorAll('svg');
|
|
107
|
+
if (allSVGs) {
|
|
108
|
+
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
109
|
+
for (let i = 0; i < allSVGs.length; i++) {
|
|
110
|
+
const svg = allSVGs[i];
|
|
111
|
+
if (svg.parentNode) {
|
|
112
|
+
svg.parentNode.removeChild(allSVGs[i]);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return newDocument.documentElement.outerHTML;
|
|
118
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CancellationToken, IDisposable, Event, URI } from '@opensumi/ide-core-common';
|
|
2
|
+
|
|
3
|
+
export interface IMarkdownService {
|
|
4
|
+
previewMarkdownInContainer(
|
|
5
|
+
content: string,
|
|
6
|
+
container: HTMLElement,
|
|
7
|
+
cancellationToken: CancellationToken,
|
|
8
|
+
onUpdate?: Event<string>,
|
|
9
|
+
onLinkClick?: (uri: URI) => void,
|
|
10
|
+
): Promise<IDisposable>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const IMarkdownService = Symbol('IMarkdownService');
|
package/src/index.ts
ADDED