@seafile/sdoc-editor 0.5.65 → 0.5.67-beta
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/dist/assets/css/plugin-editor.css +3 -0
- package/dist/basic-sdk/extension/commons/index.js +2 -1
- package/dist/basic-sdk/extension/commons/select/_option.js +32 -0
- package/dist/basic-sdk/extension/commons/select/field-setting.js +101 -0
- package/dist/basic-sdk/extension/commons/select/index.js +135 -0
- package/dist/basic-sdk/extension/commons/select/menu/index.js +4 -0
- package/dist/basic-sdk/extension/commons/select/menu/item.js +32 -0
- package/dist/basic-sdk/extension/commons/select/menu/menu.js +27 -0
- package/dist/basic-sdk/extension/commons/select/menu/style.css +43 -0
- package/dist/basic-sdk/extension/commons/select/style.css +149 -0
- package/dist/basic-sdk/extension/constants/element-type.js +4 -0
- package/dist/basic-sdk/extension/constants/index.js +2 -2
- package/dist/basic-sdk/extension/constants/menus-config.js +6 -1
- package/dist/basic-sdk/extension/plugins/column/constants/cell-types.js +29 -0
- package/dist/basic-sdk/extension/plugins/column/constants/column.js +28 -0
- package/dist/basic-sdk/extension/plugins/column/helpers.js +62 -0
- package/dist/basic-sdk/extension/plugins/column/index.js +12 -0
- package/dist/basic-sdk/extension/plugins/column/menu/index.js +28 -0
- package/dist/basic-sdk/extension/plugins/column/model.js +14 -0
- package/dist/basic-sdk/extension/plugins/column/plugin.js +24 -0
- package/dist/basic-sdk/extension/plugins/column/render-elem.js +111 -0
- package/dist/basic-sdk/extension/plugins/font/helpers.js +1 -0
- package/dist/basic-sdk/extension/plugins/group/index.js +7 -0
- package/dist/basic-sdk/extension/plugins/group/render-elem.js +17 -0
- package/dist/basic-sdk/extension/plugins/index.js +4 -2
- package/dist/basic-sdk/extension/plugins/text-align/helpers.js +1 -0
- package/dist/basic-sdk/extension/render/custom-element.js +12 -1
- package/dist/basic-sdk/extension/toolbar/header-toolbar/index.js +7 -2
- package/dist/basic-sdk/extension/toolbar/insert-element-toolbar/index.js +19 -4
- package/dist/basic-sdk/extension/toolbar/side-toolbar/index.js +16 -1
- package/dist/basic-sdk/extension/toolbar/side-toolbar/side-menu.js +10 -3
- package/dist/basic-sdk/hooks/use-force-update.js +9 -0
- package/dist/basic-sdk/utils/rebase.js +93 -14
- package/dist/components/doc-operations/revision-operations/index.js +1 -0
- package/dist/index.js +3 -1
- package/dist/pages/document-plugin-editor.js +46 -0
- package/dist/pages/document-plugin-viewer.js +55 -0
- package/package.json +1 -1
- package/public/index.html +21 -22
- package/public/media/dtable-font.css +1566 -0
- package/public/media/dtable-fonts/dtable-font.eot +0 -0
- package/public/media/dtable-fonts/dtable-font.svg +793 -0
- package/public/media/dtable-fonts/dtable-font.ttf +0 -0
- package/public/media/dtable-fonts/dtable-font.woff +0 -0
- package/public/media/dtable-fonts/dtable-font.woff2 +0 -0
- package/public/media/sdoc-editor-font/iconfont.eot +0 -0
- package/public/media/sdoc-editor-font/iconfont.svg +6 -0
- package/public/media/sdoc-editor-font/iconfont.ttf +0 -0
- package/public/media/sdoc-editor-font/iconfont.woff +0 -0
- package/public/media/sdoc-editor-font/iconfont.woff2 +0 -0
- package/public/media/sdoc-editor-font.css +22 -8
|
@@ -25,6 +25,77 @@ export const hasConflict = content => {
|
|
|
25
25
|
}
|
|
26
26
|
return flag;
|
|
27
27
|
};
|
|
28
|
+
const expandGroup = content => {
|
|
29
|
+
if ([ELEMENT_TYPE.UNORDERED_LIST, ELEMENT_TYPE.ORDERED_LIST].includes(content.type)) {
|
|
30
|
+
return [_objectSpread(_objectSpread({}, content), {}, {
|
|
31
|
+
children: content.children.map(item => {
|
|
32
|
+
if (item.type === ELEMENT_TYPE.GROUP) {
|
|
33
|
+
return expandGroup(item);
|
|
34
|
+
}
|
|
35
|
+
return item;
|
|
36
|
+
}).flat(1)
|
|
37
|
+
})];
|
|
38
|
+
}
|
|
39
|
+
return content.type === ELEMENT_TYPE.GROUP ? content.children : [content];
|
|
40
|
+
};
|
|
41
|
+
const mergeChanges = changes => {
|
|
42
|
+
if (!Array.isArray(changes) || changes.length === 0) return changes;
|
|
43
|
+
let value = [];
|
|
44
|
+
let i = 0;
|
|
45
|
+
// while (i < changes.length) {
|
|
46
|
+
// const change = changes[i];
|
|
47
|
+
// const lastChange = value[value.length - 1];
|
|
48
|
+
// if (change && lastChange && change[REBASE_MARK_KEY.MODIFY_TYPE] === lastChange[REBASE_MARK_KEY.MODIFY_TYPE] && [MODIFY_TYPE.ADD, MODIFY_TYPE.DELETE].includes(change[REBASE_MARK_KEY.MODIFY_TYPE])) {
|
|
49
|
+
// if (lastChange.type === ELEMENT_TYPE.GROUP) {
|
|
50
|
+
// change[REBASE_MARK_KEY.MODIFY_TYPE] && delete change[REBASE_MARK_KEY.MODIFY_TYPE];
|
|
51
|
+
// lastChange.children.push(change);
|
|
52
|
+
// } else {
|
|
53
|
+
// value = value.slice(0, value.length - 1);
|
|
54
|
+
// const modifyType = change[REBASE_MARK_KEY.MODIFY_TYPE];
|
|
55
|
+
// lastChange[REBASE_MARK_KEY.MODIFY_TYPE] && delete lastChange[REBASE_MARK_KEY.MODIFY_TYPE];
|
|
56
|
+
// change[REBASE_MARK_KEY.MODIFY_TYPE] && delete change[REBASE_MARK_KEY.MODIFY_TYPE];
|
|
57
|
+
// value.push({
|
|
58
|
+
// id: lastChange.id + '_group',
|
|
59
|
+
// type: ELEMENT_TYPE.GROUP,
|
|
60
|
+
// [REBASE_MARK_KEY.MODIFY_TYPE]: modifyType,
|
|
61
|
+
// children: [lastChange, change],
|
|
62
|
+
// });
|
|
63
|
+
// }
|
|
64
|
+
// i++;
|
|
65
|
+
// } else {
|
|
66
|
+
// value.push(change);
|
|
67
|
+
// i++;
|
|
68
|
+
// }
|
|
69
|
+
// }
|
|
70
|
+
|
|
71
|
+
// also add
|
|
72
|
+
|
|
73
|
+
while (i < changes.length) {
|
|
74
|
+
const change = changes[i];
|
|
75
|
+
const lastChange = value[value.length - 1];
|
|
76
|
+
if (change && lastChange && change[REBASE_MARK_KEY.MODIFY_TYPE] === MODIFY_TYPE.ADD && lastChange[REBASE_MARK_KEY.MODIFY_TYPE] === MODIFY_TYPE.DELETE) {
|
|
77
|
+
value = value.slice(0, value.length - 1);
|
|
78
|
+
lastChange[REBASE_MARK_KEY.MODIFY_TYPE] && delete lastChange[REBASE_MARK_KEY.MODIFY_TYPE];
|
|
79
|
+
change[REBASE_MARK_KEY.MODIFY_TYPE] && delete change[REBASE_MARK_KEY.MODIFY_TYPE];
|
|
80
|
+
value.push({
|
|
81
|
+
id: change.id.endsWith('_group') ? change.id : change.id + '_group',
|
|
82
|
+
type: ELEMENT_TYPE.GROUP,
|
|
83
|
+
[REBASE_MARK_KEY.MODIFY_TYPE]: MODIFY_TYPE.MODIFY,
|
|
84
|
+
children: expandGroup(change),
|
|
85
|
+
[REBASE_MARK_KEY.OLD_ELEMENT]: {
|
|
86
|
+
id: lastChange.id.endsWith('_group') ? lastChange.id : lastChange.id + '_group',
|
|
87
|
+
type: ELEMENT_TYPE.GROUP,
|
|
88
|
+
children: expandGroup(lastChange)
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
i++;
|
|
92
|
+
} else {
|
|
93
|
+
value.push(change);
|
|
94
|
+
i++;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return value;
|
|
98
|
+
};
|
|
28
99
|
const getChanges = (masterContent, revisionContent) => {
|
|
29
100
|
const {
|
|
30
101
|
map: masterContentMap,
|
|
@@ -43,15 +114,21 @@ const getChanges = (masterContent, revisionContent) => {
|
|
|
43
114
|
removed
|
|
44
115
|
} = idDiff;
|
|
45
116
|
if (added) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
117
|
+
// merge consecutive identical operations
|
|
118
|
+
content.push({
|
|
119
|
+
id: value[0].id + '_group',
|
|
120
|
+
type: ELEMENT_TYPE.GROUP,
|
|
121
|
+
[REBASE_MARK_KEY.MODIFY_TYPE]: MODIFY_TYPE.ADD,
|
|
122
|
+
children: value.map(item => currentContentMap[item])
|
|
123
|
+
});
|
|
50
124
|
} else if (removed) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
125
|
+
// merge consecutive identical operations
|
|
126
|
+
content.push({
|
|
127
|
+
id: value[0].id + '_group',
|
|
128
|
+
type: ELEMENT_TYPE.GROUP,
|
|
129
|
+
[REBASE_MARK_KEY.MODIFY_TYPE]: MODIFY_TYPE.DELETE,
|
|
130
|
+
children: value.map(item => currentContentMap[item])
|
|
131
|
+
});
|
|
55
132
|
} else {
|
|
56
133
|
value.forEach(elementId => {
|
|
57
134
|
if (ObjectUtils.isSameObject(masterContentMap[elementId], currentContentMap[elementId])) {
|
|
@@ -76,7 +153,7 @@ const getChanges = (masterContent, revisionContent) => {
|
|
|
76
153
|
});
|
|
77
154
|
}
|
|
78
155
|
});
|
|
79
|
-
return content;
|
|
156
|
+
return mergeChanges(content);
|
|
80
157
|
};
|
|
81
158
|
const getMergeElement = (diffElement, baseElement) => {
|
|
82
159
|
const modifyType = diffElement[REBASE_MARK_KEY.MODIFY_TYPE];
|
|
@@ -86,7 +163,7 @@ const getMergeElement = (diffElement, baseElement) => {
|
|
|
86
163
|
// revision does not have this element, master has this element
|
|
87
164
|
if (modifyType === MODIFY_TYPE.DELETE) {
|
|
88
165
|
// base content does not have this element, indicating that it is newly added by master and needs to be retained, and will not be counted as a conflict.
|
|
89
|
-
if (!baseElement) return
|
|
166
|
+
if (!baseElement) return expandGroup(newElement);
|
|
90
167
|
|
|
91
168
|
// base content has this element, master modified it, indicating that revision deleted it, and the user manually selected the conflict
|
|
92
169
|
if (!ObjectUtils.isSameObject(baseElement, diffElement, [REBASE_MARK_KEY.MODIFY_TYPE])) {
|
|
@@ -101,7 +178,7 @@ const getMergeElement = (diffElement, baseElement) => {
|
|
|
101
178
|
// revision has this element, master does not have this element
|
|
102
179
|
if (modifyType === MODIFY_TYPE.ADD) {
|
|
103
180
|
// base content does not have this element, indicating that it is newly added by revision and needs to be retained, and will not be counted as a conflict.
|
|
104
|
-
if (!baseElement) return
|
|
181
|
+
if (!baseElement) return expandGroup(newElement);
|
|
105
182
|
|
|
106
183
|
// master deleted it, revision modified it, and the user manually selected the conflict
|
|
107
184
|
if (!ObjectUtils.isSameObject(baseElement, diffElement, [REBASE_MARK_KEY.MODIFY_TYPE])) {
|
|
@@ -161,10 +238,10 @@ const getMergeElement = (diffElement, baseElement) => {
|
|
|
161
238
|
[REBASE_MARK_KEY.ORIGIN]: REBASE_ORIGIN.MY
|
|
162
239
|
})];
|
|
163
240
|
}
|
|
164
|
-
if (ObjectUtils.isSameObject(masterElement, baseElement)) return
|
|
241
|
+
if (ObjectUtils.isSameObject(masterElement, baseElement)) return expandGroup(newElement);
|
|
165
242
|
if (ObjectUtils.isSameObject(newElement, baseElement)) return [masterElement];
|
|
166
243
|
if (ObjectUtils.isSameObject(masterElement, newElement, ['type'])) {
|
|
167
|
-
if (ObjectUtils.isSameObject(masterElement, baseElement, ['type'])) return
|
|
244
|
+
if (ObjectUtils.isSameObject(masterElement, baseElement, ['type'])) return expandGroup(newElement);
|
|
168
245
|
if (ObjectUtils.isSameObject(newElement, baseElement, ['type'])) return [masterElement];
|
|
169
246
|
}
|
|
170
247
|
|
|
@@ -175,7 +252,7 @@ const getMergeElement = (diffElement, baseElement) => {
|
|
|
175
252
|
})];
|
|
176
253
|
}
|
|
177
254
|
newElement[REBASE_MARK_KEY.OLD_ELEMENT] && delete newElement[REBASE_MARK_KEY.OLD_ELEMENT];
|
|
178
|
-
return
|
|
255
|
+
return expandGroup(newElement);
|
|
179
256
|
};
|
|
180
257
|
const getMergeContent = (baseContent, diffChanges) => {
|
|
181
258
|
const {
|
|
@@ -234,7 +311,9 @@ export const getRebase = (masterContent, baseContent, revisionContent) => {
|
|
|
234
311
|
};
|
|
235
312
|
}
|
|
236
313
|
const diffChanges = getChanges(masterContent, revisionContent);
|
|
314
|
+
console.log('diffChanges: ', diffChanges);
|
|
237
315
|
const content = getMergeContent(baseContent, diffChanges);
|
|
316
|
+
console.log('content: ', content);
|
|
238
317
|
return {
|
|
239
318
|
canMerge: canMerge(content, revisionContent.children),
|
|
240
319
|
isNeedReplaceMaster: true,
|
package/dist/index.js
CHANGED
|
@@ -6,4 +6,6 @@ import DiffViewer from './pages/diff-viewer';
|
|
|
6
6
|
import PublishedRevisionViewer from './pages/published-revision-viewer';
|
|
7
7
|
import WikiViewer from './pages/wiki-viewer';
|
|
8
8
|
import SdocWikiViewer from './pages/sdoc-wiki-viewer';
|
|
9
|
-
|
|
9
|
+
import DocumentPluginEditor from './pages/document-plugin-editor';
|
|
10
|
+
import DocumentPluginViewer from './pages/document-plugin-viewer';
|
|
11
|
+
export { SDocViewer, SimpleEditor, SimpleViewer, EventBus, EXTERNAL_EVENT, DiffViewer, PublishedRevisionViewer, WikiViewer, SdocWikiViewer, DocumentPluginEditor, DocumentPluginViewer };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { withTranslation } from 'react-i18next';
|
|
3
|
+
import context from '../context';
|
|
4
|
+
import { SDocEditor } from '../basic-sdk';
|
|
5
|
+
import { PAGE_EDIT_AREA_WIDTH } from '../basic-sdk/constants';
|
|
6
|
+
import { createDefaultEditor } from '../basic-sdk/extension';
|
|
7
|
+
import withNodeId from '../basic-sdk/node-id';
|
|
8
|
+
import { withSocketIO } from '../basic-sdk/socket';
|
|
9
|
+
import { ColumnPlugin } from '../basic-sdk/extension/plugins';
|
|
10
|
+
import ErrorBoundary from './error-boundary';
|
|
11
|
+
import '../assets/css/simple-editor.css';
|
|
12
|
+
import '../assets/css/plugin-editor.css';
|
|
13
|
+
const DocumentPluginEditor = _ref => {
|
|
14
|
+
let {
|
|
15
|
+
document,
|
|
16
|
+
showOutline,
|
|
17
|
+
scrollRef,
|
|
18
|
+
columns
|
|
19
|
+
} = _ref;
|
|
20
|
+
context.initApi();
|
|
21
|
+
const validEditor = useMemo(() => {
|
|
22
|
+
const withColumnPlugin = ColumnPlugin.editorPlugin;
|
|
23
|
+
const defaultEditor = withColumnPlugin(createDefaultEditor());
|
|
24
|
+
const editorConfig = context.getEditorConfig();
|
|
25
|
+
const newEditor = withNodeId(withSocketIO(defaultEditor, {
|
|
26
|
+
document,
|
|
27
|
+
config: editorConfig
|
|
28
|
+
}));
|
|
29
|
+
const {
|
|
30
|
+
cursors
|
|
31
|
+
} = document;
|
|
32
|
+
newEditor.columns = columns || [];
|
|
33
|
+
newEditor.cursors = cursors || {};
|
|
34
|
+
newEditor.width = PAGE_EDIT_AREA_WIDTH; // default width
|
|
35
|
+
return newEditor;
|
|
36
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
37
|
+
}, []);
|
|
38
|
+
return /*#__PURE__*/React.createElement(ErrorBoundary, null, /*#__PURE__*/React.createElement(SDocEditor, {
|
|
39
|
+
editor: validEditor,
|
|
40
|
+
scrollRef: scrollRef,
|
|
41
|
+
document: document,
|
|
42
|
+
showComment: false,
|
|
43
|
+
showOutline: showOutline
|
|
44
|
+
}));
|
|
45
|
+
};
|
|
46
|
+
export default withTranslation('sdoc-editor')(DocumentPluginEditor);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React, { useEffect, useMemo } from 'react';
|
|
2
|
+
import { withTranslation } from 'react-i18next';
|
|
3
|
+
import context from '../context';
|
|
4
|
+
import { SDocViewer } from '../basic-sdk';
|
|
5
|
+
import { PAGE_EDIT_AREA_WIDTH } from '../basic-sdk/constants';
|
|
6
|
+
import { createDefaultEditor } from '../basic-sdk/extension';
|
|
7
|
+
import withNodeId from '../basic-sdk/node-id';
|
|
8
|
+
import { withSocketIO } from '../basic-sdk/socket';
|
|
9
|
+
import { ColumnPlugin } from '../basic-sdk/extension/plugins';
|
|
10
|
+
import ErrorBoundary from './error-boundary';
|
|
11
|
+
import useForceUpdate from '../basic-sdk/hooks/use-force-update';
|
|
12
|
+
import '../assets/css/simple-editor.css';
|
|
13
|
+
import '../assets/css/plugin-editor.css';
|
|
14
|
+
const DocumentPluginViewer = _ref => {
|
|
15
|
+
let {
|
|
16
|
+
scrollRef,
|
|
17
|
+
document,
|
|
18
|
+
showOutline,
|
|
19
|
+
columns,
|
|
20
|
+
getColumnCellValue
|
|
21
|
+
} = _ref;
|
|
22
|
+
context.initApi();
|
|
23
|
+
const forceUpdate = useForceUpdate();
|
|
24
|
+
const validEditor = useMemo(() => {
|
|
25
|
+
const withColumnPlugin = ColumnPlugin.editorPlugin;
|
|
26
|
+
const defaultEditor = withColumnPlugin(createDefaultEditor());
|
|
27
|
+
const editorConfig = context.getEditorConfig();
|
|
28
|
+
const newEditor = withNodeId(withSocketIO(defaultEditor, {
|
|
29
|
+
document,
|
|
30
|
+
config: editorConfig
|
|
31
|
+
}));
|
|
32
|
+
const {
|
|
33
|
+
cursors
|
|
34
|
+
} = document;
|
|
35
|
+
newEditor.columns = columns || [];
|
|
36
|
+
newEditor.getColumnCellValue = getColumnCellValue ? getColumnCellValue : null;
|
|
37
|
+
newEditor.cursors = cursors || {};
|
|
38
|
+
newEditor.width = PAGE_EDIT_AREA_WIDTH; // default width
|
|
39
|
+
return newEditor;
|
|
40
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
41
|
+
}, []);
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
validEditor.getColumnCellValue = getColumnCellValue ? getColumnCellValue : null;
|
|
44
|
+
forceUpdate();
|
|
45
|
+
}, [forceUpdate, getColumnCellValue, validEditor.getColumnCellValue]);
|
|
46
|
+
return /*#__PURE__*/React.createElement(ErrorBoundary, null, /*#__PURE__*/React.createElement(SDocViewer, {
|
|
47
|
+
editor: validEditor,
|
|
48
|
+
scrollRef: scrollRef,
|
|
49
|
+
document: document,
|
|
50
|
+
showComment: false,
|
|
51
|
+
showToolbar: true,
|
|
52
|
+
showOutline: showOutline
|
|
53
|
+
}));
|
|
54
|
+
};
|
|
55
|
+
export default withTranslation('sdoc-editor')(DocumentPluginViewer);
|
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<!--
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
6
|
+
<meta name="theme-color" content="#000000">
|
|
7
|
+
<!--
|
|
9
8
|
manifest.json provides metadata used when your web app is added to the
|
|
10
9
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
|
11
10
|
-->
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
|
12
|
+
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
|
13
|
+
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
|
14
|
+
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%/media/sdoc-editor-font.css">
|
|
15
|
+
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%/media/dtable-font.css">
|
|
16
|
+
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%/media/seafile-ui.css">
|
|
17
|
+
<!--
|
|
19
18
|
Notice the use of %PUBLIC_URL% in the tags above.
|
|
20
19
|
It will be replaced with the URL of the `public` folder during the build.
|
|
21
20
|
Only files inside the `public` folder can be referenced from the HTML.
|
|
@@ -24,14 +23,14 @@
|
|
|
24
23
|
work correctly both with client-side routing and a non-root public URL.
|
|
25
24
|
Learn how to configure a non-root public URL by running `npm run build`.
|
|
26
25
|
-->
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
<title>React App</title>
|
|
27
|
+
</head>
|
|
28
|
+
<body>
|
|
29
|
+
<noscript>
|
|
30
|
+
You need to enable JavaScript to run this app.
|
|
31
|
+
</noscript>
|
|
32
|
+
<div id="root"></div>
|
|
33
|
+
<!--
|
|
35
34
|
This HTML file is a template.
|
|
36
35
|
If you open it directly in the browser, you will see an empty page.
|
|
37
36
|
|
|
@@ -41,5 +40,5 @@
|
|
|
41
40
|
To begin the development, run `npm start` or `yarn start`.
|
|
42
41
|
To create a production bundle, use `npm run build` or `yarn build`.
|
|
43
42
|
-->
|
|
44
|
-
|
|
43
|
+
</body>
|
|
45
44
|
</html>
|