Add prettier

main
Andrew Dassonville 7 years ago committed by Jake Dexheimer
parent 6b1e0aedaa
commit 7e69ca2897

@ -0,0 +1,4 @@
{
singleQuote: true,
printWidth: 100
}

@ -0,0 +1,10 @@
language: node_js
node_js: stable
cache:
yarn: true
directories:
- node_modules
install:
- yarn
script:
- yarn lint

@ -1,12 +1,18 @@
import React from 'react' import React from 'react';
import { COLORS } from '../lib/constants' import { COLORS } from '../lib/constants';
export default (props) => ( export default props => (
<button onClick={props.onClick} style={Object.assign({ <button
onClick={props.onClick}
style={Object.assign(
{
background: COLORS.BLACK, background: COLORS.BLACK,
color: props.color, color: props.color,
border: `0.5px solid ${props.color}` border: `0.5px solid ${props.color}`
}, props.style)}> },
props.style
)}
>
<span>{props.title}</span> <span>{props.title}</span>
<style jsx>{` <style jsx>{`
button { button {
@ -24,4 +30,4 @@ export default (props) => (
} }
`}</style> `}</style>
</button> </button>
) );

@ -1,14 +1,14 @@
import { EOL } from 'os' import { EOL } from 'os';
import * as hljs from 'highlight.js' import * as hljs from 'highlight.js';
import React from 'react' import React from 'react';
import domtoimage from 'dom-to-image' import domtoimage from 'dom-to-image';
import CodeMirror from 'react-codemirror' import CodeMirror from 'react-codemirror';
import Spinner from 'react-spinner' import Spinner from 'react-spinner';
import toHash from 'tohash' import toHash from 'tohash';
import WindowControls from '../components/WindowControls' import WindowControls from '../components/WindowControls';
import { COLORS, DEFAULT_LANGUAGE, LANGUAGES } from '../lib/constants' import { COLORS, DEFAULT_LANGUAGE, LANGUAGES } from '../lib/constants';
const LANGUAGE_HASH = toHash(LANGUAGES, 'module') const LANGUAGE_HASH = toHash(LANGUAGES, 'module');
const DEFAULT_SETTINGS = { const DEFAULT_SETTINGS = {
paddingVertical: '50px', paddingVertical: '50px',
@ -18,56 +18,56 @@ const DEFAULT_SETTINGS = {
background: '#fed0ec', background: '#fed0ec',
theme: 'seti', theme: 'seti',
language: DEFAULT_LANGUAGE language: DEFAULT_LANGUAGE
} };
class Carbon extends React.Component { class Carbon extends React.Component {
constructor(props) { constructor(props) {
super(props) super(props);
this.state = { this.state = {
loading: true, loading: true,
language: props.config.language, language: props.config.language
} };
this.handleLanguageChange = this.handleLanguageChange.bind(this) this.handleLanguageChange = this.handleLanguageChange.bind(this);
this.codeUpdated = this.codeUpdated.bind(this) this.codeUpdated = this.codeUpdated.bind(this);
} }
componentDidMount() { componentDidMount() {
this.setState({ this.setState({
loading: false loading: false
}) });
this.handleLanguageChange(this.props.children) this.handleLanguageChange(this.props.children);
} }
componentWillReceiveProps(newProps) { componentWillReceiveProps(newProps) {
this.handleLanguageChange(newProps.children, { customProps: newProps }) this.handleLanguageChange(newProps.children, { customProps: newProps });
} }
codeUpdated(newCode) { codeUpdated(newCode) {
this.handleLanguageChange(newCode) this.handleLanguageChange(newCode);
this.props.updateCode(newCode) this.props.updateCode(newCode);
} }
handleLanguageChange(newCode, config) { handleLanguageChange(newCode, config) {
const props = (config && config.customProps) || this.props const props = (config && config.customProps) || this.props;
if (props.config.language === 'auto') { if (props.config.language === 'auto') {
// try to set the language // try to set the language
const detectedLanguage = hljs.highlightAuto(newCode).language const detectedLanguage = hljs.highlightAuto(newCode).language;
const languageModule = LANGUAGE_HASH[detectedLanguage] const languageModule = LANGUAGE_HASH[detectedLanguage];
if (languageModule) { if (languageModule) {
this.setState({ language: languageModule.module }) this.setState({ language: languageModule.module });
} }
} else { } else {
this.setState({ language: props.config.language }) this.setState({ language: props.config.language });
} }
} }
render() { render() {
const config = Object.assign(DEFAULT_SETTINGS, this.props.config) const config = Object.assign(DEFAULT_SETTINGS, this.props.config);
const options = { const options = {
lineNumbers: false, lineNumbers: false,
@ -76,32 +76,35 @@ class Carbon extends React.Component {
scrollBarStyle: null, scrollBarStyle: null,
viewportMargin: Infinity, viewportMargin: Infinity,
lineWrapping: true lineWrapping: true
} };
// create styles // create styles
const containerStyle = { const containerStyle = {
background: config.background, background: config.background,
padding: `${config.paddingVertical} ${config.paddingHorizontal}` padding: `${config.paddingVertical} ${config.paddingHorizontal}`
} };
// set content to spinner if loading, else editor // set content to spinner if loading, else editor
let content = ( let content = (
<div> <div>
<Spinner /> <Spinner />
<style jsx>{` <style jsx>
{`
div { div {
height: 352px; height: 352px;
} }
`} `}
</style> </style>
</div> </div>
) );
if (this.state.loading === false) { if (this.state.loading === false) {
content = ( content = (
<div id="container" style={containerStyle}> <div id="container" style={containerStyle}>
{config.windowControls ? <WindowControls theme={config.windowTheme} /> : null} {config.windowControls ? <WindowControls theme={config.windowTheme} /> : null}
<CodeMirror <CodeMirror
className={`CodeMirror__container window-theme__${config.windowTheme} ${config.dropShadow ? 'dropshadow' : ''}`} className={`CodeMirror__container window-theme__${config.windowTheme} ${config.dropShadow
? 'dropshadow'
: ''}`}
onChange={this.codeUpdated} onChange={this.codeUpdated}
value={this.props.children} value={this.props.children}
options={options} options={options}
@ -147,7 +150,7 @@ class Carbon extends React.Component {
} }
`}</style> `}</style>
</div> </div>
) );
} }
return ( return (
@ -162,8 +165,8 @@ class Carbon extends React.Component {
} }
`}</style> `}</style>
</div> </div>
) );
} }
} }
export default Carbon export default Carbon;

@ -1,27 +1,27 @@
import React from 'react' import React from 'react';
import enhanceWithClickOutside from 'react-click-outside' import enhanceWithClickOutside from 'react-click-outside';
import { TwitterPicker } from 'react-color' import { TwitterPicker } from 'react-color';
import WindowPointer from './WindowPointer' import WindowPointer from './WindowPointer';
import { COLORS } from '../lib/constants' import { COLORS } from '../lib/constants';
class ColorPicker extends React.Component { class ColorPicker extends React.Component {
constructor() { constructor() {
super() super();
this.state = { isVisible: false } this.state = { isVisible: false };
this.toggle = this.toggle.bind(this) this.toggle = this.toggle.bind(this);
this.handlePickColor = this.handlePickColor.bind(this) this.handlePickColor = this.handlePickColor.bind(this);
} }
toggle() { toggle() {
this.setState({ isVisible: !this.state.isVisible }) this.setState({ isVisible: !this.state.isVisible });
} }
handleClickOutside() { handleClickOutside() {
this.setState({ isVisible: false }) this.setState({ isVisible: false });
} }
handlePickColor(color) { handlePickColor(color) {
this.props.onChange(color.hex) this.props.onChange(color.hex);
} }
render() { render() {
@ -31,11 +31,15 @@ class ColorPicker extends React.Component {
<div className="colorpicker-label"> <div className="colorpicker-label">
<span>BG</span> <span>BG</span>
</div> </div>
<div className="bg-color" style={{background: this.props.bg}} onClick={this.toggle}></div> <div className="bg-color" style={{ background: this.props.bg }} onClick={this.toggle} />
</div> </div>
<div className="colorpicker-picker" hidden={!this.state.isVisible}> <div className="colorpicker-picker" hidden={!this.state.isVisible}>
<WindowPointer fromLeft="15px" /> <WindowPointer fromLeft="15px" />
<TwitterPicker color={this.props.bg} onChangeComplete={this.handlePickColor} triangle="hide" /> <TwitterPicker
color={this.props.bg}
onChangeComplete={this.handlePickColor}
triangle="hide"
/>
</div> </div>
<style jsx>{` <style jsx>{`
.colorpicker-container { .colorpicker-container {
@ -79,11 +83,11 @@ class ColorPicker extends React.Component {
width: 278px !important; width: 278px !important;
border: 0.5px solid ${COLORS.SECONDARY} !important; border: 0.5px solid ${COLORS.SECONDARY} !important;
border-radius: 3px !important; border-radius: 3px !important;
background: #1A1A1A !important; background: #1a1a1a !important;
} }
.colorpicker-picker > :global(div > div:nth-child(3) > div:nth-child(11)) { .colorpicker-picker > :global(div > div:nth-child(3) > div:nth-child(11)) {
background: #1A1A1A !important; background: #1a1a1a !important;
border: 0.5px solid ${COLORS.SECONDARY} !important; border: 0.5px solid ${COLORS.SECONDARY} !important;
border-right: none !important; border-right: none !important;
border-radius: 3px 0 0 3px !important; border-radius: 3px 0 0 3px !important;
@ -100,8 +104,8 @@ class ColorPicker extends React.Component {
} }
`}</style> `}</style>
</div> </div>
) );
} }
} }
export default enhanceWithClickOutside(ColorPicker) export default enhanceWithClickOutside(ColorPicker);

@ -1,29 +1,29 @@
import React from 'react' import React from 'react';
import enhanceWithClickOutside from 'react-click-outside' import enhanceWithClickOutside from 'react-click-outside';
import ArrowDown from './svg/Arrowdown' import ArrowDown from './svg/Arrowdown';
import Checkmark from './svg/Checkmark' import Checkmark from './svg/Checkmark';
import { COLORS } from '../lib/constants' import { COLORS } from '../lib/constants';
class Dropdown extends React.Component { class Dropdown extends React.Component {
constructor(props) { constructor(props) {
super() super();
this.state = { this.state = {
isVisible: false, isVisible: false,
selected: props.selected || props.list[0] selected: props.selected || props.list[0]
} };
this.select = this.select.bind(this) this.select = this.select.bind(this);
this.toggle = this.toggle.bind(this) this.toggle = this.toggle.bind(this);
} }
select(item) { select(item) {
if (this.state.selected !== item) { if (this.state.selected !== item) {
this.props.onChange(item) this.props.onChange(item);
this.setState({ selected: item }) this.setState({ selected: item });
} }
} }
toggle() { toggle() {
this.setState({ isVisible: !this.state.isVisible }) this.setState({ isVisible: !this.state.isVisible });
} }
handleClickOutside() { handleClickOutside() {
@ -52,23 +52,29 @@ class Dropdown extends React.Component {
} }
`}</style> `}</style>
</div> </div>
)) ));
} }
render() { render() {
// find longest list value in number of characters // find longest list value in number of characters
const MIN_WIDTH = this.props.list.reduce((max, { name }) => const MIN_WIDTH = this.props.list.reduce(
(name.length > max ? name.length : max), 0) (max, { name }) => (name.length > max ? name.length : max),
0
);
return ( return (
<div className="dropdown-container" style={{ minWidth: MIN_WIDTH * 16 }} onClick={this.toggle}> <div
className="dropdown-container"
style={{ minWidth: MIN_WIDTH * 16 }}
onClick={this.toggle}
>
<div className={`dropdown-display ${this.state.isVisible ? 'is-visible' : ''}`}> <div className={`dropdown-display ${this.state.isVisible ? 'is-visible' : ''}`}>
<span>{this.state.selected.name}</span> <span>{this.state.selected.name}</span>
<div className="arrow-down"><ArrowDown /></div> <div className="arrow-down">
<ArrowDown />
</div> </div>
<div className="dropdown-list">
{ this.renderListItems() }
</div> </div>
<div className="dropdown-list">{this.renderListItems()}</div>
<style jsx>{` <style jsx>{`
.arrow-down { .arrow-down {
position: absolute; position: absolute;
@ -115,8 +121,8 @@ class Dropdown extends React.Component {
} }
`}</style> `}</style>
</div> </div>
) );
} }
} }
export default enhanceWithClickOutside(Dropdown) export default enhanceWithClickOutside(Dropdown);

