diff options
| author | Jacky Zhao <[email protected]> | 2021-03-06 17:57:24 -0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-03-06 17:57:24 -0800 |
| commit | 5dd02b5c2acd7a4c408ce9ffa1d95e208d20bbc8 (patch) | |
| tree | 5a09bea364331dd0f41510153924065b815b702e | |
| parent | fix(typo): public api docs endpoint (diff) | |
| parent | fix password modal (diff) | |
| download | ctrl-v-5dd02b5c2acd7a4c408ce9ffa1d95e208d20bbc8.tar.xz ctrl-v-5dd02b5c2acd7a4c408ce9ffa1d95e208d20bbc8.zip | |
Merge pull request #70 from jackyzha0/visual-overhaul
34 files changed, 691 insertions, 588 deletions
diff --git a/frontend/package.json b/frontend/package.json index 376660a..4fc1fe7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,7 +10,6 @@ "d3-scale": "^3.2.1", "elliptic": "^6.5.3", "file-saver": "^2.0.2", - "indent-textarea": "^2.0.2", "rc-slider": "^9.2.4", "react": "^16.13.1", "react-component-export-image": "^0.1.4", @@ -23,7 +22,9 @@ "react-render-html": "^0.6.0", "react-router-dom": "^5.2.0", "react-scripts": "3.4.1", + "react-simple-code-editor": "^0.11.0", "react-syntax-highlighter": "^12.2.1", + "react-syntax-highlighter-virtualized-renderer": "^1.1.0", "serialize-javascript": "^4.0.0", "styled-components": "^5.1.0", "use-clipboard-copy": "^0.1.2" diff --git a/frontend/public/index.html b/frontend/public/index.html index 0a92619..8ca24a5 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -9,8 +9,8 @@ content="a modern, open-source pastebin with latex and markdown rendering support" /> <link rel="icon" href="icon.png"> - <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jackyzha0/[email protected]/src/lite.css"> - <link href="https://fonts.googleapis.com/css2?family=Lora:wght@400;600&family=Roboto+Mono&display=swap" rel="stylesheet"> + <link rel="preconnect" href="https://fonts.gstatic.com"> + <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;700&display=swap" rel="stylesheet"> <title>ctrl-v | a modern, open-source pastebin </title> <!-- Global site tag (gtag.js) - Google Analytics --> <script async src="https://www.googletagmanager.com/gtag/js?id=G-DE1TYY2F24"></script> diff --git a/frontend/src/App.js b/frontend/src/App.js new file mode 100644 index 0000000..a8da469 --- /dev/null +++ b/frontend/src/App.js @@ -0,0 +1,58 @@ +import React from 'react'; +import NewPaste from './components/NewPaste' +import ViewPaste from './components/ViewPaste' +import styled from 'styled-components' +import { + BrowserRouter as Router, + Switch, + Route, + useParams +} from "react-router-dom"; +import Raw from './components/renderers/Raw' +import ThemeProvider from './theme/ThemeProvider' +import GlobalStyle from './theme/GlobalStyle' +import {Watermark} from "./components/Watermark"; + +const Main = styled.div` + margin-top: 10vh; + padding: 0 20vw 30px 20vw; +` + +const GetPasteWithParam = () => { + let { hash } = useParams(); + return <ViewPaste hash = {hash} />; +} + +const GetRawWithParam = () => { + let { hash } = useParams(); + return <Raw hash={hash} />; +} + +const App = () => { + return ( + <ThemeProvider> + <GlobalStyle /> + <Router> + <Switch> + <Route path="/raw/:hash"><GetRawWithParam /></Route> + <Route> + <Watermark/> + <Main id="appElement"> + <Switch> + <Route path="/:hash"> + <GetPasteWithParam /> + </Route> + <Route path="/"> + <NewPaste /> + </Route> + </Switch> + </Main> + </Route> + </Switch> + </Router> + </ThemeProvider> + ); +} + + +export default App; diff --git a/frontend/src/components/App.js b/frontend/src/components/App.js deleted file mode 100644 index 543cd46..0000000 --- a/frontend/src/components/App.js +++ /dev/null @@ -1,80 +0,0 @@ -import React from 'react'; -import NewPaste from './NewPaste' -import ViewPaste from './ViewPaste' -import Footer from './Footer' -import styled from 'styled-components' -import { - BrowserRouter as Router, - Switch, - Route, - Link, - useParams -} from "react-router-dom"; -import Raw from './renderers/Raw' - -const SpacedTitle = styled.div` - margin-top: 10vh -` - -const Desc = () => { - return ( - <h3>a modern, <a href="https://github.com/jackyzha0/ctrl-v" target="_blank" rel="noopener noreferrer">open-source</a> pastebin with latex and markdown rendering support</h3> - ); -} - -const GetPasteWithParam = () => { - let { hash } = useParams(); - - return ( - <ViewPaste hash = {hash} /> - ); -} - -const GetRawWithParam = () => { - let { hash } = useParams(); - - return ( - <Raw hash={hash} /> - ); -} - -const App = () => { - return ( - <Router> - <Switch> - <Route path="/raw/:hash" - children={<GetRawWithParam />} - /> - <Route> - <div className="lt-content-column"> - <SpacedTitle> - <nav> - <h1 className="mainLogo"> - <span role="img" aria-label="clipboard">📋 </span> - <Link to="/">ctrl-v</Link> - </h1> - <Desc /> - </nav> - </SpacedTitle> - - <main id="appElement"> - <Switch> - <Route path="/:hash" - children={<GetPasteWithParam />} - /> - <Route path="/"> - <NewPaste /> - </Route> - </Switch> - </main> - - <Footer /> - </div> - </Route> - </Switch> - </Router> - ); -} - - -export default App; diff --git a/frontend/src/components/Common/Button.js b/frontend/src/components/Common/Button.js new file mode 100644 index 0000000..59e148b --- /dev/null +++ b/frontend/src/components/Common/Button.js @@ -0,0 +1,32 @@ +import styled, {css} from 'styled-components' +import {Border, ButtonLike, DropShadow, Rounded} from "./mixins"; + +const Base = css` + ${DropShadow} + ${Rounded} + ${ButtonLike} + margin-right: 2em; + height: calc(16px + 1.6em); + cursor: pointer; +` + +const Primary = css` + ${Base}; + border: none; + color: ${p => p.theme.colors.background}; + background-color: ${p => p.theme.colors.text}; +` +const Secondary = css` + ${Base}; + ${Border}; + color: ${p => p.theme.colors.text}; + background-color: ${p => p.theme.colors.background}; +` + +export const Button = styled.button` + ${p => p.secondary ? css`${Secondary}` : css`${Primary}` } +` + +export const SubmitButton = styled.input` + ${Primary} +`
\ No newline at end of file diff --git a/frontend/src/components/Common/Input.js b/frontend/src/components/Common/Input.js new file mode 100644 index 0000000..e000cfb --- /dev/null +++ b/frontend/src/components/Common/Input.js @@ -0,0 +1,9 @@ +import styled from 'styled-components' +import {Border, DropShadow, InputLike, Rounded} from "./mixins"; + +export const Input = styled.input` + ${Border} + ${Rounded} + ${DropShadow} + ${InputLike} +`
\ No newline at end of file diff --git a/frontend/src/components/Common/mixins.js b/frontend/src/components/Common/mixins.js new file mode 100644 index 0000000..ff2759f --- /dev/null +++ b/frontend/src/components/Common/mixins.js @@ -0,0 +1,52 @@ +import {css} from 'styled-components'; + +export const DropShadow = css` + box-shadow: 0 14px 28px rgba(27, 33, 48,0.06), 0 10px 10px rgba(27, 33, 48,0.02); +` + +export const Hover = css` + opacity: 0.5; + transition: all 0.5s cubic-bezier(.25,.8,.25,1); + + & ~ pre { + transition: all 0.5s cubic-bezier(.25,.8,.25,1); + opacity: 0.5; + } + + &:focus, &:hover, &:focus span, &:focus ~ pre { + opacity: 1; + } +` + +export const Rounded = css` + border-radius: 3px; +` + +export const Border = css` + border: 1px solid ${p => p.theme.colors.border}; +` + +export const InputLike = css` + ${Hover} + font-family: 'JetBrains Mono', monospace; + width: 100%; + font-size: 0.8em; + padding: 0.6em; + outline: none; + margin: 1.7em 0; +` + +export const CodeLike = css` + font-family: JetBrains Mono !important; + font-size: 13px !important; + line-height: 1.6em !important; + white-space: pre-wrap; +` + +export const ButtonLike = css` + font-family: 'JetBrains Mono', serif; + font-weight: 700; + padding: 0.6em 1.5em; + margin: 2em 0; + outline: 0; +`
\ No newline at end of file diff --git a/frontend/src/components/Footer.js b/frontend/src/components/Footer.js index c7878e5..4afc7ba 100644 --- a/frontend/src/components/Footer.js +++ b/frontend/src/components/Footer.js @@ -2,7 +2,12 @@ import React from 'react'; import styled from 'styled-components' const SpacedFooter = styled.div` - margin: 2em 0; + & > p { + font-size: 0.8em; + } + & a { + color: ${p => p.theme.colors.text}; + } ` const Link = (props) => { @@ -14,7 +19,7 @@ const Link = (props) => { const Footer = () => { return ( <SpacedFooter> - © 2020 — <Link link="https://jzhao.xyz/" name="jacky" />, <Link link="https://ryanmehri.tech/" name="ryan" /> + <p>(c) 2020 // <Link link="https://jzhao.xyz/" name="jacky" />, <Link link="https://ryanmehri.tech/" name="ryan" /></p> </SpacedFooter> ); } diff --git a/frontend/src/components/Inputs.js b/frontend/src/components/Inputs.js deleted file mode 100644 index 3adad92..0000000 --- a/frontend/src/components/Inputs.js +++ /dev/null @@ -1,200 +0,0 @@ -import React, {useEffect, useRef} from 'react'; -import CharLimit from './decorators/CharLimit' -import styled from 'styled-components' -import FloatingLabel from './decorators/FloatingLabel' -import Dropdown from 'react-dropdown'; -import { LANGS, THEMES } from './renderers/Code'; -import * as indentation from 'indent-textarea'; - -const RelPositioning = styled.div` - position: relative; - height: calc(100% - 4em); -` - -const FlexChild = styled.div` - display: block; - margin-left: 2em; -` - -const TitleInput = (props) => { - return ( - <RelPositioning> - <FloatingLabel - label="title" - id={props.id} - value={props.value} /> - <input - name="title" - readOnly={props.readOnly} - className="lt-shadow" - placeholder="Title" - id={props.id} - type="text" - autoFocus - autoComplete="off" - onChange={props.onChange} - value={props.value} /> - <CharLimit - content={props.value} - maxLength={props.maxLength} /> - </RelPositioning> - ); -} - -const PasteInput = ({content, ...props}) => { - const textInput = useRef(null); - - useEffect(() => { - indentation.watch(textInput.current); - }, [textInput]) - - return ( - <RelPositioning> - <FloatingLabel - label="content" - id={props.id} - value={content} /> - <textarea - name="content" - readOnly={props.readOnly} - placeholder="Paste your text here" - value={content} - id={props.id} - ref={textInput} - required - onChange={props.onChange} - className="lt-shadow" /> - <CharLimit - content={content} - maxLength={props.maxLength} /> - </RelPositioning> - ); -} - -const PassInput = (props) => { - return ( - <FlexChild> - <RelPositioning> - <FloatingLabel - label="password" - id={props.id} - value={props.value} /> - <input - name="pass" - className="lt-shadow" - placeholder="password" - type="password" - autoComplete="off" - onChange={props.onChange} - value={props.value} - id={props.id} /> - </RelPositioning> - </FlexChild> - ); -} - -const GenericDropdown = (props) => { - function _onSelect(option) { - props.onChange({ - target: { - name: props.label, - value: option.label - } - }); - } - - return ( - <FlexChild> - <Dropdown - options={props.options} - onChange={_onSelect} - value={props.value} - placeholder={props.placeholder} - id={props.id} /> - <FloatingLabel - label={props.label} - id={props.id} - value={props.value} /> - </FlexChild> - ); -} - -const ExpiryInput = (props) => { - const options = [ - '5 years', - '1 year', - '1 month', - '1 week', - '1 day', - '1 hour', - '10 min', - ]; - - return ( - <GenericDropdown - {...props} - options={options} - placeholder='1 week' - label='expiry' - /> - ); -} - -const LangInput = (props) => { - const options = Object.entries(LANGS).map((key, _) => { - return { - 'value': key[1], - 'label': key[0] - } - }) - - return ( - <GenericDropdown - {...props} - options={options} - placeholder={LANGS.raw} - label='language' - /> - ); -} - -const ThemeInput = (props) => { - const options = Object.entries(THEMES).map((key, _) => { - return { - 'value': key[1], - 'label': key[0] - } - }) - - return ( - <GenericDropdown - {...props} - options={options} - placeholder={'atom'} - label='theme' - /> - ); -} - -const PasteURLInput = ({id, fullURL}) => { - return ( - <FlexChild> - <RelPositioning> - <FloatingLabel - label="url" - id={id} - value={id} /> - <input - name="paste_url" - className="lt-shadow" - type="text" - readOnly - autoFocus - id={id} - value={fullURL} /> - </RelPositioning> - </FlexChild> - ); -} - -export { TitleInput, PasteInput, PassInput, ExpiryInput, PasteURLInput, LangInput, ThemeInput }
\ No newline at end of file diff --git a/frontend/src/components/Inputs/Code.js b/frontend/src/components/Inputs/Code.js new file mode 100644 index 0000000..adb1536 --- /dev/null +++ b/frontend/src/components/Inputs/Code.js @@ -0,0 +1,54 @@ +import React from "react"; +import styled from 'styled-components' +import CharLimit from "../decorators/CharLimit"; +import Editor from 'react-simple-code-editor'; +import {Highlighter} from "../renderers/Code"; +import {CodeLike, Hover} from "../Common/mixins"; + +const Wrapper = styled.div` + position: relative; +` +const EditorWrapper = styled(Editor)` + overflow: visible !important; + + & > * { + padding: 0 !important; + width: 100%; + } + + & pre, & code, & > textarea { + ${CodeLike} + min-height: 40vh; + } + + & pre { + z-index: -1 !important; + } + + & > textarea { + ${Hover} + padding: 0.6em !important; + outline: none !important; + } +` + +export const Code = ({content, id, readOnly, setContentCallback, ...props}) => { + return ( + <Wrapper> + <EditorWrapper + name="content" + readOnly={readOnly} + placeholder="Paste your text here" + value={content} + id={id} + required + highlight={code => <Highlighter theme="atom">{code}</Highlighter> } + onValueChange={code => setContentCallback(code)} + padding={15} + /> + <CharLimit + content={content} + maxLength={props.maxLength} /> + </Wrapper> + ); +}
\ No newline at end of file diff --git a/frontend/src/components/Inputs/Dropdown.js b/frontend/src/components/Inputs/Dropdown.js new file mode 100644 index 0000000..9fde6ed --- /dev/null +++ b/frontend/src/components/Inputs/Dropdown.js @@ -0,0 +1,120 @@ +import Dropdown from "react-dropdown"; +import React from "react"; +import styled from 'styled-components'; +import {LANGS, THEMES} from "../renderers/Code"; +import {Labelled} from "../decorators/Labelled"; +import {Border, DropShadow, InputLike, Rounded} from "../Common/mixins"; + +const StyledDropdown = styled(Dropdown)` + ${Border} + ${Rounded} + ${DropShadow} + ${InputLike} + cursor: pointer; + + & .Dropdown-root { + cursor: pointer; + + &:hover, &.is-open { + opacity: 1; + } + } + + & .Dropdown-placeholder { + width: 5.5em; + } + + & .Dropdown-menu { + border-top: 1px solid ${p => p.theme.colors.text}; + margin-top: 0.5em; + bottom: auto; + } + + & .Dropdown-option { + margin-top: 0.5em; + transition: all 0.5s cubic-bezier(.25,.8,.25,1); + + &:hover { + font-weight: 700; + opacity: 0.4; + } + } +` + +const GenericDropdown = (props) => { + function _onSelect(option) { + props.onChange({ + target: { + name: props.label, + value: option.label + } + }); + } + + return ( + <Labelled + label={props.label} + id={props.id} + value={props.value}> + <StyledDropdown + options={props.options} + onChange={_onSelect} + value={props.value} + placeholder={props.placeholder} + id={props.id} /> + </Labelled> + ); +} + +export const Expiry = (props) => { + const options = [ + '5 years', + '1 year', + '1 month', + '1 week', + '1 day', + '1 hour', + '10 min', + ]; + + return ( + <GenericDropdown + {...props} + options={options} + placeholder='1 week' + label='expiry' + /> + ); +} + +export const Language = (props) => { + const options = Object.entries(LANGS).map((key, _) => ({ + 'value': key[1], + 'label': key[0] + })) + + return ( + <GenericDropdown + {...props} + options={options} + placeholder='detect' + label='language' + /> + ); +} + +export const Theme = (props) => { + const options = Object.entries(THEMES).map((key) => ({ + 'value': key[1], + 'label': key[0] + })) + + return ( + <GenericDropdown + {...props} + options={options} + placeholder='atom' + label='theme' + /> + ); +}
\ No newline at end of file diff --git a/frontend/src/components/Inputs/Password.js b/frontend/src/components/Inputs/Password.js new file mode 100644 index 0000000..099aae4 --- /dev/null +++ b/frontend/src/components/Inputs/Password.js @@ -0,0 +1,14 @@ +import React from "react"; +import {Labelled} from "../decorators/Labelled"; +import {Input} from "../Common/Input"; + +export const Password = (props) => <Labelled label="password"> + <Input + name="pass" + placeholder={props.placeholder ?? "add password"} + type="password" + autoComplete="off" + onChange={props.onChange} + value={props.value} + id={props.id} /> +</Labelled>
\ No newline at end of file diff --git a/frontend/src/components/Inputs/Text.js b/frontend/src/components/Inputs/Text.js new file mode 100644 index 0000000..600e653 --- /dev/null +++ b/frontend/src/components/Inputs/Text.js @@ -0,0 +1,25 @@ +import CharLimit from "../decorators/CharLimit"; +import React from "react"; +import {Labelled} from "../decorators/Labelled"; +import {Input} from "../Common/Input"; + +export const Text = React.forwardRef(({label, id, readOnly, onChange, value, maxLength, autoFocus}, ref) => { + return ( + <Labelled label={label} value={value}> + <Input + ref={ref} + name={label} + readOnly={readOnly} + placeholder="Title" + id={id} + type="text" + autoFocus={autoFocus} + autoComplete="off" + onChange={onChange} + value={value} /> + <CharLimit + content={value} + maxLength={maxLength} /> + </Labelled> + ); +})
\ No newline at end of file diff --git a/frontend/src/components/Inputs/index.js b/frontend/src/components/Inputs/index.js new file mode 100644 index 0000000..b16deb3 --- /dev/null +++ b/frontend/src/components/Inputs/index.js @@ -0,0 +1,6 @@ +import {Code} from './Code'; +import {Expiry, Language, Theme} from "./Dropdown"; +import {Password} from "./Password"; +import {Text} from "./Text"; + +export {Code, Expiry, Language, Theme, Password, Text};
\ No newline at end of file diff --git a/frontend/src/components/NewPaste.js b/frontend/src/components/NewPaste.js index ff945a0..b322351 100644 --- a/frontend/src/components/NewPaste.js +++ b/frontend/src/components/NewPaste.js @@ -1,20 +1,14 @@ import React, { useEffect, useState, useRef } from 'react'; -import { TitleInput, PasteInput } from './Inputs' +import { Text, Code } from './Inputs' import OptionsContainer from './Options' import Error from './Err' import { PostNewPaste } from '../helpers/httpHelper' import PasteModal from './modals/PasteModal' -import { LANGS } from './renderers/Code' import styled from 'styled-components' import CodeRenderer from './renderers/Code' import Latex from './renderers/Latex' import Markdown from './renderers/Markdown' - -const Button = styled.button` - margin-right: 0 !important; - margin-left: 2em !important; - height: calc(16px + 1.6em + 2px); -` +import {Button, SubmitButton} from "./Common/Button"; const Flex = styled.div` display: flex; @@ -39,7 +33,7 @@ const NewPaste = () => { const [title, setTitle] = useState(''); const [content, setContent] = useState(''); const [pass, setPass] = useState(''); - const [language, setLanguage] = useState(LANGS.raw); + const [language, setLanguage] = useState('detect'); const [expiry, setExpiry] = useState(''); const [hash, setHash] = useState(''); const [isPreview, setIsPreview] = useState(false); @@ -78,11 +72,10 @@ const NewPaste = () => { } function renderPreview() { - const pasteInput = <PasteInput - onChange={(e) => { setContent(e.target.value) }} + const pasteInput = <Code + setContentCallback={setContent} content={content} - maxLength="100000" - id="pasteInput" /> + maxLength="100000" /> if (isPreview) { var preview @@ -129,9 +122,11 @@ const NewPaste = () => { return ( <form onSubmit={handleSubmit}> <PasteModal hash={hash} /> - <TitleInput + <Text + label="title" onChange={(e) => {setTitle(e.target.value)}} value={title} + autoFocus maxLength="100" id="titleInput" /> {renderPreview()} @@ -142,13 +137,15 @@ const NewPaste = () => { onPassChange={(e) => { setPass(e.target.value) }} onLangChange={(e) => { setLanguage(e.target.value) }} onExpiryChange={(e) => { setExpiry(e.target.value) }} /> - <input className="lt-button lt-shadow lt-hover" type="submit" value="new paste" /> - <Button - className="lt-shadow lt-hover" + <div> + <SubmitButton type="submit" value="new paste" /> + {language !== 'detect' && <Button + secondary type="button" onClick={() => setIsPreview(!isPreview)}> preview - </Button> + </Button>} + </div> <br /> <Error ref={ErrorLabel} /> </form> diff --git a/frontend/src/components/Options.js b/frontend/src/components/Options.js index 09f92f3..97d3127 100644 --- a/frontend/src/components/Options.js +++ b/frontend/src/components/Options.js @@ -1,31 +1,34 @@ import React from 'react'; import styled from 'styled-components' -import { PassInput, ExpiryInput, LangInput } from './Inputs' +import { Password, Expiry, Language } from './Inputs' const Flex = styled.div` float: right; display: flex; flex-direction: row; transform: translateY(0.2em); + + & > *:not(:first-child) { + margin-left: 2em; + } @media (max-width: 850px) { float: none !important; - transform: translateX(-2em); } ` const OptionsContainer = ({pass, lang, expiry, onPassChange, onLangChange, onExpiryChange}) => { return ( <Flex> - <PassInput + <Password value={pass} onChange={onPassChange} id="passwordInput" /> - <LangInput + <Language value={lang} onChange={onLangChange} id="langInput" /> - <ExpiryInput + <Expiry value={expiry} onChange={onExpiryChange} id="expiryInput" /> diff --git a/frontend/src/components/PasteInfo.js b/frontend/src/components/PasteInfo.js index bab7e23..6ab5b19 100644 --- a/frontend/src/components/PasteInfo.js +++ b/frontend/src/components/PasteInfo.js @@ -1,8 +1,8 @@ import React from 'react'; import styled from 'styled-components' import { useHistory } from 'react-router-dom'; -import { ThemeInput } from './Inputs' -import { exportComponentAsPNG } from "react-component-export-image"; +import { Theme } from './Inputs' +import {Button} from "./Common/Button"; const Bold = styled.span` font-weight: 700 @@ -13,10 +13,7 @@ const StyledDiv = styled.div` margin: 2em 0; ` -const Button = styled.button` - margin-right: 0 !important; - margin-left: 2em !important; - height: calc(16px + 1.6em); +const ShiftedButton = styled(Button)` margin-top: 1.6em !important; ` @@ -28,7 +25,6 @@ const Flex = styled.div` float: right; display: flex; flex-direction: row; - transform: translateY(0.2em); ` const PasteInfo = (props) => { @@ -42,12 +38,12 @@ const PasteInfo = (props) => { const buttonTxt = props.isRenderMode ? 'text' : 'render' if (props.lang === 'latex' || props.lang === 'markdown') { return ( - <Button - className="lt-shadow lt-hover" + <ShiftedButton + secondary type="button" onClick={props.toggleRenderCallback}> {buttonTxt} - </Button> + </ShiftedButton> ); } } @@ -55,20 +51,14 @@ const PasteInfo = (props) => { return ( <div> <Flex> - <Button - className="lt-shadow lt-hover" + <ShiftedButton + secondary type="button" onClick={redirRaw}> view raw - </Button> - <Button - className="lt-shadow lt-hover" - type="button" - onClick={() => exportComponentAsPNG(props.compref, `paste-${props.hash}.png`)}> - save png - </Button> + </ShiftedButton> {renderable()} - <ThemeInput + <Theme value={props.theme} onChange={props.onChange} id="themeInput" /> diff --git a/frontend/src/components/ViewPaste.js b/frontend/src/components/ViewPaste.js index 5f9a962..9dd281c 100644 --- a/frontend/src/components/ViewPaste.js +++ b/frontend/src/components/ViewPaste.js @@ -1,13 +1,12 @@ import React, { useEffect, useState, useRef } from 'react'; import Error from './Err'; -import { TitleInput } from './Inputs'; +import { Text } from './Inputs'; import CodeRenderer from './renderers/Code' import PasteInfo from './PasteInfo'; import PasswordModal from './modals/PasswordModal' import { FetchPaste, FetchPasswordPaste } from '../helpers/httpHelper' import { LANGS } from './renderers/Code' import RenderDispatch from './renderers/RenderDispatch' -import MetaTags from 'react-meta-tags'; function fmtDateStr(dateString) { const d = new Date(dateString) @@ -24,14 +23,13 @@ const ViewPaste = (props) => { const [expiry, setExpiry] = useState(''); const [theme, setTheme] = useState('atom'); const [isRenderMode, setIsRenderMode] = useState(false); - const [language, setLanguage] = useState(LANGS.raw); + const [language, setLanguage] = useState(LANGS.detect); useEffect(() => { setIsRenderMode(language === 'latex' || language === 'markdown') }, [language]) const ErrorLabelRef = useRef(null); - const ComponentRef = useRef(null); function validatePass(pass, onErrorCallBack) { FetchPasswordPaste(props.hash, pass) @@ -98,39 +96,26 @@ const ViewPaste = (props) => { }, [props.hash]) function getDisplay() { - if (isRenderMode) { - return ( - <RenderDispatch - language={language} - content={content} - ref={ComponentRef} - /> - ) - } else { - return ( - <CodeRenderer - content={content} - lang={language} - theme={theme} - ref={ComponentRef} - id="pasteInput" /> - ) - } + return isRenderMode ? <RenderDispatch + language={language} + content={content} + /> : <CodeRenderer + content={content} + lang={language} + theme={theme} + id="pasteInput" /> } return ( <div> - <MetaTags> - <meta name="description" content={`${language}, expires ${expiry}. hosted on ctrl-v`} /> - <meta property="og:title" content={title} /> - </MetaTags> <PasswordModal hasPass={hasPass} validPass={validPass} value={enteredPass} onChange={(e) => setEnteredPass(e.target.value)} validateCallback={validatePass} /> - <TitleInput + <Text + label="title" value={title} id="titleInput" readOnly /> @@ -143,7 +128,6 @@ const ViewPaste = (props) => { toggleRenderCallback={() => setIsRenderMode(!isRenderMode)} isRenderMode={isRenderMode} onChange={(e) => setTheme(e.target.value)} - compref={ComponentRef} err={<Error ref={ErrorLabelRef} />} /> </div> diff --git a/frontend/src/components/Watermark.js b/frontend/src/components/Watermark.js new file mode 100644 index 0000000..2f6c8c6 --- /dev/null +++ b/frontend/src/components/Watermark.js @@ -0,0 +1,32 @@ +import styled from "styled-components"; +import React from "react"; +import Footer from "./Footer"; + +const Logo = styled.div` + position: fixed; + bottom: 1em; + left: 2em; + opacity: 0.3; + transition: opacity 0.5s cubic-bezier(.25,.8,.25,1); + + &:hover { + opacity: 1; + } + + & > h1 { + font-size: 50px; + margin: 0 0; + + & > a { + text-decoration: none; + position: relative; + color: ${p => p.theme.colors.text}; + } + } +` +export const Watermark = () => <Logo> + <h1> + <a href="https://github.com/jackyzha0/ctrl-v">ctrl-v</a> + </h1> + <Footer /> +</Logo>
\ No newline at end of file diff --git a/frontend/src/components/decorators/CharLimit.js b/frontend/src/components/decorators/CharLimit.js index 5a6fdca..3d1d981 100644 --- a/frontend/src/components/decorators/CharLimit.js +++ b/frontend/src/components/decorators/CharLimit.js @@ -1,34 +1,24 @@ import React from 'react'; -import styled, { css } from 'styled-components' +import styled from 'styled-components' // show char limit if length > half of max const Chars = styled.p` color: #11111100; - font-family: 'Roboto Mono', monospace; position: absolute; font-size: 0.8em; writing-mode: vertical-rl; top: 50%; - transform: translate(5em, -50%); + transform: translate(4em, -50%); right: 0; transition: all 0.5s cubic-bezier(.25,.8,.25,1); - ${props => - ((props.content.length / props.maxLength) > 0.5) && - css` - color: #111111; - `}; - - ${props => - ((props.content.length / props.maxLength) > 1) && - css` - color: #ee1111; - `}; + ${p => ((p.content.length / p.maxLength) > 0.5) && ` color: ${p.theme.colors.text}; `}; + ${p => ((p.content.length / p.maxLength) > 1) && ` color: ${p.theme.colors.error}; `}; `; const CharLimit = (props) => { return ( - <Chars {...props} >{props.maxLength - props.content.length}/{props.maxLength}</Chars> + <Chars {...props}>{props.maxLength - props.content.length}/{props.maxLength}</Chars> ); } diff --git a/frontend/src/components/decorators/FloatingLabel.js b/frontend/src/components/decorators/FloatingLabel.js deleted file mode 100644 index ef56b44..0000000 --- a/frontend/src/components/decorators/FloatingLabel.js +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import styled, { css } from 'styled-components' - -const StyledLabel = styled.label` - position: absolute; - top: 0.5em; - font-weight: 700; - font-size: 1em; - opacity: 0; - transition: all 0.5s cubic-bezier(.25,.8,.25,1); - - ${props => - (props.value.length > 0) && - css` - top: -0.1em; - opacity: 1 - `}; -` - -const FloatingLabel = (props) => { - return ( - <StyledLabel - label={props.label} - value={props.value} - className={props.id} - htmlFor={props.id}> - {props.label} - </StyledLabel> - ); -} - -export default FloatingLabel
\ No newline at end of file diff --git a/frontend/src/components/decorators/Labelled.js b/frontend/src/components/decorators/Labelled.js new file mode 100644 index 0000000..0d7fe39 --- /dev/null +++ b/frontend/src/components/decorators/Labelled.js @@ -0,0 +1,30 @@ +import styled from "styled-components"; +import React from "react"; + +const StyledLabel = styled.label` + position: relative; + & > span { + position: absolute; + transform: translateY(-0.2em); + font-weight: 700; + font-size: 1em; + opacity: 0.5; + transition: opacity 0.5s cubic-bezier(.25,.8,.25,1); + } + + &:hover > span { + opacity: 1; + } +` + +const FloatingLabel = (props) => <StyledLabel label={props.label}> + {props.children} +</StyledLabel> + + +export const Labelled = ({label, value, children}) => <div> + <FloatingLabel label={label} value={value} > + <span>{label}</span> + {children} + </FloatingLabel> +</div>
\ No newline at end of file diff --git a/frontend/src/components/hooks/useFetchPaste.js b/frontend/src/components/hooks/useFetchPaste.js new file mode 100644 index 0000000..a61ca1e --- /dev/null +++ b/frontend/src/components/hooks/useFetchPaste.js @@ -0,0 +1,5 @@ +import React from 'react' + +export default () => { + +}
\ No newline at end of file diff --git a/frontend/src/components/modals/PasswordModal.js b/frontend/src/components/modals/PasswordModal.js index bf373cc..a829091 100644 --- a/frontend/src/components/modals/PasswordModal.js +++ b/frontend/src/components/modals/PasswordModal.js @@ -1,19 +1,9 @@ import React, { useRef } from 'react'; import Modal from 'react-modal'; -import { PassInput } from '../Inputs' -import { RightPad, LeftPad, ModalHeader, Padding } from './shared' +import { Password } from '../Inputs' +import {ModalHeader, Padding, modalStyles, Form} from './shared' import Error from '../Err'; - -const modalStyles = { - content: { - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)', - width: '400px', - height: '250px', - border: '1px solid #11111188' - } -}; +import {SubmitButton} from "../Common/Button"; const PasswordModal = (props) => { const ErrorLabel = useRef(null); @@ -31,21 +21,16 @@ const PasswordModal = (props) => { style={modalStyles} contentLabel="enter paste password" > - <form onSubmit={submitPassword}> - <LeftPad> - <ModalHeader><span role="img" aria-label="warning">🚧 </span>err: password protected</ModalHeader> - </LeftPad> - <RightPad> - <PassInput - value={props.value} - onChange={props.onChange} /> - </RightPad> - <LeftPad> - <input className="lt-button lt-shadow lt-hover" type="submit" value="continue" /> - <Padding /> - <Error ref={ErrorLabel} /> - </LeftPad> - </form> + <Form onSubmit={submitPassword}> + <ModalHeader><span role="img" aria-label="warning">🚧 </span>err: password protected</ModalHeader> + <Password + placeholder="hunter2" + value={props.value} + onChange={props.onChange} /> + <SubmitButton type="submit" value="continue" /> + <Padding /> + <Error ref={ErrorLabel} /> + </Form> </Modal> ); } diff --git a/frontend/src/components/modals/PasteModal.js b/frontend/src/components/modals/PasteModal.js index 7b28abb..e7dbed2 100644 --- a/frontend/src/components/modals/PasteModal.js +++ b/frontend/src/components/modals/PasteModal.js @@ -1,20 +1,10 @@ import React from 'react'; import Modal from 'react-modal'; -import { LeftPad, ModalHeader, RightPad } from './shared' +import {Form, ModalHeader, modalStyles} from './shared' import { useHistory } from 'react-router-dom'; -import { PasteURLInput } from '../Inputs' +import { Text } from '../Inputs' import { useClipboard } from 'use-clipboard-copy'; - -const modalStyles = { - content: { - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)', - width: '500px', - height: '250px', - border: '1px solid #11111188' - } -}; +import {Button} from "../Common/Button"; const PasteModal = (props) => { const history = useHistory(); @@ -34,35 +24,27 @@ const PasteModal = (props) => { style={modalStyles} contentLabel="paste created" > - <form onSubmit={redir}> - <LeftPad> - <ModalHeader><span role="img" aria-label="success">📎 </span>paste created</ModalHeader> - </LeftPad> - <RightPad> - <PasteURLInput - id="paste_modal" - fullURL={fullURL} /> - <input - hidden - type="text" - value={fullURL} - readOnly - ref={clipboard.target} /> - </RightPad> - <LeftPad> - <button - className="lt-button lt-shadow lt-hover" - type="submit"> - view - </button> - <button - className="lt-button lt-shadow lt-hover" - type="button" - onClick={clipboard.copy}> - {clipboard.copied ? 'copied' : 'copy url'} - </button> - </LeftPad> - </form> + <Form onSubmit={redir}> + <ModalHeader> + <span role="img" aria-label="success">📎 </span>paste created + </ModalHeader> + <Text + label="url" + type="text" + value={fullURL} + readOnly + ref={clipboard.target} /> + <Button + type="submit"> + go to paste + </Button> + <Button + secondary + type="button" + onClick={clipboard.copy}> + {clipboard.copied ? 'copied' : 'copy url'} + </Button> + </Form> </Modal> ); } diff --git a/frontend/src/components/modals/shared.js b/frontend/src/components/modals/shared.js index d63be06..0336818 100644 --- a/frontend/src/components/modals/shared.js +++ b/frontend/src/components/modals/shared.js @@ -1,11 +1,19 @@ import styled from 'styled-components' -export const RightPad = styled.div` - margin-right: 3em; -` +export const modalStyles = { + content: { + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + width: '500px', + height: '250px', + border: '1px solid #11111188', + }, +}; -export const LeftPad = styled.div` - margin-left: 2em; +export const Form = styled.form` + margin: 2em; + margin-right: 3em; ` export const ModalHeader = styled.h3` diff --git a/frontend/src/components/renderers/Code.js b/frontend/src/components/renderers/Code.js index a312c51..4ab1175 100644 --- a/frontend/src/components/renderers/Code.js +++ b/frontend/src/components/renderers/Code.js @@ -1,7 +1,9 @@ import React from 'react'; import SyntaxHighlighter from 'react-syntax-highlighter'; +import virtualizedRenderer from 'react-syntax-highlighter-virtualized-renderer'; import { atomOneLight, ascetic, atomOneDark, dracula, ocean } from 'react-syntax-highlighter/dist/esm/styles/hljs'; import styled from 'styled-components' +import {Border, CodeLike, DropShadow, Rounded} from "../Common/mixins"; export const THEMES = Object.freeze({ 'atom': atomOneLight, @@ -12,66 +14,50 @@ export const THEMES = Object.freeze({ }) export const LANGS = Object.freeze({ - 'bash': 'bash', - 'c': 'c', - 'c++': 'cpp', - 'c#': 'cs', - 'css': 'css', - 'docker': 'dockerfile', - 'go': 'go', - 'haskell': 'haskell', - 'html': 'html', - 'java': 'java', - 'js': 'javascript', - 'jsx': 'jsx', 'latex': 'latex', - 'lisp': 'lisp', - 'makefile': 'makefile', 'markdown': 'markdown', - 'php': 'php', - 'python': 'python', - 'raw': 'text', - 'ruby': 'ruby', - 'scala': 'scala', - 'yaml': 'yaml' + 'detect': 'text', }) -const StyledPre = styled.pre` - width: 100%; - font-size: 0.8em; - min-height: 1.2em; - border-radius: 3px !important; - border: 1px solid #565656 !important; - padding: calc(0.8em - 1px) !important; - outline: none; +export const StyledPre = styled.pre` + ${Rounded}; + ${Border}; + ${DropShadow}; + width: calc(100%); + padding: calc(0.6em - 1px) !important; margin: 1.7em 0; + position: relative; + outline: none; + + & code { + ${CodeLike} + } - & code:first-child { + & code:first-child:not(:only-of-type) { margin-right: 10px !important; border-radius: 0 !important; border-right: 1px solid #11111155 !important; } ` -const CodeRenderer = React.forwardRef((props, ref) => { - - const Pre = (props) => { - return ( - <StyledPre {...props} ref={ref} /> - ); - } - - return ( - <div className="lt-shadow"> - <SyntaxHighlighter - language={LANGS[props.lang]} - style={THEMES[props.theme]} - showLineNumbers - PreTag={Pre}> - {props.content} - </SyntaxHighlighter> - </div> - ); -}); +export const Highlighter = ({language, lineNumbers, theme, pre = StyledPre, children}) => <SyntaxHighlighter + language={LANGS[language]} + style={THEMES[theme]} + showLineNumbers={lineNumbers} + PreTag={pre}> + {children} +</SyntaxHighlighter> + +const CodeRenderer = (props) => { + return (<Highlighter + lineNumbers={true} + language={props.lang} + theme={props.theme} + renderer={virtualizedRenderer()} + pre={StyledPre} + > + {props.content} + </Highlighter>) +}; export default CodeRenderer
\ No newline at end of file diff --git a/frontend/src/components/renderers/Raw.js b/frontend/src/components/renderers/Raw.js index d4dc830..182bfff 100644 --- a/frontend/src/components/renderers/Raw.js +++ b/frontend/src/components/renderers/Raw.js @@ -1,12 +1,10 @@ import React, { useState, useEffect } from 'react'; import styled from 'styled-components' import { FetchPaste } from '../../helpers/httpHelper' +import {CodeLike} from "../Common/mixins"; const RawText = styled.pre` - word-wrap: break-word; - white-space: pre-wrap; - line-height: initial; - font-size: 0.8em; + ${CodeLike} padding: 0 1em; ` diff --git a/frontend/src/components/renderers/RenderDispatch.js b/frontend/src/components/renderers/RenderDispatch.js index 3f1c87b..365a822 100644 --- a/frontend/src/components/renderers/RenderDispatch.js +++ b/frontend/src/components/renderers/RenderDispatch.js @@ -8,16 +8,16 @@ const RenderWrapper = styled.div` padding: 1em; ` -const RenderDispatch = React.forwardRef((props, ref) => { +const RenderDispatch = (props) => { switch (props.language) { case 'latex': return ( - <RenderWrapper ref={ref}> + <RenderWrapper> <Latex content={props.content} /> </RenderWrapper>) case 'markdown': return ( - <RenderWrapper ref={ref} className="md" > + <RenderWrapper className="md" > <Markdown content={props.content} /> </RenderWrapper>) default: @@ -26,9 +26,8 @@ const RenderDispatch = React.forwardRef((props, ref) => { content={props.content} lang={props.language} theme={props.theme} - ref={ref} id="pasteInput" />) } -}); +}; export default RenderDispatch
\ No newline at end of file diff --git a/frontend/src/css/index.css b/frontend/src/css/index.css index fcda1a3..0340813 100644 --- a/frontend/src/css/index.css +++ b/frontend/src/css/index.css @@ -4,20 +4,13 @@ } } -body { - margin: 0; - padding: 0; - background-color: #faf9f5; - font-family: 'Lora', serif; -} - form { width: 100%; } textarea, input[type=text], input[type=password], .Dropdown-root { width: 100%; - font-family: 'Roboto Mono', monospace; + font-family: 'JetBrains Mono', monospace; font-size: 0.8em; padding: calc(0.8em - 1px); border-radius: 3px; @@ -37,7 +30,7 @@ textarea, input[type=text], input[type=password], .Dropdown-root { code, pre { background: #00000000; - font-family: 'Roboto Mono', monospace; + font-family: 'JetBrains Mono', monospace; padding: initial; border-radius: 3px; outline: none; @@ -105,7 +98,7 @@ a { } input[type=submit], button[type=submit] { - font-family: 'Lora', serif; + font-family: 'JetBrains Mono', serif; font-weight: 700; color: #faf9f5; background-color: #111111; @@ -115,7 +108,7 @@ input[type=submit], button[type=submit] { } button[type=button] { - font-family: 'Lora', serif; + font-family: 'JetBrains Mono', serif; font-weight: 700; width: 8em; padding: calc(0.8em - 1px) 1.5em; @@ -127,32 +120,6 @@ button[type=button] { margin: 2em 2em; } -.mainLogo { - font-size: 50px; - margin: 0 0; - display: inline-block; -} - -.mainLogo a { - text-decoration: none; - position: relative; - margin: 3px; -} - -.mainLogo a::after { - content: ""; - position: absolute; - left: 0; - bottom: 0; - height: 4px; - background-color: #111111; - width: 0; - transition: width 0.25s ease; -} - -.mainLogo:hover a::after { - width: 100%; -} /* fixing markdown renderer */ .md h3 { diff --git a/frontend/src/index.js b/frontend/src/index.js index 437fe03..7173ce5 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -1,7 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import './css/index.css'; -import App from './components/App'; +import App from './App'; ReactDOM.render( <React.StrictMode> diff --git a/frontend/src/theme/GlobalStyle.js b/frontend/src/theme/GlobalStyle.js new file mode 100644 index 0000000..9fe80a5 --- /dev/null +++ b/frontend/src/theme/GlobalStyle.js @@ -0,0 +1,11 @@ +import { createGlobalStyle } from 'styled-components' + +export default createGlobalStyle` + body { + margin: 0; + padding: 0; + background: ${(p) => p.theme.colors.background}; + font-family: 'JetBrains Mono', monospace; + color: ${(p) => p.theme.colors.text}; + } +`
\ No newline at end of file diff --git a/frontend/src/theme/ThemeProvider.js b/frontend/src/theme/ThemeProvider.js new file mode 100644 index 0000000..d9edcb0 --- /dev/null +++ b/frontend/src/theme/ThemeProvider.js @@ -0,0 +1,13 @@ +import React from 'react' +import { ThemeProvider } from 'styled-components' + +const theme = { + colors: { + background: '#faf9f5', + border: '#565656', + text: '#111111', + error: '#ee1111', + }, +} + +export default ({ children }) => <ThemeProvider theme={theme}>{children}</ThemeProvider>
\ No newline at end of file diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 2289530..27f2ab3 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1081,6 +1081,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.8.7": + version "7.13.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.9.tgz#97dbe2116e2630c489f22e0656decd60aaa1fcee" + integrity sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.1", "@babel/template@^7.4.0", "@babel/template@^7.8.6": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" @@ -2444,7 +2451,7 @@ babel-preset-react-app@^9.1.2: babel-plugin-macros "2.8.0" babel-plugin-transform-react-remove-prop-types "0.4.24" [email protected], babel-runtime@^6.26.0: [email protected], babel-runtime@^6.18.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= @@ -3104,6 +3111,11 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" +clsx@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" + integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -3709,6 +3721,11 @@ csstype@^2.2.0: resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.10.tgz#e63af50e66d7c266edb6b32909cfd0aabe03928b" integrity sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w== +csstype@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.7.tgz#2a5fb75e1015e84dd15692f71e89a1450290950b" + integrity sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g== + cyclist@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" @@ -4013,6 +4030,14 @@ dom-converter@^0.2: dependencies: utila "~0.4" +dom-helpers@^5.1.3: + version "5.2.0" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.0.tgz#57fd054c5f8f34c52a3eeffdb7e7e93cd357d95b" + integrity sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + dom-serializer@0: version "0.2.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" @@ -5361,6 +5386,11 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== +highlight.js@~9.12.0: + version "9.12.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" + integrity sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4= + highlight.js@~9.15.0, highlight.js@~9.15.1: version "9.15.10" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.15.10.tgz#7b18ed75c90348c045eef9ed08ca1319a2219ad2" @@ -5681,13 +5711,6 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -indent-textarea@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/indent-textarea/-/indent-textarea-2.0.2.tgz#9fc142cb0cf40c1b0320558c017eddb8dd494174" - integrity sha512-2E/WQXpCOd3lLJoLPk8QIArLfflZNoGD6udendhbRpRpA+nkIYSNexsY9fpSCjw4poqKfuP51d9ASYucFdjm4Q== - dependencies: - insert-text-textarea "^2.0.0" - indexes-of@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" @@ -5764,11 +5787,6 @@ inquirer@^7.0.0: strip-ansi "^6.0.0" through "^2.3.6" -insert-text-textarea@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/insert-text-textarea/-/insert-text-textarea-2.0.1.tgz#ac3ccbb05b27a1280ca9ea62349c5ef65ba7d4a0" - integrity sha512-I4PC8aD1HjD61lqPiAxauv1r6Tc0vPk3BGauZw/oQl+1YnKhStzHOIG0K7peKZgZ7XG0N/OmbR8KMZ5wE0n1tA== - internal-ip@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" @@ -7075,6 +7093,14 @@ [email protected]: fault "^1.0.2" highlight.js "~9.15.0" +lowlight@~1.9.1: + version "1.9.2" + resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.9.2.tgz#0b9127e3cec2c3021b7795dd81005c709a42fdd1" + integrity sha512-Ek18ElVCf/wF/jEm1b92gTnigh94CtBNWiZ2ad+vTgW7cTmQxUY3I98BjHK68gZAJEWmybGBZgx9qv3QxLQB/Q== + dependencies: + fault "^1.0.2" + highlight.js "~9.12.0" + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -9464,6 +9490,19 @@ [email protected]: optionalDependencies: fsevents "2.1.2" +react-simple-code-editor@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/react-simple-code-editor/-/react-simple-code-editor-0.11.0.tgz#bb57c7c29b570f2ab229872599eac184f5bc673c" + integrity sha512-xGfX7wAzspl113ocfKQAR8lWPhavGWHL3xSzNLeseDRHysT+jzRBi/ExdUqevSMos+7ZtdfeuBOXtgk9HTwsrw== + +react-syntax-highlighter-virtualized-renderer@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/react-syntax-highlighter-virtualized-renderer/-/react-syntax-highlighter-virtualized-renderer-1.1.0.tgz#7536d9f18f9cce736fac15031a891b8cbaabe90b" + integrity sha1-dTbZ8Y+cznNvrBUDGokbjLqr6Qs= + dependencies: + react-syntax-highlighter "^5.1.2" + react-virtualized "^9.3.0" + react-syntax-highlighter@^12.2.1: version "12.2.1" resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-12.2.1.tgz#14d78352da1c1c3f93c6698b70ec7c706b83493e" @@ -9475,6 +9514,27 @@ react-syntax-highlighter@^12.2.1: prismjs "^1.8.4" refractor "^2.4.1" +react-syntax-highlighter@^5.1.2: + version "5.8.0" + resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-5.8.0.tgz#a220c010fd0641751d93532509ba7159cc3a4383" + integrity sha512-+FolT9NhFBqE4SsZDelSzsYJJS/JCnQqo4+GxLrFPoML548uvr8f4Eh5nnd5o6ERKFW7ryiygOX9SPnxdnlpkg== + dependencies: + babel-runtime "^6.18.0" + highlight.js "~9.12.0" + lowlight "~1.9.1" + +react-virtualized@^9.3.0: + version "9.22.3" + resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.22.3.tgz#f430f16beb0a42db420dbd4d340403c0de334421" + integrity sha512-MKovKMxWTcwPSxE1kK1HcheQTWfuCxAuBoSTf2gwyMM21NdX/PXUhnoP8Uc5dRKd+nKm8v41R36OellhdCpkrw== + dependencies: + "@babel/runtime" "^7.7.2" + clsx "^1.0.4" + dom-helpers "^5.1.3" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-lifecycles-compat "^3.0.4" + react@^16.12.0, react@^16.13.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" |