add code-viewer
This commit is contained in:
parent
1c50b71f14
commit
c622314eff
8 changed files with 140 additions and 48 deletions
|
@ -36,6 +36,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bluebird": "^3.5.1",
|
"bluebird": "^3.5.1",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
|
"codemirror": "^5.39.2",
|
||||||
"country-data": "^0.0.31",
|
"country-data": "^0.0.31",
|
||||||
"dom-scroll-into-view": "^1.2.1",
|
"dom-scroll-into-view": "^1.2.1",
|
||||||
"electron-dl": "^1.11.0",
|
"electron-dl": "^1.11.0",
|
||||||
|
|
38
src/renderer/component/viewers/codeViewer.jsx
Normal file
38
src/renderer/component/viewers/codeViewer.jsx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import CodeMirror from 'codemirror';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
value: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
class CodeViewer extends React.PureComponent<Props> {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.codeMirror = null;
|
||||||
|
this.textarea = React.createRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.codeMirror = CodeMirror.fromTextArea(this.textarea.current, {
|
||||||
|
mode: 'markdown',
|
||||||
|
readOnly: true,
|
||||||
|
dragDrop: false,
|
||||||
|
lineNumbers: true,
|
||||||
|
lineWrapping: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { value } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="document-viewer__content">
|
||||||
|
<textarea ref={this.textarea} disabled="true" value={value} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CodeViewer;
|
|
@ -3,6 +3,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import LoadingScreen from 'component/common/loading-screen';
|
import LoadingScreen from 'component/common/loading-screen';
|
||||||
|
import CodeViewer from 'component/viewers/codeViewer';
|
||||||
import MarkdownPreview from 'component/common/markdown-preview';
|
import MarkdownPreview from 'component/common/markdown-preview';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -34,7 +35,7 @@ class DocumentViewer extends React.PureComponent<Props> {
|
||||||
});
|
});
|
||||||
|
|
||||||
stream.on('end', () => {
|
stream.on('end', () => {
|
||||||
this.setState({ content: data });
|
this.setState({ content: data, loading: false });
|
||||||
});
|
});
|
||||||
|
|
||||||
stream.on('error', error => {
|
stream.on('error', error => {
|
||||||
|
@ -42,36 +43,34 @@ class DocumentViewer extends React.PureComponent<Props> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderDocument(data) {
|
renderDocument() {
|
||||||
|
let viewer = null;
|
||||||
const { source } = this.props;
|
const { source } = this.props;
|
||||||
const { fileType } = source;
|
const { content, error } = this.state;
|
||||||
|
const isReady = content && !error;
|
||||||
|
const markdownType = ['md', 'markdown'];
|
||||||
|
|
||||||
// Match supported documents
|
if (isReady && markdownType.includes(source.fileType)) {
|
||||||
const docs = {
|
// Render markdown
|
||||||
md: content => <MarkdownPreview content={content} promptLinks />,
|
viewer = <MarkdownPreview content={content} promptLinks />;
|
||||||
};
|
} else if (isReady) {
|
||||||
|
// Render plain text
|
||||||
if (docs[fileType]) {
|
viewer = <CodeViewer value={content} />;
|
||||||
// Use custom parser
|
|
||||||
return docs[fileType](data);
|
|
||||||
}
|
}
|
||||||
// Render plain text
|
|
||||||
this.setState({ loading: false });
|
return viewer;
|
||||||
return <textarea disabled="true" value={data} />;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { content, error, loading } = this.state;
|
const { error, loading } = this.state;
|
||||||
const loadingMessage = __('Rendering document.');
|
const loadingMessage = __('Rendering document.');
|
||||||
const errorMessage = __("Sorry looks like we can't load the document.");
|
const errorMessage = __("Sorry looks like we can't load the document.");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="document-viewer file-render__viewer">
|
<div className="file-render__viewer document-viewer">
|
||||||
{loading && !error && <LoadingScreen status={loadingMessage} spinner />}
|
{loading && !error && <LoadingScreen status={loadingMessage} spinner />}
|
||||||
{error && <LoadingScreen status={errorMessage} spinner={false} />}
|
{error && <LoadingScreen status={errorMessage} spinner={false} />}
|
||||||
{content && (
|
{this.renderDocument()}
|
||||||
<div className="document-viewer__content">{content && this.renderDocument(content)}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,25 +96,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-render {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin: auto;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
z-index: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.file-render__viewer {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: black;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
img {
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|
|
@ -9,17 +9,80 @@
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.file-render__viewer {
|
.file-render__viewer {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: black;
|
||||||
|
|
||||||
|
iframe,
|
||||||
|
webview {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: black;
|
}
|
||||||
|
}
|
||||||
iframe,
|
|
||||||
webview {
|
.document-viewer {
|
||||||
width: 100%;
|
overflow: auto;
|
||||||
height: 100%;
|
background-color: var(--card-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.document-viewer .markdown-preview {
|
||||||
|
padding: 32px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-viewer .CodeMirror {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100px;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
.CodeMirror-code {
|
||||||
|
font-size: calc(var(--font-size-subtext-multiple) * 1em);
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-linenumber {
|
||||||
|
color: var(--card-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-line {
|
||||||
|
padding-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-gutters {
|
||||||
|
border-right: 1px solid var(--color-divider);
|
||||||
|
background: var(--color-bg-alt);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-invalidchar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-viewer__content {
|
||||||
|
overflow: auto;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.markdown-preview {
|
||||||
|
padding: 32px 16px;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
/*
|
||||||
|
resize: none;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--card-text-color);
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
font-family: monospace;
|
||||||
|
*/
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Image */
|
||||||
|
img {
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Horizontal Rule */
|
/* Horizontal Rule */
|
||||||
hr {
|
hr {
|
||||||
border: 1px solid var(--color-divider);
|
border: 1px solid var(--color-divider);
|
||||||
|
|
|
@ -3,7 +3,8 @@ import mime from 'mime';
|
||||||
const formats = [
|
const formats = [
|
||||||
[/\.(mp4|m4v|webm|flv|f4v|ogv)$/i, 'video'],
|
[/\.(mp4|m4v|webm|flv|f4v|ogv)$/i, 'video'],
|
||||||
[/\.(mp3|m4a|aac|wav|flac|ogg|opus)$/i, 'audio'],
|
[/\.(mp3|m4a|aac|wav|flac|ogg|opus)$/i, 'audio'],
|
||||||
[/\.(html|htm|xml|pdf|odf|doc|docx|md|markdown|txt|epub|org)$/i, 'document'],
|
[/\.(json|csv|txt|log|md|markdown|rtf|xml|yml|yaml)$/i, 'document'],
|
||||||
|
[/\.(pdf|odf|doc|docx|epub|org)$/i, 'e-book'],
|
||||||
[/\.(stl|obj|fbx|gcode)$/i, '3D-file'],
|
[/\.(stl|obj|fbx|gcode)$/i, '3D-file'],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -1893,6 +1893,10 @@ codemirror@*:
|
||||||
version "5.39.0"
|
version "5.39.0"
|
||||||
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.39.0.tgz#4654f7d2f7e525e04a62e72d9482348ccb37dce5"
|
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.39.0.tgz#4654f7d2f7e525e04a62e72d9482348ccb37dce5"
|
||||||
|
|
||||||
|
codemirror@^5.39.2:
|
||||||
|
version "5.39.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.39.2.tgz#778aa13b55ebf280745c309cb1b148e3fc06f698"
|
||||||
|
|
||||||
collapse-white-space@^1.0.0, collapse-white-space@^1.0.2:
|
collapse-white-space@^1.0.0, collapse-white-space@^1.0.2:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.4.tgz#ce05cf49e54c3277ae573036a26851ba430a0091"
|
resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.4.tgz#ce05cf49e54c3277ae573036a26851ba430a0091"
|
||||||
|
|
Loading…
Reference in a new issue