@ -1,15 +1,27 @@
import React from 'react' import React from 'react';
import Link from 'next/link' import Link from 'next/link';
import { COLORS } from '../lib/constants' import { COLORS } from '../lib/constants';
const Footer = (props) => ( const Footer = props => (
<div className="mt3"> <div className="mt3">
<div className="mt3"> <div className="mt3">
<Link href="/about"><a className="link">about</a></Link> <Link href="/about">
<a className="link" href="https://github.com/dawnlabs/carbon/issues/new">send feedback</a> <a className="link">about</a>
<a className="link" href="https://github.com/dawnlabs/carbon">source</a> </Link>
<a className="link" href="https://github.com/dawnlabs/carbon/issues/new">
send feedback
</a>
<a className="link" href="https://github.com/dawnlabs/carbon">
source
</a>
</div>
<div className="mt3 mb2">
a project by{' '}
<a className="dawn-link" href="https://dawnlabs.io">
Dawn Labs
</a>{' '}
¬
</div> </div>
<div className="mt3 mb2">a project by <a className="dawn-link" href="https://dawnlabs.io">Dawn Labs</a> ¬</div>
<style jsx>{` <style jsx>{`
div > div { div > div {
text-align: center; text-align: center;
@ -35,6 +47,6 @@ const Footer = (props) => (
} }
`}</style> `}</style>
</div> </div>
) );
export default Footer export default Footer;

@ -1,11 +1,18 @@
import React from 'react' import React from 'react';
import Logo from './svg/Logo' import Logo from './svg/Logo';
const Header = ({ enableHeroText }) => ( const Header = ({ enableHeroText }) => (
<div className="header mb4"> <div className="header mb4">
<div className="header-content"> <div className="header-content">
<a href="/"><Logo /></a> <a href="/">
{ enableHeroText ? (<h2 className="mt3">Create and share beautiful images of your source code.<br/> Start typing, or drop a file into the text area to get started.</h2>) : null } <Logo />
</a>
{enableHeroText ? (
<h2 className="mt3">
Create and share beautiful images of your source code.<br /> Start typing, or drop a file
into the text area to get started.
</h2>
) : null}
</div> </div>
<style jsx>{` <style jsx>{`
.header { .header {
@ -27,6 +34,6 @@ const Header = ({ enableHeroText }) => (
} }
`}</style> `}</style>
</div> </div>
) );
export default Header export default Header;

@ -0,0 +1,22 @@
import React from 'react';
import { Controls, ControlsBW } from './svg/Controls';
export default ({ language }) => (
<div className="language">
{language.name}
<style jsx>
{`
.language {
position: absolute;
top: 7px;
right: 7px;
background: rgba(0, 0, 0, 0.3);
font-size: 12px;
padding: 5px;
border-radius: 3px;
z-index: 1;
}
`}
</style>
</div>
);

@ -1,7 +1,7 @@
import Head from 'next/head' import Head from 'next/head';
import { THEMES_ARRAY, COLORS } from '../lib/constants' import { THEMES_ARRAY, COLORS } from '../lib/constants';
import Reset from './style/Reset' import Reset from './style/Reset';
import Typography from './style/Typography' import Typography from './style/Typography';
export default () => ( export default () => (
<div className="meta"> <div className="meta">
@ -9,21 +9,38 @@ export default () => (
<meta charSet="utf-8" /> <meta charSet="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Carbon is the easiest way to create and share beautiful images of your source code." /> <meta
name="description"
content="Carbon is the easiest way to create and share beautiful images of your source code."
/>
<meta name="application-name" content="Carbon" /> <meta name="application-name" content="Carbon" />
<meta name="twitter:title" content="Carbon" /> <meta name="twitter:title" content="Carbon" />
<meta name="twitter:description" content="Carbon is the easiest way to create and share beautiful images of your source code." /> <meta
name="twitter:description"
content="Carbon is the easiest way to create and share beautiful images of your source code."
/>
<meta name="og:title" content="Carbon" /> <meta name="og:title" content="Carbon" />
<meta name="og:description" content="Carbon is the easiest way to create and share beautiful images of your source code." /> <meta
name="og:description"
content="Carbon is the easiest way to create and share beautiful images of your source code."
/>
<meta name="og:image" content="/static/banner.png" /> <meta name="og:image" content="/static/banner.png" />
<title>Carbon</title> <title>Carbon</title>
<link rel="shortcut icon" href="/static/favicon.ico" /> <link rel="shortcut icon" href="/static/favicon.ico" />
<link rel="stylesheet" href='//cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/codemirror.min.css' /> <link
{ rel="stylesheet"
THEMES_ARRAY.map((theme, i) => ( href="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/codemirror.min.css"
<link key={i} rel="stylesheet" href={theme.link || `//cdnjs.cloudflare.com/ajax/libs/codemirror/5.30.0/theme/${theme.id}.min.css`}/> />
)) {THEMES_ARRAY.map((theme, i) => (
<link
key={i}
rel="stylesheet"
href={
theme.link ||
`//cdnjs.cloudflare.com/ajax/libs/codemirror/5.30.0/theme/${theme.id}.min.css`
} }
/>
))}
<link rel="stylesheet" href="//cdn.jsdelivr.net/font-hack/2.020/css/hack-extended.min.css" /> <link rel="stylesheet" href="//cdn.jsdelivr.net/font-hack/2.020/css/hack-extended.min.css" />
<link rel="stylesheet" type="text/css" href="/static/react-spinner.css" /> <link rel="stylesheet" type="text/css" href="/static/react-spinner.css" />
</Head> </Head>
@ -35,4 +52,4 @@ export default () => (
} }
`}</style> `}</style>
</div> </div>
) );

@ -22,6 +22,6 @@ const Overlay = props => (
} }
`}</style> `}</style>
</div> </div>
) );
export default Overlay export default Overlay;

@ -1,15 +1,13 @@
import React from 'react' import React from 'react';
import Meta from './Meta' import Meta from './Meta';
import Header from './Header' import Header from './Header';
import Footer from './Footer' import Footer from './Footer';
export default ({ children, enableHeroText }) => ( export default ({ children, enableHeroText }) => (
<div className="main mt4 mb4"> <div className="main mt4 mb4">
<Meta /> <Meta />
<Header enableHeroText={enableHeroText} /> <Header enableHeroText={enableHeroText} />
<div className="page"> <div className="page">{children}</div>
{ children }
</div>
<Footer /> <Footer />
@ -22,4 +20,4 @@ export default ({ children, enableHeroText }) => (
} }
`}</style> `}</style>
</div> </div>
) );

@ -1,48 +1,61 @@
import React from 'react' import React from 'react';
import enhanceWithClickOutside from 'react-click-outside' import enhanceWithClickOutside from 'react-click-outside';
import SettingsIcon from './svg/Settings' import SettingsIcon from './svg/Settings';
import ThemeSelect from './ThemeSelect' import ThemeSelect from './ThemeSelect';
import Slider from './Slider' import Slider from './Slider';
import Toggle from './Toggle' import Toggle from './Toggle';
import WindowPointer from './WindowPointer'; import WindowPointer from './WindowPointer';
import { COLORS } from '../lib/constants' import { COLORS } from '../lib/constants';
class Settings extends React.Component { class Settings extends React.Component {
constructor(props) { constructor(props) {
super() super();
this.state = { this.state = {
isVisible: false isVisible: false
} };
this.toggle = this.toggle.bind(this) this.toggle = this.toggle.bind(this);
} }
toggle() { toggle() {
this.setState({ isVisible: !this.state.isVisible }) this.setState({ isVisible: !this.state.isVisible });
} }
handleClickOutside() { handleClickOutside() {
this.setState({ isVisible: false }) this.setState({ isVisible: false });
} }
render() { render() {
return ( return (
<div className="settings-container"> <div className="settings-container">
<div className={`settings-display ${this.state.isVisible ? 'is-visible' : ''}`} onClick={this.toggle}> <div
className={`settings-display ${this.state.isVisible ? 'is-visible' : ''}`}
onClick={this.toggle}
>
<SettingsIcon /> <SettingsIcon />
</div> </div>
<div className="settings-settings"> <div className="settings-settings">
<WindowPointer fromLeft="15px" /> <WindowPointer fromLeft="15px" />
<ThemeSelect onChange={this.props.onChange.bind(null, 'windowTheme')} /> <ThemeSelect onChange={this.props.onChange.bind(null, 'windowTheme')} />
<Toggle label="Drop shadow" <Toggle
label="Drop shadow"
enabled={this.props.enabled.dropShadow} enabled={this.props.enabled.dropShadow}
onChange={this.props.onChange.bind(null, 'dropShadow')} onChange={this.props.onChange.bind(null, 'dropShadow')}
/> />
<Toggle label="Window controls" <Toggle
label="Window controls"
enabled={this.props.enabled.windowControls} enabled={this.props.enabled.windowControls}
onChange={this.props.onChange.bind(null, 'windowControls')} onChange={this.props.onChange.bind(null, 'windowControls')}
/> />
<Slider label="Padding (vertical)" initialValue={16} onChange={this.props.onChange.bind(null, 'paddingVertical')}/> <Slider
<Slider label="Padding (horizontal)" initialValue={32} onChange={this.props.onChange.bind(null, 'paddingHorizontal')}/> label="Padding (vertical)"
initialValue={16}
onChange={this.props.onChange.bind(null, 'paddingVertical')}
/>
<Slider
label="Padding (horizontal)"
initialValue={32}
onChange={this.props.onChange.bind(null, 'paddingHorizontal')}
/>
</div> </div>
<style jsx>{` <style jsx>{`
.settings-container { .settings-container {
@ -93,11 +106,10 @@ class Settings extends React.Component {
.settings-settings > :global(div):first-child { .settings-settings > :global(div):first-child {
border-bottom: none; border-bottom: none;
} }
`}</style> `}</style>
</div> </div>
) );
} }
} }
export default enhanceWithClickOutside(Settings) export default enhanceWithClickOutside(Settings);

@ -1,27 +1,38 @@
import React from 'react' import React from 'react';
export default class extends React.Component { export default class extends React.Component {
constructor(props) { constructor(props) {
super() super();
this.state = { value: props.initialValue || 0 } this.state = { value: props.initialValue || 0 };
this.handleChange = this.handleChange.bind(this) this.handleChange = this.handleChange.bind(this);
} }
handleChange(e) { handleChange(e) {
this.setState({ value: e.target.value }, () => { this.setState({ value: e.target.value }, () => {
this.props.onChange(`${this.state.value}px`) this.props.onChange(`${this.state.value}px`);
}) });
} }
render() { render() {
const minValue = this.props.minValue || 0 const minValue = this.props.minValue || 0;
const maxValue = this.props.maxValue || 100 const maxValue = this.props.maxValue || 100;
return ( return (
<div className="slider"> <div className="slider">
<span className="label">{this.props.label}</span> <span className="label">{this.props.label}</span>
<input type="range" defaultValue={this.state.value} onChange={this.handleChange} min={minValue} max={maxValue} /> <input
<div className="slider-bg" style={{ transform: `translate3d(${(this.state.value * 1.0 / maxValue) * 100}%, 0px, 0px)` }}></div> type="range"
defaultValue={this.state.value}
onChange={this.handleChange}
min={minValue}
max={maxValue}
/>
<div
className="slider-bg"
style={{
transform: `translate3d(${this.state.value * 1.0 / maxValue * 100}%, 0px, 0px)`
}}
/>
<style jsx>{` <style jsx>{`
.slider { .slider {
position: relative; position: relative;
@ -57,9 +68,8 @@ export default class extends React.Component {
width: 100%; width: 100%;
background: rgba(255, 255, 255, 0.165); background: rgba(255, 255, 255, 0.165);
} }
`}</style> `}</style>
</div> </div>
) );
} }
} }

@ -1,30 +1,30 @@
import React from 'react' import React from 'react';
import { None, BW, Sharp } from './svg/WindowThemes' import { None, BW, Sharp } from './svg/WindowThemes';
import { COLORS } from '../lib/constants' import { COLORS } from '../lib/constants';
const WINDOW_THEMES_MAP = { none: None, sharp: Sharp, bw: BW } const WINDOW_THEMES_MAP = { none: None, sharp: Sharp, bw: BW };
export const WINDOW_THEMES = Object.keys(WINDOW_THEMES_MAP) export const WINDOW_THEMES = Object.keys(WINDOW_THEMES_MAP);
export default class extends React.Component { export default class extends React.Component {
constructor(props) { constructor(props) {
super() super();
this.state = { selected: props.selected || WINDOW_THEMES[0] } this.state = { selected: props.selected || WINDOW_THEMES[0] };
this.select = this.select.bind(this) this.select = this.select.bind(this);
} }
select(theme) { select(theme) {
if (this.state.selected !== theme) { if (this.state.selected !== theme) {
this.props.onChange(theme) this.props.onChange(theme);
this.setState({ selected: theme }) this.setState({ selected: theme });
} }
} }
renderThemes() { renderThemes() {
return WINDOW_THEMES.map((theme, i) => { return WINDOW_THEMES.map((theme, i) => {
const Img = WINDOW_THEMES_MAP[theme] const Img = WINDOW_THEMES_MAP[theme];
return ( return (
<div <div
className={`theme ${this.state.selected === theme ? "selected" : ""}`} className={`theme ${this.state.selected === theme ? 'selected' : ''}`}
key={i} key={i}
onClick={this.select.bind(null, theme)} onClick={this.select.bind(null, theme)}
> >
@ -45,17 +45,15 @@ export default class extends React.Component {
} }
`}</style> `}</style>
</div> </div>
) );
}) });
} }
render() { render() {
return ( return (
<div className="window-theme"> <div className="window-theme">
<span className="label">Window Theme</span> <span className="label">Window Theme</span>
<div className="themes"> <div className="themes">{this.renderThemes()}</div>
{ this.renderThemes() }
</div>
<style jsx>{` <style jsx>{`
.window-theme { .window-theme {
padding: 8px; padding: 8px;
@ -73,6 +71,6 @@ export default class extends React.Component {
} }
`}</style> `}</style>
</div> </div>
) );
} }
} }

@ -1,17 +1,17 @@
import React from 'react' import React from 'react';
import Checkmark from './svg/Checkmark' import Checkmark from './svg/Checkmark';
export default class extends React.Component { export default class extends React.Component {
constructor(props) { constructor(props) {
super() super();
this.state = { isEnabled: props.enabled || false } this.state = { isEnabled: props.enabled || false };
this.toggle = this.toggle.bind(this) this.toggle = this.toggle.bind(this);
} }
toggle() { toggle() {
this.setState({ isEnabled: !this.state.isEnabled }, () => { this.setState({ isEnabled: !this.state.isEnabled }, () => {
this.props.onChange(this.state.isEnabled) this.props.onChange(this.state.isEnabled);
}) });
} }
render() { render() {
@ -30,6 +30,6 @@ export default class extends React.Component {
} }
`}</style> `}</style>
</div> </div>
) );
} }
} }

@ -1,5 +1,5 @@
import React from 'react' import React from 'react';
const Toolbar = (props) => ( const Toolbar = props => (
<div id="toolbar"> <div id="toolbar">
{props.children} {props.children}
<style jsx>{` <style jsx>{`
@ -23,6 +23,6 @@ const Toolbar = (props) => (
} }
`}</style> `}</style>
</div> </div>
) );
export default Toolbar export default Toolbar;

@ -1,10 +1,11 @@
import React from 'react' import React from 'react';
import { Controls, ControlsBW } from './svg/Controls' import { Controls, ControlsBW } from './svg/Controls';
export default ({ theme }) => ( export default ({ theme }) => (
<div className="window-controls"> <div className="window-controls">
{theme === 'bw' ? <ControlsBW /> : <Controls />} {theme === 'bw' ? <ControlsBW /> : <Controls />}
<style jsx>{` <style jsx>
{`
div { div {
margin-top: -24px; margin-top: -24px;
position: relative; position: relative;
@ -15,4 +16,4 @@ export default ({ theme }) => (
`} `}
</style> </style>
</div> </div>
) );

@ -1,4 +1,4 @@
import React from 'react' import React from 'react';
export default ({ fromLeft }) => ( export default ({ fromLeft }) => (
<div style={{ left: fromLeft }}> <div style={{ left: fromLeft }}>
@ -16,4 +16,4 @@ export default ({ fromLeft }) => (
} }
`}</style> `}</style>
</div> </div>
) );

@ -1,25 +1,93 @@
import { COLORS } from '../../lib/constants' import { COLORS } from '../../lib/constants';
export default () => export default () => (
<style jsx global>{` <style jsx global>{`
/* http://meyerweb.com/eric/tools/css/reset/ /* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126 v2.0 | 20110126
License: none (public domain) License: none (public domain)
*/ */
html, body, div, span, applet, object, iframe, html,
h1, h2, h3, h4, h5, h6, p, blockquote, pre, body,
a, abbr, acronym, address, big, cite, code, div,
del, dfn, em, img, ins, kbd, q, s, samp, span,
small, strike, strong, sub, sup, tt, var, applet,
b, u, i, center, object,
dl, dt, dd, ol, ul, li, iframe,
fieldset, form, label, legend, h1,
table, caption, tbody, tfoot, thead, tr, th, td, h2,
article, aside, canvas, details, embed, h3,
figure, figcaption, footer, header, hgroup, h4,
menu, nav, output, ruby, section, summary, h5,
time, mark, audio, video { h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0; margin: 0;
padding: 0; padding: 0;
border: 0; border: 0;
@ -30,18 +98,31 @@ export default () =>
vertical-align: baseline; vertical-align: baseline;
} }
/* HTML5 display-role reset for older browsers */ /* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure, article,
footer, header, hgroup, menu, nav, section { aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block; display: block;
} }
ol, ul { ol,
ul {
list-style: none; list-style: none;
} }
blockquote, q { blockquote,
q {
quotes: none; quotes: none;
} }
blockquote:before, blockquote:after, blockquote:before,
q:before, q:after { blockquote:after,
q:before,
q:after {
content: ''; content: '';
content: none; content: none;
} }
@ -57,12 +138,7 @@ export default () =>
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
background: ${COLORS.BLACK}; background: ${COLORS.BLACK};
color: white; color: white;
font-family: -apple-system, font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Ubuntu, 'Helvetica Neue',
BlinkMacSystemFont,
"Segoe UI",
Roboto,
Ubuntu,
"Helvetica Neue",
sans-serif; sans-serif;
font-weight: 400; font-weight: 400;
font-style: normal; font-style: normal;
@ -73,9 +149,16 @@ export default () =>
min-height: 704px; min-height: 704px;
} }
* { box-sizing: border-box; } * {
box-sizing: border-box;
}
h1, h2, h3, h4, h5, h6 { h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: 500; font-weight: 500;
} }
@ -86,7 +169,7 @@ export default () =>
} }
*::selection { *::selection {
background: rgba(255,255,255,.99); background: rgba(255, 255, 255, 0.99);
color: #121212; color: #121212;
} }
@ -94,7 +177,11 @@ export default () =>
color: #fff; color: #fff;
text-decoration: none; text-decoration: none;
padding-bottom: 3px; padding-bottom: 3px;
background: linear-gradient(to right, rgba(255,255,255, 0.7) 0%, rgba(255,255,255, 0.7) 100%); background: linear-gradient(
to right,
rgba(255, 255, 255, 0.7) 0%,
rgba(255, 255, 255, 0.7) 100%
);
background-size: 1px 1px; background-size: 1px 1px;
background-position: 0 100%; background-position: 0 100%;
background-repeat: repeat-x; background-repeat: repeat-x;
@ -105,3 +192,4 @@ export default () =>
background: none; background: none;
} }
`}</style> `}</style>
);

@ -1,4 +1,4 @@
export default () => export default () => (
<style jsx global>{` <style jsx global>{`
/* https://github.com/jxnblk/type-system /* https://github.com/jxnblk/type-system
Brent Jackson Brent Jackson
@ -10,7 +10,7 @@ export default () =>
--h2: 2.25rem; --h2: 2.25rem;
--h3: 1.5rem; --h3: 1.5rem;
--h4: 1.125rem; --h4: 1.125rem;
--h5: .75rem; --h5: 0.75rem;
--lh: calc(4/3); --lh: calc(4/3);
--mx: 32em; --mx: 32em;
@ -19,7 +19,7 @@ export default () =>
--m2: calc(4/3 * 1em); --m2: calc(4/3 * 1em);
--m3: calc(8/3 * 1em); --m3: calc(8/3 * 1em);
--m4: calc(16/3 * 1em); --m4: calc(16/3 * 1em);
--x1: .5rem; --x1: 0.5rem;
--x2: 1rem; --x2: 1rem;
--x3: 2rem; --x3: 2rem;
--x4: 4rem; --x4: 4rem;
@ -33,61 +33,146 @@ export default () =>
margin: 2rem 0; margin: 2rem 0;
} }
h1, h2, h3 { h1,
h2,
h3 {
margin-top: var(--m1); margin-top: var(--m1);
margin-bottom: 0; margin-bottom: 0;
} }
h4, h5, h6, h4,
p, dl, ol, ul, blockquote { h5,
h6,
p,
dl,
ol,
ul,
blockquote {
margin-top: var(--m2); margin-top: var(--m2);
margin-bottom: var(--m2); margin-bottom: var(--m2);
} }
h1 { font-size: var(--h2) } h1 {
h2, h3 { font-size: var(--h3) } font-size: var(--h2);
h4 { font-size: var(--h4) } }
h5, h6 { font-size: var(--h5) } h2,
h3 {
font-size: var(--h3);
}
h4 {
font-size: var(--h4);
}
h5,
h6 {
font-size: var(--h5);
}
.h0 { font-size: var(--h0) } .h0 {
.h1 { font-size: var(--h1) } font-size: var(--h0);
.h2 { font-size: var(--h2) } }
.h3 { font-size: var(--h3) } .h1 {
.h4 { font-size: var(--h4) } font-size: var(--h1);
.h5 { font-size: var(--h5) } }
.h6 { font-size: var(--h5) } .h2 {
font-size: var(--h2);
}
.h3 {
font-size: var(--h3);
}
.h4 {
font-size: var(--h4);
}
.h5 {
font-size: var(--h5);
}
.h6 {
font-size: var(--h5);
}
@media screen and (min-width: 40em) { @media screen and (min-width: 40em) {
.xh0 { font-size: var(--h0) } .xh0 {
.xh1 { font-size: var(--h1) } font-size: var(--h0);
.xh2 { font-size: var(--h2) } }
.xh3 { font-size: var(--h3) } .xh1 {
.xh4 { font-size: var(--h4) } font-size: var(--h1);
.xh5 { font-size: var(--h5) } }
.xh6 { font-size: var(--h5) } .xh2 {
font-size: var(--h2);
}
.xh3 {
font-size: var(--h3);
}
.xh4 {
font-size: var(--h4);
}
.xh5 {
font-size: var(--h5);
}
.xh6 {
font-size: var(--h5);
}
} }
.lh1 { line-height: 1 } .lh1 {
line-height: 1;
}
/* h0, h1, h3 */ /* h0, h1, h3 */
.lh2 { line-height: calc(7/6 * 1em) } .lh2 {
line-height: calc(7/6 * 1em);
}
/* For body copy */ /* For body copy */
.lh3 { line-height: calc(16/9 * 1em) } .lh3 {
line-height: calc(16/9 * 1em);
}
.mt0 { margin-top: 0 } .mt0 {
.mb0 { margin-bottom: 0 } margin-top: 0;
.mt1 { margin-top: var(--x1) } }
.mb1 { margin-bottom: var(--x1) } .mb0 {
.mt2 { margin-top: var(--x2) } margin-bottom: 0;
.mb2 { margin-bottom: var(--x2) } }
.mt3 { margin-top: var(--x3) } .mt1 {
.mb3 { margin-bottom: var(--x3) } margin-top: var(--x1);
.mt4 { margin-top: var(--x4) } }
.mb4 { margin-bottom: var(--x4) } .mb1 {
.mt5 { margin-top: var(--x5) } margin-bottom: var(--x1);
.mb5 { margin-bottom: var(--x5) } }
.mt6 { margin-top: var(--x6) } .mt2 {
.mb6 { margin-bottom: var(--x6) } margin-top: var(--x2);
}
.mb2 {
margin-bottom: var(--x2);
}
.mt3 {
margin-top: var(--x3);
}
.mb3 {
margin-bottom: var(--x3);
}
.mt4 {
margin-top: var(--x4);
}
.mb4 {
margin-bottom: var(--x4);
}
.mt5 {
margin-top: var(--x5);
}
.mb5 {
margin-bottom: var(--x5);
}
.mt6 {
margin-top: var(--x6);
}
.mb6 {
margin-bottom: var(--x6);
}
.mx { max-width: var(--mx) } .mx {
.bold { font-weight: bold } max-width: var(--mx);
}
.bold {
font-weight: bold;
}
`}</style> `}</style>
);

@ -1,7 +1,12 @@
import React from 'react' import React from 'react';
export default () => ( export default () => (
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="6" viewBox="0 0 10 6"> <svg xmlns="http://www.w3.org/2000/svg" width="10" height="6" viewBox="0 0 10 6">
<path fill="#fff" fillRule="evenodd" d="M93.7890633,17.6396882 L97.4926052,14.0851393 C97.6061243,13.9716202 97.7196417,13.9716202 97.8331608,14.0851393 L98.4929872,14.723681 C98.6065063,14.8372001 98.6065063,14.9507175 98.4929872,15.0642366 L93.9593411,19.4063203 C93.9167714,19.4488899 93.8600127,19.4701744 93.7890633,19.4701744 C93.7181138,19.4701744 93.6613552,19.4488899 93.6187855,19.4063203 L89.0851393,15.0642366 C88.9716202,14.9507175 88.9716202,14.8372001 89.0851393,14.723681 L89.7449658,14.0851393 C89.8584849,13.9716202 89.9720022,13.9716202 90.0855213,14.0851393 L93.7890633,17.6396882 Z" transform="translate(-89 -14)"/> <path
fill="#fff"
fillRule="evenodd"
d="M93.7890633,17.6396882 L97.4926052,14.0851393 C97.6061243,13.9716202 97.7196417,13.9716202 97.8331608,14.0851393 L98.4929872,14.723681 C98.6065063,14.8372001 98.6065063,14.9507175 98.4929872,15.0642366 L93.9593411,19.4063203 C93.9167714,19.4488899 93.8600127,19.4701744 93.7890633,19.4701744 C93.7181138,19.4701744 93.6613552,19.4488899 93.6187855,19.4063203 L89.0851393,15.0642366 C88.9716202,14.9507175 88.9716202,14.8372001 89.0851393,14.723681 L89.7449658,14.0851393 C89.8584849,13.9716202 89.9720022,13.9716202 90.0855213,14.0851393 L93.7890633,17.6396882 Z"
transform="translate(-89 -14)"
/>
</svg> </svg>
) );

@ -1,7 +1,11 @@
import React from 'react' import React from 'react';
export default () => ( export default () => (
<svg xmlns="http://www.w3.org/2000/svg" width="9" height="8" viewBox="0 0 9 7"> <svg xmlns="http://www.w3.org/2000/svg" width="9" height="8" viewBox="0 0 9 7">
<polygon fill="#FFFFFF" fillRule="evenodd" points="2.852 5.016 8.275 0 9 .67 2.852 6.344 0 3.711 .713 3.042"/> <polygon
fill="#FFFFFF"
fillRule="evenodd"
points="2.852 5.016 8.275 0 9 .67 2.852 6.344 0 3.711 .713 3.042"
/>
</svg> </svg>
) );

@ -1,4 +1,4 @@
import React from 'react' import React from 'react';
export const Controls = () => ( export const Controls = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"> <svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14">
@ -8,7 +8,7 @@ export const Controls = () => (
<circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" strokeWidth=".5" /> <circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" strokeWidth=".5" />
</g> </g>
</svg> </svg>
) );
export const ControlsBW = () => ( export const ControlsBW = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="54" height="16" viewBox="0 0 54 14"> <svg xmlns="http://www.w3.org/2000/svg" width="54" height="16" viewBox="0 0 54 14">
@ -18,4 +18,4 @@ export const ControlsBW = () => (
<circle cx="46" cy="6" r="6" /> <circle cx="46" cy="6" r="6" />
</g> </g>
</svg> </svg>
) );

File diff suppressed because one or more lines are too long

@ -1,7 +1,12 @@
import React from 'react' import React from 'react';
export default () => ( export default () => (
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 18 18"> <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 18 18">
<path fill="#fff" fillRule="evenodd" d="M206.532032,366.702224 L208.523318,368.142728 C208.69279,368.3122 208.721035,368.509915 208.608053,368.735877 L206.786238,371.74399 C206.673257,371.969953 206.503788,372.040565 206.277825,371.955829 L203.989964,371.066106 C203.283831,371.546276 202.775423,371.842848 202.464724,371.955829 L202.125782,374.286058 C202.069291,374.51202 201.913944,374.625 201.659736,374.625 L198.058474,374.625 C197.804266,374.625 197.648919,374.51202 197.592428,374.286058 L197.253486,371.955829 C196.829806,371.786357 196.321398,371.489786 195.728246,371.066106 L193.440385,371.955829 C193.214422,372.068811 193.044953,371.998198 192.931972,371.74399 L191.110157,368.735877 C190.96893,368.481669 190.997175,368.283955 191.194892,368.142728 L193.101443,366.702224 C193.101443,366.617488 193.094382,366.476263 193.080259,366.278546 C193.066136,366.080828 193.059075,365.925481 193.059075,365.8125 C193.059075,365.699519 193.066136,365.544172 193.080259,365.346454 C193.094382,365.148737 193.101443,365.007512 193.101443,364.922776 L191.152525,363.482272 C190.983053,363.3128 190.954808,363.115085 191.067789,362.889123 L192.889604,359.88101 C193.002585,359.655047 193.172055,359.584435 193.398017,359.669171 L195.685878,360.558894 C196.392011,360.078724 196.90042,359.782152 197.211118,359.669171 L197.550061,357.338942 C197.606551,357.11298 197.761898,357 198.016106,357 L201.617368,357 C201.871576,357 202.026923,357.11298 202.083414,357.338942 L202.379988,359.669171 C202.803668,359.838643 203.312077,360.135214 203.905229,360.558894 L206.150722,359.669171 C206.376684,359.556189 206.560276,359.626802 206.701503,359.88101 L208.523318,362.889123 C208.664544,363.143331 208.6363,363.341045 208.438582,363.482272 L206.532032,364.922776 C206.532032,365.007512 206.539093,365.148737 206.553216,365.346454 C206.567338,365.544172 206.5744,365.699519 206.5744,365.8125 C206.5744,366.23618 206.560277,366.532752 206.532032,366.702224 Z M199.795553,368.905349 C200.671159,368.905349 201.419649,368.608777 202.041046,368.015625 C202.662443,367.422473 202.973138,366.688105 202.973138,365.8125 C202.973138,364.936895 202.662443,364.202527 202.041046,363.609375 C201.419649,363.016223 200.671159,362.719651 199.795553,362.719651 C198.919948,362.719651 198.178519,363.016223 197.571244,363.609375 C196.96397,364.202527 196.660337,364.936895 196.660337,365.8125 C196.660337,366.688105 196.96397,367.422473 197.571244,368.015625 C198.178519,368.608777 198.919948,368.905349 199.795553,368.905349 Z" transform="translate(-191 -357)"/> <path
fill="#fff"
fillRule="evenodd"
d="M206.532032,366.702224 L208.523318,368.142728 C208.69279,368.3122 208.721035,368.509915 208.608053,368.735877 L206.786238,371.74399 C206.673257,371.969953 206.503788,372.040565 206.277825,371.955829 L203.989964,371.066106 C203.283831,371.546276 202.775423,371.842848 202.464724,371.955829 L202.125782,374.286058 C202.069291,374.51202 201.913944,374.625 201.659736,374.625 L198.058474,374.625 C197.804266,374.625 197.648919,374.51202 197.592428,374.286058 L197.253486,371.955829 C196.829806,371.786357 196.321398,371.489786 195.728246,371.066106 L193.440385,371.955829 C193.214422,372.068811 193.044953,371.998198 192.931972,371.74399 L191.110157,368.735877 C190.96893,368.481669 190.997175,368.283955 191.194892,368.142728 L193.101443,366.702224 C193.101443,366.617488 193.094382,366.476263 193.080259,366.278546 C193.066136,366.080828 193.059075,365.925481 193.059075,365.8125 C193.059075,365.699519 193.066136,365.544172 193.080259,365.346454 C193.094382,365.148737 193.101443,365.007512 193.101443,364.922776 L191.152525,363.482272 C190.983053,363.3128 190.954808,363.115085 191.067789,362.889123 L192.889604,359.88101 C193.002585,359.655047 193.172055,359.584435 193.398017,359.669171 L195.685878,360.558894 C196.392011,360.078724 196.90042,359.782152 197.211118,359.669171 L197.550061,357.338942 C197.606551,357.11298 197.761898,357 198.016106,357 L201.617368,357 C201.871576,357 202.026923,357.11298 202.083414,357.338942 L202.379988,359.669171 C202.803668,359.838643 203.312077,360.135214 203.905229,360.558894 L206.150722,359.669171 C206.376684,359.556189 206.560276,359.626802 206.701503,359.88101 L208.523318,362.889123 C208.664544,363.143331 208.6363,363.341045 208.438582,363.482272 L206.532032,364.922776 C206.532032,365.007512 206.539093,365.148737 206.553216,365.346454 C206.567338,365.544172 206.5744,365.699519 206.5744,365.8125 C206.5744,366.23618 206.560277,366.532752 206.532032,366.702224 Z M199.795553,368.905349 C200.671159,368.905349 201.419649,368.608777 202.041046,368.015625 C202.662443,367.422473 202.973138,366.688105 202.973138,365.8125 C202.973138,364.936895 202.662443,364.202527 202.041046,363.609375 C201.419649,363.016223 200.671159,362.719651 199.795553,362.719651 C198.919948,362.719651 198.178519,363.016223 197.571244,363.609375 C196.96397,364.202527 196.660337,364.936895 196.660337,365.8125 C196.660337,366.688105 196.96397,367.422473 197.571244,368.015625 C198.178519,368.608777 198.919948,368.905349 199.795553,368.905349 Z"
transform="translate(-191 -357)"
/>
</svg> </svg>
) );

@ -1,7 +1,13 @@
import React from 'react' import React from 'react';
export const Sharp = () => ( export const Sharp = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 81 81" xmlnsXlink="http://www.w3.org/1999/xlink"> <svg
xmlns="http://www.w3.org/2000/svg"
width="50"
height="50"
viewBox="0 0 81 81"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
<defs> <defs>
<rect id="a" width="81" height="81" rx="3" /> <rect id="a" width="81" height="81" rx="3" />
</defs> </defs>
@ -11,18 +17,46 @@ export const Sharp = () => (
</mask> </mask>
<use fill="#616161" xlinkHref="#a" /> <use fill="#616161" xlinkHref="#a" />
<g transform="translate(16 32)" mask="url(#b)"> <g transform="translate(16 32)" mask="url(#b)">
<path fill="#000000" fillRule="nonzero" d="M66.0458013,46.1092762 C66.0458013,48.3193105 64.2622787,50.1077029 62.050805,50.1077029 L0.174089069,50.1077029 L0.174089069,6.16868499 C0.174089069,0.174657534 0.174089069,0.174657534 0.174089069,0.174657534 L66.0458013,0.174657534 L66.0458013,46.1092762 Z"/> <path
fill="#000000"
fillRule="nonzero"
d="M66.0458013,46.1092762 C66.0458013,48.3193105 64.2622787,50.1077029 62.050805,50.1077029 L0.174089069,50.1077029 L0.174089069,6.16868499 C0.174089069,0.174657534 0.174089069,0.174657534 0.174089069,0.174657534 L66.0458013,0.174657534 L66.0458013,46.1092762 Z"
/>
<g transform="translate(19.96 15.27)"> <g transform="translate(19.96 15.27)">
<ellipse cx="7.045" cy="7.048" fill="#FF5E55" fillRule="nonzero" stroke="#E0443E" strokeWidth=".5" rx="7.045" ry="7.048"/> <ellipse
<ellipse cx="30.526" cy="7.048" fill="#FFC02C" fillRule="nonzero" stroke="#DEA123" strokeWidth=".5" rx="7.045" ry="7.048"/> cx="7.045"
cy="7.048"
fill="#FF5E55"
fillRule="nonzero"
stroke="#E0443E"
strokeWidth=".5"
rx="7.045"
ry="7.048"
/>
<ellipse
cx="30.526"
cy="7.048"
fill="#FFC02C"
fillRule="nonzero"
stroke="#DEA123"
strokeWidth=".5"
rx="7.045"
ry="7.048"
/>
</g> </g>
</g> </g>
</g> </g>
</svg> </svg>
) );
export const BW = () => ( export const BW = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 81 81" xmlnsXlink="http://www.w3.org/1999/xlink"> <svg
xmlns="http://www.w3.org/2000/svg"
width="50"
height="50"
viewBox="0 0 81 81"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
<defs> <defs>
<rect id="a" width="81" height="81" rx="3" /> <rect id="a" width="81" height="81" rx="3" />
</defs> </defs>
@ -32,7 +66,12 @@ export const BW = () => (
</mask> </mask>
<use fill="#616161" xlinkHref="#a" /> <use fill="#616161" xlinkHref="#a" />
<g transform="translate(17 33)" mask="url(#b)"> <g transform="translate(17 33)" mask="url(#b)">
<path fill="#000000" stroke="#FFFFFF" strokeWidth="2" d="M65.0458013,49.1077029 C66.0458013,49.1077029 0.174089069,49.1077029 0.174089069,49.1077029 L0.174089069,5.16868499 C0.174089069,2.41055979 2.40986586,0.174657534 5.17268563,0.174657534 L65.0458013,0.174657534 L65.0458013,49.1077029 Z"/> <path
fill="#000000"
stroke="#FFFFFF"
strokeWidth="2"
d="M65.0458013,49.1077029 C66.0458013,49.1077029 0.174089069,49.1077029 0.174089069,49.1077029 L0.174089069,5.16868499 C0.174089069,2.41055979 2.40986586,0.174657534 5.17268563,0.174657534 L65.0458013,0.174657534 L65.0458013,49.1077029 Z"
/>
<g fillRule="nonzero" stroke="#878787" transform="translate(18.96 14.27)"> <g fillRule="nonzero" stroke="#878787" transform="translate(18.96 14.27)">
<ellipse cx="7.045" cy="7.048" rx="7.045" ry="7.048" /> <ellipse cx="7.045" cy="7.048" rx="7.045" ry="7.048" />
<ellipse cx="30.526" cy="7.048" rx="7.045" ry="7.048" /> <ellipse cx="30.526" cy="7.048" rx="7.045" ry="7.048" />
@ -40,10 +79,16 @@ export const BW = () => (
</g> </g>
</g> </g>
</svg> </svg>
) );
export const None = () => ( export const None = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 81 81" xmlnsXlink="http://www.w3.org/1999/xlink"> <svg
xmlns="http://www.w3.org/2000/svg"
width="50"
height="50"
viewBox="0 0 81 81"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
<defs> <defs>
<rect id="a" width="81" height="81" rx="3" /> <rect id="a" width="81" height="81" rx="3" />
</defs> </defs>
@ -53,7 +98,11 @@ export const None = () => (
</mask> </mask>
<use fill="#616161" xlinkHref="#a" /> <use fill="#616161" xlinkHref="#a" />
<g transform="translate(16 32)" mask="url(#b)"> <g transform="translate(16 32)" mask="url(#b)">
<path fill="#000000" fillRule="nonzero" d="M66.0458013,46.1092762 C66.0458013,48.3193105 64.2622787,50.1077029 62.050805,50.1077029 L0.174089069,50.1077029 L0.174089069,6.16868499 C0.174089069,2.85738806 2.85846845,0.174657534 6.17268563,0.174657534 L66.0458013,0.174657534 L66.0458013,46.1092762 Z"/> <path
fill="#000000"
fillRule="nonzero"
d="M66.0458013,46.1092762 C66.0458013,48.3193105 64.2622787,50.1077029 62.050805,50.1077029 L0.174089069,50.1077029 L0.174089069,6.16868499 C0.174089069,2.85738806 2.85846845,0.174657534 6.17268563,0.174657534 L66.0458013,0.174657534 L66.0458013,46.1092762 Z"
/>
<g fillRule="nonzero" strokeWidth=".5" transform="translate(19.96 15.27)"> <g fillRule="nonzero" strokeWidth=".5" transform="translate(19.96 15.27)">
<ellipse cx="7.045" cy="7.048" fill="#FF5E55" stroke="#E0443E" rx="7.045" ry="7.048" /> <ellipse cx="7.045" cy="7.048" fill="#FF5E55" stroke="#E0443E" rx="7.045" ry="7.048" />
<ellipse cx="30.526" cy="7.048" fill="#FFC02C" stroke="#DEA123" rx="7.045" ry="7.048" /> <ellipse cx="30.526" cy="7.048" fill="#FFC02C" stroke="#DEA123" rx="7.045" ry="7.048" />
@ -61,4 +110,4 @@ export const None = () => (
</g> </g>
</g> </g>
</svg> </svg>
) );

@ -1,46 +1,46 @@
const Twitter = require('twitter') const Twitter = require('twitter');
const morph = require('morphmorph') const morph = require('morphmorph');
const RATE_LIMIT_CODE = 420 const RATE_LIMIT_CODE = 420;
const client = new Twitter({ const client = new Twitter({
consumer_key: process.env.TWITTER_CONSUMER_KEY, consumer_key: process.env.TWITTER_CONSUMER_KEY,
consumer_secret: process.env.TWITTER_CONSUMER_SECRET, consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
access_token_key: process.env.TWITTER_ACCESS_TOKEN_KEY, access_token_key: process.env.TWITTER_ACCESS_TOKEN_KEY,
access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET
}) });
const uploadImage = data => client.post('media/upload', { media_data: data }) const uploadImage = data => client.post('media/upload', { media_data: data });
const uploadTweet = (media = {}) => const uploadTweet = (media = {}) =>
client.post('statuses/update', { client.post('statuses/update', {
status: `Carbon Copy #${media.media_id_string.slice(0, 8)}`, status: `Carbon Copy #${media.media_id_string.slice(0, 8)}`,
media_ids: media.media_id_string media_ids: media.media_id_string
}) });
const extractImageUrl = morph.get('entities.media.0.display_url') const extractImageUrl = morph.get('entities.media.0.display_url');
const extractErrorCode = morph.get('0.code') const extractErrorCode = morph.get('0.code');
const respondSuccess = (res, url) => res.json({ url }) const respondSuccess = (res, url) => res.json({ url });
const respondFail = (res, err) => { const respondFail = (res, err) => {
const errorCode = extractErrorCode(err) const errorCode = extractErrorCode(err);
// check for rate limit // check for rate limit
if (errorCode === RATE_LIMIT_CODE) { if (errorCode === RATE_LIMIT_CODE) {
return res.status(420).send() return res.status(420).send();
} }
console.error(`Error: ${err.message || JSON.stringify(err, null, 2)}`) console.error(`Error: ${err.message || JSON.stringify(err, null, 2)}`);
res.status(500).send() res.status(500).send();
} };
module.exports = (req, res) => { module.exports = (req, res) => {
if (!req.body.data) { if (!req.body.data) {
return res.status(400).send() return res.status(400).send();
} }
uploadImage(req.body.data) uploadImage(req.body.data)
.then(uploadTweet) .then(uploadTweet)
.then(extractImageUrl) .then(extractImageUrl)
.then(respondSuccess.bind(null, res)) .then(respondSuccess.bind(null, res))
.catch(respondFail.bind(null, res)) .catch(respondFail.bind(null, res));
} };

@ -1,9 +1,9 @@
import axios from 'axios' import axios from 'axios';
import debounce from 'lodash.debounce' import debounce from 'lodash.debounce';
import ms from 'ms' import ms from 'ms';
const DOMAIN = process.browser ? document.location.origin : '' const DOMAIN = process.browser ? document.location.origin : '';
const RATE_LIMIT_CODE = 420 const RATE_LIMIT_CODE = 420;
const gistClient = axios.create({ const gistClient = axios.create({
baseURL: 'https://api.github.com', baseURL: 'https://api.github.com',
@ -15,46 +15,50 @@ const gistClient = axios.create({
}); });
async function tweet(encodedImage) { async function tweet(encodedImage) {
const processedData = encodedImage.split(',')[1] const processedData = encodedImage.split(',')[1];
return axios.post(`${DOMAIN}/twitter`, { data: processedData }) return axios
.post(`${DOMAIN}/twitter`, { data: processedData })
.then(res => res.data.url) .then(res => res.data.url)
.then(url => encodeURIComponent(`Built with #Carbon, by @dawn_labs ${url}`)) .then(url => encodeURIComponent(`Built with #Carbon, by @dawn_labs ${url}`))
.then(uri => `https://twitter.com/intent/tweet?text=${uri}`) .then(uri => `https://twitter.com/intent/tweet?text=${uri}`)
.then(openTwitterUrl) .then(openTwitterUrl)
.catch(checkIfRateLimited) .catch(checkIfRateLimited);
} }
const getGist = (id) => { const getGist = id => {
const uid = id.split('/').pop() const uid = id.split('/').pop();
return gistClient.get(`/gists/${uid}`) return gistClient
.get(`/gists/${uid}`)
.then(res => res.data) .then(res => res.data)
.then(gist => gist.files) .then(gist => gist.files)
.then(files => files[Object.keys(files)[0]]) .then(files => files[Object.keys(files)[0]])
.then(file => file.content) .then(file => file.content);
} };
// private // private
function openTwitterUrl(twitterUrl) { function openTwitterUrl(twitterUrl) {
const width = 575, height = 400 const width = 575,
const left = (window.outerWidth - width) / 2 height = 400;
const right = (window.outerHeight - height) / 2 const left = (window.outerWidth - width) / 2;
const opts = `status=1,width=${width},height=${height},top=${top},left=${left}` const right = (window.outerHeight - height) / 2;
const opts = `status=1,width=${width},height=${height},top=${top},left=${left}`;
window.open(twitterUrl, 'twitter', opts) window.open(twitterUrl, 'twitter', opts);
} }
function checkIfRateLimited(err) { function checkIfRateLimited(err) {
if (err.response.status === RATE_LIMIT_CODE) { if (err.response.status === RATE_LIMIT_CODE) {
alert('Oh no! Looks like to many people are trying to tweet right now and we\'ve been rate limited. Try again soon or save and upload manually!') alert(
return "Oh no! Looks like to many people are trying to tweet right now and we've been rate limited. Try again soon or save and upload manually!"
);
return;
} }
throw err throw err;
} }
export default { export default {
getGist, getGist,
tweet: debounce(tweet, ms('5s'), { leading: true, trailing: true }) tweet: debounce(tweet, ms('5s'), { leading: true, trailing: true })
} };

@ -1,4 +1,4 @@
import toHash from 'tohash' import toHash from 'tohash';
export const THEMES_ARRAY = [ export const THEMES_ARRAY = [
{ {
@ -67,9 +67,9 @@ export const THEMES_ARRAY = [
id: 'zenburn', id: 'zenburn',
name: 'Zenburn' name: 'Zenburn'
} }
] ];
export const THEMES = toHash(THEMES_ARRAY) export const THEMES = toHash(THEMES_ARRAY);
export const LANGUAGES = [ export const LANGUAGES = [
{ {
@ -277,16 +277,16 @@ export const LANGUAGES = [
name: 'YAML', name: 'YAML',
module: 'yaml' module: 'yaml'
} }
] ];
export const DEFAULT_LANGUAGE = 'auto' export const DEFAULT_LANGUAGE = 'auto';
export const COLORS = { export const COLORS = {
BLACK: '#121212', BLACK: '#121212',
PRIMARY: '#F8E81C', PRIMARY: '#F8E81C',
SECONDARY: '#fff', SECONDARY: '#fff',
GRAY: '#858585' GRAY: '#858585'
} };
export const DEFAULT_CODE = `const pluckDeep = key => obj => key.split('.').reduce((accum, key) => accum[key], obj) export const DEFAULT_CODE = `const pluckDeep = key => obj => key.split('.').reduce((accum, key) => accum[key], obj)
@ -298,16 +298,14 @@ const unfold = (f, seed) => {
return res ? go(f, res[1], acc.concat([res[0]])) : acc return res ? go(f, res[1], acc.concat([res[0]])) : acc
} }
return go(f, seed, []) return go(f, seed, [])
}` }`;
if (typeof window !== 'undefined' && typeof window.navigator !== 'undefined') { if (typeof window !== 'undefined' && typeof window.navigator !== 'undefined') {
LANGUAGES LANGUAGES.filter(language => language.module !== 'auto').forEach(language => {
.filter(language => language.module !== 'auto')
.forEach((language) => {
if (language.module) { if (language.module) {
!language.custom !language.custom
? require(`codemirror/mode/${language.module}/${language.module}`) ? require(`codemirror/mode/${language.module}/${language.module}`)
: require(`./customModes/${language.module}`) : require(`./customModes/${language.module}`);
} }
}) });
} }

@ -1,10 +1,9 @@
const CodeMirror = require('codemirror') const CodeMirror = require('codemirror');
CodeMirror.defineMode('kotlin', function(config, parserConfig) {
CodeMirror.defineMode("kotlin", function(config, parserConfig) {
function words(str) { function words(str) {
var obj = {}, var obj = {},
words = str.split(" "); words = str.split(' ');
for (var i = 0; i < words.length; ++i) obj[words[i]] = true; for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
return obj; return obj;
} }
@ -12,15 +11,17 @@ CodeMirror.defineMode("kotlin", function(config, parserConfig) {
var multiLineStrings = parserConfig.multiLineStrings; var multiLineStrings = parserConfig.multiLineStrings;
var keywords = words( var keywords = words(
"package continue return object while break class data trait throw super " + 'package continue return object while break class data trait throw super ' +
"when type this else This try val var fun for is in if do as true false null get set " + 'when type this else This try val var fun for is in if do as true false null get set ' +
"import where by get set abstract enum open annotation override private public internal " + 'import where by get set abstract enum open annotation override private public internal ' +
"protected catch out vararg inline finally final ref const"); 'protected catch out vararg inline finally final ref const'
var blockKeywords = words("catch class do else finally for if where try while enum"); );
var atoms = words("null true false this"); var blockKeywords = words('catch class do else finally for if where try while enum');
var atoms = words('null true false this');
var builtins = words( var builtins = words(
"Int Double Float Long Short Byte IntArray ShortArray ByteArray String Boolean List Set " + 'Int Double Float Long Short Byte IntArray ShortArray ByteArray String Boolean List Set ' +
"Map MutableList MutableSet print println shl shr ushr and or xor inv "); 'Map MutableList MutableSet print println shl shr ushr and or xor inv '
);
var curPunc; var curPunc;
@ -32,8 +33,8 @@ CodeMirror.defineMode("kotlin", function(config, parserConfig) {
} }
// Wildcard import w/o trailing semicolon (import smth.*) // Wildcard import w/o trailing semicolon (import smth.*)
if (ch == "." && stream.eat("*")) { if (ch == '.' && stream.eat('*')) {
return "word"; return 'word';
} }
if (/[\[\]{}\(\),;\:\.]/.test(ch)) { if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
@ -47,18 +48,18 @@ CodeMirror.defineMode("kotlin", function(config, parserConfig) {
stream.eatWhile(/\d/); stream.eatWhile(/\d/);
} }
return "number"; return 'number';
} }
if (ch == "/") { if (ch == '/') {
if (stream.eat("*")) { if (stream.eat('*')) {
state.tokenize.push(tokenComment); state.tokenize.push(tokenComment);
return tokenComment(stream, state); return tokenComment(stream, state);
} }
if (stream.eat("/")) { if (stream.eat('/')) {
stream.skipToEnd(); stream.skipToEnd();
return "comment"; return 'comment';
} }
if (expectExpression(state.lastToken)) { if (expectExpression(state.lastToken)) {
@ -67,14 +68,14 @@ CodeMirror.defineMode("kotlin", function(config, parserConfig) {
} }
// Commented // Commented
if (ch == "-" && stream.eat(">")) { if (ch == '-' && stream.eat('>')) {
curPunc = "->"; curPunc = '->';
return null; return null;
} }
if (/[\-+*&%=<>!?|\/~]/.test(ch)) { if (/[\-+*&%=<>!?|\/~]/.test(ch)) {
stream.eatWhile(/[\-+*&%=<>|~]/); stream.eatWhile(/[\-+*&%=<>|~]/);
return "operator"; return 'operator';
} }
stream.eatWhile(/[\w\$_]/); stream.eatWhile(/[\w\$_]/);
@ -82,33 +83,34 @@ CodeMirror.defineMode("kotlin", function(config, parserConfig) {
var cur = stream.current(); var cur = stream.current();
if (atoms.propertyIsEnumerable(cur)) { if (atoms.propertyIsEnumerable(cur)) {
return "atom"; return 'atom';
} }
if (keywords.propertyIsEnumerable(cur)) { if (keywords.propertyIsEnumerable(cur)) {
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; if (blockKeywords.propertyIsEnumerable(cur)) curPunc = 'newstatement';
return "keyword"; return 'keyword';
} }
if (builtins.propertyIsEnumerable(cur)) { if (builtins.propertyIsEnumerable(cur)) {
return "builtin"; return 'builtin';
} }
return "word"; return 'word';
} }
tokenBase.isBase = true; tokenBase.isBase = true;
function startString(quote, stream, state) { function startString(quote, stream, state) {
var tripleQuoted = false; var tripleQuoted = false;
if (quote != "/" && stream.eat(quote)) { if (quote != '/' && stream.eat(quote)) {
if (stream.eat(quote)) tripleQuoted = true; if (stream.eat(quote)) tripleQuoted = true;
else return "string"; else return 'string';
} }
function t(stream, state) { function t(stream, state) {
var escaped = false, var escaped = false,
next, end = !tripleQuoted; next,
end = !tripleQuoted;
while ((next = stream.next()) != null) { while ((next = stream.next()) != null) {
if (next == quote && !escaped) { if (next == quote && !escaped) {
@ -122,24 +124,23 @@ CodeMirror.defineMode("kotlin", function(config, parserConfig) {
} }
} }
if (quote == '"' && next == "$" && !escaped && stream.eat("{")) { if (quote == '"' && next == '$' && !escaped && stream.eat('{')) {
state.tokenize.push(tokenBaseUntilBrace()); state.tokenize.push(tokenBaseUntilBrace());
return "string"; return 'string';
} }
if (next == "$" && !escaped && !stream.eat(" ")) { if (next == '$' && !escaped && !stream.eat(' ')) {
state.tokenize.push(tokenBaseUntilSpace()); state.tokenize.push(tokenBaseUntilSpace());
return "string"; return 'string';
} }
escaped = !escaped && next == "\\"; escaped = !escaped && next == '\\';
} }
if (multiLineStrings) if (multiLineStrings) state.tokenize.push(t);
state.tokenize.push(t);
if (end) state.tokenize.pop(); if (end) state.tokenize.pop();
return "string"; return 'string';
} }
state.tokenize.push(t); state.tokenize.push(t);
@ -150,13 +151,13 @@ CodeMirror.defineMode("kotlin", function(config, parserConfig) {
var depth = 1; var depth = 1;
function t(stream, state) { function t(stream, state) {
if (stream.peek() == "}") { if (stream.peek() == '}') {
depth--; depth--;
if (depth == 0) { if (depth == 0) {
state.tokenize.pop(); state.tokenize.pop();
return state.tokenize[state.tokenize.length - 1](stream, state); return state.tokenize[state.tokenize.length - 1](stream, state);
} }
} else if (stream.peek() == "{") { } else if (stream.peek() == '{') {
depth++; depth++;
} }
@ -173,11 +174,11 @@ CodeMirror.defineMode("kotlin", function(config, parserConfig) {
var isWord = stream.eatWhile(/[\w]/); var isWord = stream.eatWhile(/[\w]/);
if (isWord) { if (isWord) {
state.tokenize.pop(); state.tokenize.pop();
return "word"; return 'word';
} }
} }
state.tokenize.pop(); state.tokenize.pop();
return "string"; return 'string';
} }
t.isBase = true; t.isBase = true;
@ -187,21 +188,28 @@ CodeMirror.defineMode("kotlin", function(config, parserConfig) {
function tokenComment(stream, state) { function tokenComment(stream, state) {
var maybeEnd = false, var maybeEnd = false,
ch; ch;
while (ch = stream.next()) { while ((ch = stream.next())) {
if (ch == "/" && maybeEnd) { if (ch == '/' && maybeEnd) {
state.tokenize.pop(); state.tokenize.pop();
break; break;
} }
maybeEnd = (ch == "*"); maybeEnd = ch == '*';
} }
return "comment"; return 'comment';
} }
function expectExpression(last) { function expectExpression(last) {
return !last || last == "operator" || last == "->" || /[\.\[\{\(,;:]/.test(last) || return (
last == "newstatement" || last == "keyword" || last == "proplabel"; !last ||
last == 'operator' ||
last == '->' ||
/[\.\[\{\(,;:]/.test(last) ||
last == 'newstatement' ||
last == 'keyword' ||
last == 'proplabel'
);
} }
function Context(indented, column, type, align, prev) { function Context(indented, column, type, align, prev) {
@ -213,14 +221,13 @@ CodeMirror.defineMode("kotlin", function(config, parserConfig) {
} }
function pushContext(state, col, type) { function pushContext(state, col, type) {
return state.context = new Context(state.indented, col, type, null, state.context); return (state.context = new Context(state.indented, col, type, null, state.context));
} }
function popContext(state) { function popContext(state) {
var t = state.context.type; var t = state.context.type;
if (t == ")" || t == "]" || t == "}") if (t == ')' || t == ']' || t == '}') state.indented = state.context.indented;
state.indented = state.context.indented; return (state.context = state.context.prev);
return state.context = state.context.prev;
} }
// Interface // Interface
@ -229,7 +236,7 @@ CodeMirror.defineMode("kotlin", function(config, parserConfig) {
startState: function(basecolumn) { startState: function(basecolumn) {
return { return {
tokenize: [tokenBase], tokenize: [tokenBase],
context: new Context((basecolumn || 0) - config.indentUnit, 0, "top", false), context: new Context((basecolumn || 0) - config.indentUnit, 0, 'top', false),
indented: 0, indented: 0,
startOfLine: true, startOfLine: true,
lastToken: null lastToken: null
@ -243,7 +250,7 @@ CodeMirror.defineMode("kotlin", function(config, parserConfig) {
state.indented = stream.indentation(); state.indented = stream.indentation();
state.startOfLine = true; state.startOfLine = true;
// Automatic semicolon insertion // Automatic semicolon insertion
if (ctx.type == "statement" && !expectExpression(state.lastToken)) { if (ctx.type == 'statement' && !expectExpression(state.lastToken)) {
popContext(state); popContext(state);
ctx = state.context; ctx = state.context;
} }
@ -251,23 +258,27 @@ CodeMirror.defineMode("kotlin", function(config, parserConfig) {
if (stream.eatSpace()) return null; if (stream.eatSpace()) return null;
curPunc = null; curPunc = null;
var style = state.tokenize[state.tokenize.length - 1](stream, state); var style = state.tokenize[state.tokenize.length - 1](stream, state);
if (style == "comment") return style; if (style == 'comment') return style;
if (ctx.align == null) ctx.align = true; if (ctx.align == null) ctx.align = true;
if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state); if ((curPunc == ';' || curPunc == ':') && ctx.type == 'statement') popContext(state);
else if (curPunc == '->' && ctx.type == 'statement' && ctx.prev.type == '}') {
// Handle indentation for {x -> \n ... } // Handle indentation for {x -> \n ... }
else if (curPunc == "->" && ctx.type == "statement" && ctx.prev.type == "}") {
popContext(state); popContext(state);
state.context.align = false; state.context.align = false;
} else if (curPunc == "{") pushContext(state, stream.column(), "}"); } else if (curPunc == '{') pushContext(state, stream.column(), '}');
else if (curPunc == "[") pushContext(state, stream.column(), "]"); else if (curPunc == '[') pushContext(state, stream.column(), ']');
else if (curPunc == "(") pushContext(state, stream.column(), ")"); else if (curPunc == '(') pushContext(state, stream.column(), ')');
else if (curPunc == "}") { else if (curPunc == '}') {
while (ctx.type == "statement") ctx = popContext(state); while (ctx.type == 'statement') ctx = popContext(state);
if (ctx.type == "}") ctx = popContext(state); if (ctx.type == '}') ctx = popContext(state);
while (ctx.type == "statement") ctx = popContext(state); while (ctx.type == 'statement') ctx = popContext(state);
} else if (curPunc == ctx.type) popContext(state); } else if (curPunc == ctx.type) popContext(state);
else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement")) else if (
pushContext(state, stream.column(), "statement"); ctx.type == '}' ||
ctx.type == 'top' ||
(ctx.type == 'statement' && curPunc == 'newstatement')
)
pushContext(state, stream.column(), 'statement');
state.startOfLine = false; state.startOfLine = false;
state.lastToken = curPunc || style; state.lastToken = curPunc || style;
return style; return style;
@ -277,19 +288,19 @@ CodeMirror.defineMode("kotlin", function(config, parserConfig) {
if (!state.tokenize[state.tokenize.length - 1].isBase) return 0; if (!state.tokenize[state.tokenize.length - 1].isBase) return 0;
var firstChar = textAfter && textAfter.charAt(0), var firstChar = textAfter && textAfter.charAt(0),
ctx = state.context; ctx = state.context;
if (ctx.type == "statement" && !expectExpression(state.lastToken)) ctx = ctx.prev; if (ctx.type == 'statement' && !expectExpression(state.lastToken)) ctx = ctx.prev;
var closing = firstChar == ctx.type; var closing = firstChar == ctx.type;
if (ctx.type == "statement") { if (ctx.type == 'statement') {
return ctx.indented + (firstChar == "{" ? 0 : config.indentUnit); return ctx.indented + (firstChar == '{' ? 0 : config.indentUnit);
} else if (ctx.align) return ctx.column + (closing ? 0 : 1); } else if (ctx.align) return ctx.column + (closing ? 0 : 1);
else return ctx.indented + (closing ? 0 : config.indentUnit); else return ctx.indented + (closing ? 0 : config.indentUnit);
}, },
closeBrackets: { closeBrackets: {
triples: "'\"" triples: '\'"'
}, },
electricChars: "{}" electricChars: '{}'
}; };
}); });
CodeMirror.defineMIME("text/x-kotlin", "kotlin"); CodeMirror.defineMIME('text/x-kotlin', 'kotlin');

@ -1,77 +1,300 @@
const CodeMirror = require('codemirror') const CodeMirror = require('codemirror');
CodeMirror.defineMode('nimrod', function(conf, parserConf) {
CodeMirror.defineMode("nimrod", function(conf, parserConf) {
var ERRORCLASS = 'error'; var ERRORCLASS = 'error';
function wordRegexp(words) { function wordRegexp(words) {
return new RegExp("^((" + words.join(")|(") + "))\\b"); return new RegExp('^((' + words.join(')|(') + '))\\b');
} }
var operators = new RegExp("\\=\\+\\-\\*\\/\\<\\>\\@\\$\\~\\&\\%\\|\\!\\?\\^\\:\\\\"); var operators = new RegExp('\\=\\+\\-\\*\\/\\<\\>\\@\\$\\~\\&\\%\\|\\!\\?\\^\\:\\\\');
var identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*"); var identifiers = new RegExp('^[_A-Za-z][_A-Za-z0-9]*');
var commonkeywords = ['addr', 'asm', 'atomic', var commonkeywords = [
'bind', 'block', 'break', 'case', 'cast', 'addr',
'const', 'continue', 'converter', 'asm',
'discard', 'distinct', 'do', 'atomic',
'elif', 'else', 'end', 'enum', 'except', 'export', 'bind',
'finally', 'for', 'from', 'block',
'break',
'case',
'cast',
'const',
'continue',
'converter',
'discard',
'distinct',
'do',
'elif',
'else',
'end',
'enum',
'except',
'export',
'finally',
'for',
'from',
'generic', 'generic',
'if', 'import', 'include', 'interface', 'iterator', 'if',
'lambda', 'let', 'import',
'macro', 'method', 'mixin', 'nil', 'include',
'object', 'out', 'interface',
'proc', 'ptr', 'iterator',
'raise', 'ref', 'return', 'lambda',
'shared', 'static', 'let',
'template', 'try', 'tuple', 'type', 'macro',
'method',
'mixin',
'nil',
'object',
'out',
'proc',
'ptr',
'raise',
'ref',
'return',
'shared',
'static',
'template',
'try',
'tuple',
'type',
'using', 'using',
'var', 'var',
'when', 'while', 'with', 'without', 'when',
'while',
'with',
'without',
'yield', 'yield',
// keyword operators // keyword operators
'shl', 'shr', 'and', 'or', 'xor', 'not', 'shl',
'div', 'mod', 'is', 'isnot', 'in', 'as', 'of']; 'shr',
'and',
var commonBuiltins = ["int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", 'or',
"uint32", "uint64", "float", "float32", "float64", "bool", "char", 'xor',
"string", "cstring", "pointer", "range", "array", "openarray", 'not',
"seq", "set", "Byte", "Natural", "Positive", "TObject", "PObject", 'div',
"Conversion", "TResult", "TAddress", "BiggestInt", "BiggestFloat", 'mod',
"cchar", "cschar", "cshort", "cint", "csize", "cuchar", "cushort", 'is',
"clong", "clonglong", "cfloat", "cdouble", "clongdouble", "cuint", 'isnot',
"culong", "culonglong", "cchar", "cstringArray", "TEndian", "PFloat32", 'in',
"PFloat64", "PInt64", "PInt32", "TGC_Strategy", "TFile", "TFileMode", 'as',
"TFileHandle", "isMainModule", "CompileDate", "CompileTime", "NimrodVersion", 'of'
"NimrodMajor", "NimrodMinor", "NimrodPatch", "cpuEndian", "hostOS", "hostCPU", ];
"inf", "neginf", "nan", "QuitSuccess", "QuitFailure", "dbgLineHook", "stdin",
"stdout", "stderr", "defined", "new", "high", "low", "sizeof", "succ", "pred", var commonBuiltins = [
"inc", "dec", "newSeq", "len", "incl", "excl", "card", "ord", "chr", "ze", 'int',
"ze64", "toU8", "toU16", "toU32", "abs", "min", "max", "add", "repr", "contains", 'int8',
"toFloat", "toBiggestFloat", "toInt", "toBiggestInt", "addQuitProc", "copy", 'int16',
"setLen", "newString", "zeroMem", "copyMem", "moveMem", "equalMem", "alloc", 'int32',
"alloc0", "realloc", "dealloc", "setLen", "assert", "swap", "getRefcount", 'int64',
"getCurrentException", "Msg", "getOccupiedMem", "getFreeMem", "getTotalMem", 'uint',
"isNil", "seqToPtr", "find", "pop", "GC_disable", "GC_enable", "GC_fullCollect", 'uint8',
"GC_setStrategy", "GC_enableMarkAnd", "Sweep", "GC_disableMarkAnd", "Sweep", 'uint16',
"GC_getStatistics", "GC_ref", "GC_ref", "GC_ref", "GC_unref", "GC_unref", 'uint32',
"GC_unref", "quit", "OpenFile", "OpenFile", "CloseFile", "EndOfFile", 'uint64',
"readChar", "FlushFile", "readFile", "write", "readLine", "writeln", 'float',
"writeln", "getFileSize", "ReadBytes", "ReadChars", "readBuffer", 'float32',
"writeBytes", "writeChars", "writeBuffer", "setFilePos", "getFilePos", 'float64',
"fileHandle", "countdown", "countup", "items", "lines", 'bool',
"true", "false", 'char',
'string',
'cstring',
'pointer',
'range',
'array',
'openarray',
'seq',
'set',
'Byte',
'Natural',
'Positive',
'TObject',
'PObject',
'Conversion',
'TResult',
'TAddress',
'BiggestInt',
'BiggestFloat',
'cchar',
'cschar',
'cshort',
'cint',
'csize',
'cuchar',
'cushort',
'clong',
'clonglong',
'cfloat',
'cdouble',
'clongdouble',
'cuint',
'culong',
'culonglong',
'cchar',
'cstringArray',
'TEndian',
'PFloat32',
'PFloat64',
'PInt64',
'PInt32',
'TGC_Strategy',
'TFile',
'TFileMode',
'TFileHandle',
'isMainModule',
'CompileDate',
'CompileTime',
'NimrodVersion',
'NimrodMajor',
'NimrodMinor',
'NimrodPatch',
'cpuEndian',
'hostOS',
'hostCPU',
'inf',
'neginf',
'nan',
'QuitSuccess',
'QuitFailure',
'dbgLineHook',
'stdin',
'stdout',
'stderr',
'defined',
'new',
'high',
'low',
'sizeof',
'succ',
'pred',
'inc',
'dec',
'newSeq',
'len',
'incl',
'excl',
'card',
'ord',
'chr',
'ze',
'ze64',
'toU8',
'toU16',
'toU32',
'abs',
'min',
'max',
'add',
'repr',
'contains',
'toFloat',
'toBiggestFloat',
'toInt',
'toBiggestInt',
'addQuitProc',
'copy',
'setLen',
'newString',
'zeroMem',
'copyMem',
'moveMem',
'equalMem',
'alloc',
'alloc0',
'realloc',
'dealloc',
'setLen',
'assert',
'swap',
'getRefcount',
'getCurrentException',
'Msg',
'getOccupiedMem',
'getFreeMem',
'getTotalMem',
'isNil',
'seqToPtr',
'find',
'pop',
'GC_disable',
'GC_enable',
'GC_fullCollect',
'GC_setStrategy',
'GC_enableMarkAnd',
'Sweep',
'GC_disableMarkAnd',
'Sweep',
'GC_getStatistics',
'GC_ref',
'GC_ref',
'GC_ref',
'GC_unref',
'GC_unref',
'GC_unref',
'quit',
'OpenFile',
'OpenFile',
'CloseFile',
'EndOfFile',
'readChar',
'FlushFile',
'readFile',
'write',
'readLine',
'writeln',
'writeln',
'getFileSize',
'ReadBytes',
'ReadChars',
'readBuffer',
'writeBytes',
'writeChars',
'writeBuffer',
'setFilePos',
'getFilePos',
'fileHandle',
'countdown',
'countup',
'items',
'lines',
'true',
'false',
// exceptions // exceptions
"E_Base", "EAsynch", "ESynch", "ESystem", "EIO", "EOS", "ERessourceExhausted", 'E_Base',
"EArithmetic", "EDivByZero", "EOverflow", "EAccessViolation", "EAssertionFailed", 'EAsynch',
"EControlC", "EInvalidValue", "EOutOfMemory", "EInvalidIndex", "EInvalidField", 'ESynch',
"EOutOfRange", "EStackOverflow", "ENoExceptionToReraise", "EInvalidObjectAssignment", 'ESystem',
"EInvalidObject", "EInvalidLibrary", "EInvalidKey", "EInvalidObjectConversion", 'EIO',
"EFloatingPoint", "EFloatInvalidOp", "EFloatDivByZero", "EFloatOverflow", 'EOS',
"EFloatInexact", "EDeadThrea"]; 'ERessourceExhausted',
'EArithmetic',
'EDivByZero',
'EOverflow',
'EAccessViolation',
'EAssertionFailed',
'EControlC',
'EInvalidValue',
'EOutOfMemory',
'EInvalidIndex',
'EInvalidField',
'EOutOfRange',
'EStackOverflow',
'ENoExceptionToReraise',
'EInvalidObjectAssignment',
'EInvalidObject',
'EInvalidLibrary',
'EInvalidKey',
'EInvalidObjectConversion',
'EFloatingPoint',
'EFloatInvalidOp',
'EFloatDivByZero',
'EFloatOverflow',
'EFloatInexact',
'EDeadThrea'
];
if (parserConf.extra_keywords != undefined) if (parserConf.extra_keywords != undefined)
commonkeywords = commonkeywords.concat(parserConf.extra_keywords); commonkeywords = commonkeywords.concat(parserConf.extra_keywords);
@ -84,7 +307,7 @@ CodeMirror.defineMode("nimrod", function(conf, parserConf) {
var indentInfo = null; var indentInfo = null;
var stringPrefixes = new RegExp("^(('{3}|\"{3}|['\"]))", "i"); var stringPrefixes = new RegExp('^((\'{3}|"{3}|[\'"]))', 'i');
// tokenizers // tokenizers
function tokenBase(stream, state) { function tokenBase(stream, state) {
@ -106,8 +329,7 @@ CodeMirror.defineMode("nimrod", function(conf, parserConf) {
} }
} }
if (stream.eatSpace()) if (stream.eatSpace()) return null;
return null;
var ch = stream.peek(); var ch = stream.peek();
@ -121,9 +343,15 @@ CodeMirror.defineMode("nimrod", function(conf, parserConf) {
if (stream.match(/^[0-9\.]/, false)) { if (stream.match(/^[0-9\.]/, false)) {
var floatLiteral = false; var floatLiteral = false;
// Floats // Floats
if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; } if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) {
if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; } floatLiteral = true;
if (stream.match(/^\.\d+/)) { floatLiteral = true; } }
if (stream.match(/^\d+\.\d*/)) {
floatLiteral = true;
}
if (stream.match(/^\.\d+/)) {
floatLiteral = true;
}
if (floatLiteral) { if (floatLiteral) {
// Float literals may be "imaginary" // Float literals may be "imaginary"
stream.eat(/J/i); stream.eat(/J/i);
@ -132,11 +360,17 @@ CodeMirror.defineMode("nimrod", function(conf, parserConf) {
// Integers // Integers
var intLiteral = false; var intLiteral = false;
// Hex // Hex
if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; } if (stream.match(/^0x[0-9a-f]+/i)) {
intLiteral = true;
}
// Binary // Binary
if (stream.match(/^0b[01]+/i)) { intLiteral = true; } if (stream.match(/^0b[01]+/i)) {
intLiteral = true;
}
// Octal // Octal
if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; } if (stream.match(/^0o[0-7]+/i)) {
intLiteral = true;
}
// Decimal // Decimal
if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) { if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
// Decimal literals may be "imaginary" // Decimal literals may be "imaginary"
@ -145,7 +379,9 @@ CodeMirror.defineMode("nimrod", function(conf, parserConf) {
intLiteral = true; intLiteral = true;
} }
// Zero by itself with no other piece of number. // Zero by itself with no other piece of number.
if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; } if (stream.match(/^0(?![\dx])/i)) {
intLiteral = true;
}
if (intLiteral) { if (intLiteral) {
// Integer literals may be "long" // Integer literals may be "long"
stream.eat(/L/i); stream.eat(/L/i);
@ -159,18 +395,17 @@ CodeMirror.defineMode("nimrod", function(conf, parserConf) {
return state.tokenize(stream, state); return state.tokenize(stream, state);
} }
if (stream.match(operators)) if (stream.match(operators)) return 'operator';
return 'operator';
if (stream.match(keywords)) if (stream.match(keywords)) return 'keyword';
return 'keyword';
if (stream.match(builtins)) if (stream.match(builtins)) return 'builtin';
return 'builtin';
if (stream.match(identifiers)) { if (stream.match(identifiers)) {
if (state.lastToken != null && if (
state.lastToken.match(/proc|iterator|macro|template|class|converter/)) { state.lastToken != null &&
state.lastToken.match(/proc|iterator|macro|template|class|converter/)
) {
return 'def'; return 'def';
} }
@ -288,25 +523,21 @@ CodeMirror.defineMode("nimrod", function(conf, parserConf) {
return style; return style;
} }
if ((style === 'variable' || style === 'builtin') if ((style === 'variable' || style === 'builtin') && state.lastStyle === 'meta') {
&& state.lastStyle === 'meta') {
style = 'meta'; style = 'meta';
} }
// Handle scope changes. // Handle scope changes.
if (current.match(/return|break|continue|raise/) || if (current.match(/return|break|continue|raise/) || (current === 'discard' && stream.eol()))
(current === 'discard' && stream.eol()))
state.dedent += 1; state.dedent += 1;
if (current === 'lambda' || current === 'proc') if (current === 'lambda' || current === 'proc') state.lambda = true;
state.lambda = true;
var delimiter_index = '[({'.indexOf(current); var delimiter_index = '[({'.indexOf(current);
if (delimiter_index !== -1) { if (delimiter_index !== -1) {
indent(stream, state, '])}'.slice(delimiter_index, delimiter_index + 1)); indent(stream, state, '])}'.slice(delimiter_index, delimiter_index + 1));
} } else if (stream.eol() && current.match(/\=|\:|import|include|type|const|var|let/)) {
else if(stream.eol() && current.match(/\=|\:|import|include|type|const|var|let/)) {
indent(stream, state); indent(stream, state);
} }
@ -349,28 +580,24 @@ CodeMirror.defineMode("nimrod", function(conf, parserConf) {
state.lastStyle = style; state.lastStyle = style;
var current = stream.current(); var current = stream.current();
if (current && style) if (current && style) state.lastToken = current;
state.lastToken = current;
if (stream.eol() && state.lambda) if (stream.eol() && state.lambda) state.lambda = false;
state.lambda = false;
return style; return style;
}, },
indent: function(state) { indent: function(state) {
if (state.tokenize != tokenBase) if (state.tokenize != tokenBase) return state.tokenize.isString ? CodeMirror.Pass : 0;
return state.tokenize.isString ? CodeMirror.Pass : 0;
return state.scopes[0].offset; return state.scopes[0].offset;
}, },
lineComment: "#", lineComment: '#',
fold: "indent" fold: 'indent'
}; };
return external; return external;
}); });
CodeMirror.defineMIME("text/x-nimrod", "nimrod"); CodeMirror.defineMIME('text/x-nimrod', 'nimrod');

@ -7,7 +7,9 @@
"dev": "node server.js", "dev": "node server.js",
"build": "next build", "build": "next build",
"start": "NODE_ENV=production node server.js", "start": "NODE_ENV=production node server.js",
"deploy": "now --public" "deploy": "now --public",
"prettier": "prettier --config .prettierrc --write 'components/**/*.js' 'handlers/**/*.js' 'lib/**/*.js' 'pages/**/*.js' 'server.js'",
"lint": "prettier --config .prettierrc -l 'components/**/*.js' 'handlers/**/*.js' 'lib/**/*.js' 'pages/**/*.js' 'server.js'"
}, },
"dependencies": { "dependencies": {
"axios": "^0.16.2", "axios": "^0.16.2",
@ -37,6 +39,7 @@
"twitter": "^1.7.1" "twitter": "^1.7.1"
}, },
"devDependencies": { "devDependencies": {
"prettier": "^1.7.2",
"uglifyjs-webpack-plugin": "1.0.0-beta.1", "uglifyjs-webpack-plugin": "1.0.0-beta.1",
"webpack": "^3.6.0" "webpack": "^3.6.0"
}, },

@ -1,15 +1,20 @@
import Page from '../components/Page' import Page from '../components/Page';
import Meta from '../components/Meta' import Meta from '../components/Meta';
import Header from '../components/Header' import Header from '../components/Header';
import Footer from '../components/Footer' import Footer from '../components/Footer';
import { COLORS } from '../lib/constants' import { COLORS } from '../lib/constants';
export default () => ( export default () => (
<Page> <Page>
<div className="about"> <div className="about">
<div className="mb4"> <div className="mb4">
<h2>What does this do?</h2> <h2>What does this do?</h2>
<p>You know all of those code screenshots you see on Twitter? Although the code's usually impressive, we saw room for improvement in the aesthetic department. Carbon is the easiest way to create beautiful images of your source code. So what are you waiting for? Go impress all of your followers with your newfound design prowess. <span>🎨</span></p> <p>
You know all of those code screenshots you see on Twitter? Although the code's usually
impressive, we saw room for improvement in the aesthetic department. Carbon is the easiest
way to create beautiful images of your source code. So what are you waiting for? Go
impress all of your followers with your newfound design prowess. <span>🎨</span>
</p>
</div> </div>
<div className="mb4"> <div className="mb4">
<h2>How do I use it?</h2> <h2>How do I use it?</h2>
@ -17,23 +22,38 @@ export default () => (
<p className="mb1 mt2">There are a few different ways to import code into Carbon:</p> <p className="mb1 mt2">There are a few different ways to import code into Carbon:</p>
<ul className="mt0 mb3"> <ul className="mt0 mb3">
<li>Drop a file into the editor</li> <li>Drop a file into the editor</li>
<li>Append a GitHub gist id to the url (<a className="link" href="/0db00e81d5416c339181e59481c74b59">example</a>)</li> <li>
Append a GitHub gist id to the url (<a
className="link"
href="/0db00e81d5416c339181e59481c74b59"
>
example
</a>)
</li>
<li>Paste your code directly</li> <li>Paste your code directly</li>
</ul> </ul>
<h4 className="mb0">Customization</h4> <h4 className="mb0">Customization</h4>
<p className="mt2 mb3">Once you've got all of your code into Carbon, you can customize your image by changing the syntax theme, background color, window theme, or padding.</p> <p className="mt2 mb3">
Once you've got all of your code into Carbon, you can customize your image by changing the
syntax theme, background color, window theme, or padding.
</p>
<h4 className="mb0">Export/Sharing</h4> <h4 className="mb0">Export/Sharing</h4>
<p className="mt2">After you've customized your image you can Tweet a link to the image, or save it directly.</p> <p className="mt2">
After you've customized your image you can Tweet a link to the image, or save it directly.
</p>
</div> </div>
<div> <div>
<h2>I want to make this better.</h2> <h2>I want to make this better.</h2>
<p> <p>
<a className="link" href="https://github.com/dawnlabs/carbon">Please do.</a> <a className="link" href="https://github.com/dawnlabs/carbon">
Please do.
</a>
</p> </p>
</div> </div>
</div> </div>
<style jsx>{` <style jsx>{`
p, li { p,
li {
color: ${COLORS.GRAY}; color: ${COLORS.GRAY};
} }
@ -51,4 +71,4 @@ export default () => (
} }
`}</style> `}</style>
</Page> </Page>
) );

@ -1,20 +1,20 @@
// Theirs // Theirs
import React from 'react' import React from 'react';
import HTML5Backend from 'react-dnd-html5-backend' import HTML5Backend from 'react-dnd-html5-backend';
import { DragDropContext } from 'react-dnd' import { DragDropContext } from 'react-dnd';
import domtoimage from 'dom-to-image' import domtoimage from 'dom-to-image';
import ReadFileDropContainer from 'dropperx' import ReadFileDropContainer from 'dropperx';
// Ours // Ours
import Page from '../components/Page' import Page from '../components/Page';
import Button from '../components/Button' import Button from '../components/Button';
import Dropdown from '../components/Dropdown' import Dropdown from '../components/Dropdown';
import ColorPicker from '../components/ColorPicker' import ColorPicker from '../components/ColorPicker';
import Settings from '../components/Settings' import Settings from '../components/Settings';
import Toolbar from '../components/Toolbar' import Toolbar from '../components/Toolbar';
import Overlay from '../components/Overlay' import Overlay from '../components/Overlay';
import Carbon from '../components/Carbon' import Carbon from '../components/Carbon';
import api from '../lib/api' import api from '../lib/api';
import { import {
THEMES_ARRAY, THEMES_ARRAY,
THEMES, THEMES,
@ -22,7 +22,7 @@ import {
DEFAULT_LANGUAGE, DEFAULT_LANGUAGE,
COLORS, COLORS,
DEFAULT_CODE DEFAULT_CODE
} from '../lib/constants' } from '../lib/constants';
class Editor extends React.Component { class Editor extends React.Component {
/* pathname, asPath, err, req, res */ /* pathname, asPath, err, req, res */
@ -30,17 +30,17 @@ class Editor extends React.Component {
try { try {
// TODO fix this hack // TODO fix this hack
if (asPath.length > 30) { if (asPath.length > 30) {
const content = await api.getGist(asPath) const content = await api.getGist(asPath);
return { content } return { content };
} }
} catch (e) { } catch (e) {
console.log(e) console.log(e);
} }
return {} return {};
} }
constructor(props) { constructor(props) {
super(props) super(props);
this.state = { this.state = {
background: '#ABB8C3', background: '#ABB8C3',
theme: THEMES.seti.id, theme: THEMES.seti.id,
@ -51,15 +51,15 @@ class Editor extends React.Component {
paddingHorizontal: '32px', paddingHorizontal: '32px',
uploading: false, uploading: false,
code: props.content || DEFAULT_CODE code: props.content || DEFAULT_CODE
} };
this.save = this.save.bind(this) this.save = this.save.bind(this);
this.upload = this.upload.bind(this) this.upload = this.upload.bind(this);
this.updateCode = this.updateCode.bind(this) this.updateCode = this.updateCode.bind(this);
} }
getCarbonImage() { getCarbonImage() {
const node = document.getElementById('section') const node = document.getElementById('section');
const config = { const config = {
style: { style: {
@ -68,36 +68,35 @@ class Editor extends React.Component {
}, },
width: node.offsetWidth * 2, width: node.offsetWidth * 2,
height: node.offsetHeight * 2 height: node.offsetHeight * 2
} };
return domtoimage.toPng(node, config) return domtoimage.toPng(node, config);
} }
updateCode(code) { updateCode(code) {
this.setState({ code }) this.setState({ code });
} }
save() { save() {
this.getCarbonImage() this.getCarbonImage().then(dataUrl => {
.then((dataUrl) => { const link = document.createElement('a');
const link = document.createElement('a') link.download = 'carbon.png';
link.download = 'carbon.png' link.href = dataUrl;
link.href = dataUrl document.body.appendChild(link);
document.body.appendChild(link) link.click();
link.click() link.remove();
link.remove() });
})
} }
upload() { upload() {
this.setState({ uploading: true }) this.setState({ uploading: true });
this.getCarbonImage() this.getCarbonImage()
.then(api.tweet) .then(api.tweet)
.then(() => this.setState({ uploading: false })) .then(() => this.setState({ uploading: false }))
.catch((err) => { .catch(err => {
console.error(err) console.error(err);
this.setState({ uploading: false }) this.setState({ uploading: false });
}) });
} }
render() { render() {
@ -105,13 +104,23 @@ class Editor extends React.Component {
<Page enableHeroText> <Page enableHeroText>
<div id="editor"> <div id="editor">
<Toolbar> <Toolbar>
<Dropdown selected={THEMES[this.state.theme]} list={THEMES_ARRAY} onChange={theme => this.setState({ theme: theme.id })}/> <Dropdown
<Dropdown list={LANGUAGES} onChange={language => this.setState({ language: language.module })}/> selected={THEMES[this.state.theme]}
list={THEMES_ARRAY}
onChange={theme => this.setState({ theme: theme.id })}
/>
<Dropdown
list={LANGUAGES}
onChange={language => this.setState({ language: language.module })}
/>
<ColorPicker <ColorPicker
onChange={color => this.setState({ background: color })} onChange={color => this.setState({ background: color })}
bg={this.state.background} bg={this.state.background}
/> />
<Settings onChange={(key, value) => this.setState({ [key]: value })} enabled={this.state} /> <Settings
onChange={(key, value) => this.setState({ [key]: value })}
enabled={this.state}
/>
<div className="buttons"> <div className="buttons">
<Button <Button
className="tweetButton" className="tweetButton"
@ -125,18 +134,20 @@ class Editor extends React.Component {
</Toolbar> </Toolbar>
<ReadFileDropContainer onDrop={([file]) => this.setState({ code: file.content })}> <ReadFileDropContainer onDrop={([file]) => this.setState({ code: file.content })}>
{ {({ isOver, canDrop }) => (
({ isOver, canDrop }) => ( <Overlay
<Overlay isOver={isOver || canDrop} title={`Drop your file here to import ${isOver ? '✋' : '✊'}`}> isOver={isOver || canDrop}
title={`Drop your file here to import ${isOver ? '✋' : '✊'}`}
>
<Carbon config={this.state} updateCode={this.updateCode}> <Carbon config={this.state} updateCode={this.updateCode}>
{this.state.code} {this.state.code}
</Carbon> </Carbon>
</Overlay> </Overlay>
) )}
}
</ReadFileDropContainer> </ReadFileDropContainer>
</div> </div>
<style jsx>{` <style jsx>
{`
#editor { #editor {
background: ${COLORS.BLACK}; background: ${COLORS.BLACK};
border: 3px solid ${COLORS.SECONDARY}; border: 3px solid ${COLORS.SECONDARY};
@ -151,8 +162,8 @@ class Editor extends React.Component {
`} `}
</style> </style>
</Page> </Page>
) );
} }
} }
export default DragDropContext(HTML5Backend)(Editor) export default DragDropContext(HTML5Backend)(Editor);

@ -1,2 +1,2 @@
import Editor from './editor' import Editor from './editor';
export default Editor export default Editor;

@ -1,40 +1,38 @@
const express = require('express') const express = require('express');
const morgan = require('morgan') const morgan = require('morgan');
const bodyParser = require('body-parser') const bodyParser = require('body-parser');
const next = require('next') const next = require('next');
const port = parseInt(process.env.PORT, 10) || 3000 const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== 'production' && !process.env.NOW const dev = process.env.NODE_ENV !== 'production' && !process.env.NOW;
const app = next({ dev }) const app = next({ dev });
const handle = app.getRequestHandler() const handle = app.getRequestHandler();
function wrap(handler) { function wrap(handler) {
return (req, res) => handler(req, res).catch((err) => { return (req, res) =>
console.log('ERR:', err) handler(req, res).catch(err => {
}) console.log('ERR:', err);
});
} }
app.prepare() app.prepare().then(() => {
.then(() => { const server = express();
const server = express()
server.use(morgan('tiny')) server.use(morgan('tiny'));
server.get('/about', (req, res) => server.get('/about', (req, res) => app.render(req, res, '/about'));
app.render(req, res, '/about'))
// if root, render webpage from next // if root, render webpage from next
server.get('/*', (req, res) => server.get('/*', (req, res) => app.render(req, res, '/', req.query));
app.render(req, res, '/', req.query))
// otherwise, try and get gist // otherwise, try and get gist
server.get('*', handle) server.get('*', handle);
// api endpoints // api endpoints
server.post('/twitter', bodyParser.json({ limit: '5mb' }), require('./handlers/twitter')) server.post('/twitter', bodyParser.json({ limit: '5mb' }), require('./handlers/twitter'));
server.listen(port, (err) => { server.listen(port, err => {
if (err) throw err if (err) throw err;
console.log(`> Ready on http://localhost:${port}`) console.log(`> Ready on http://localhost:${port}`);
}) });
}) });

File diff suppressed because it is too large Load Diff

@ -3033,6 +3033,10 @@ preserve@^0.2.0:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
prettier@^1.7.2:
version "1.7.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.7.2.tgz#81371e64018aafc69cf1031956c70e029339f54e"
private@^0.1.6, private@^0.1.7: private@^0.1.6, private@^0.1.7:
version "0.1.7" version "0.1.7"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1" resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1"

Loading…
Cancel
Save