@@ -0,0 +1,43 @@
.TopBar {
width: 100vw;
height: 48px;
color: #fff;
z-index: 11;
/* background: linear-gradient(-45deg, #313358, #1D1F31); */
/* background: #1d1f31; */
/* background: #202231; */
/* border-bottom: 1px solid #32335a; */
}

.left {
Flex: row flex-start center;
Size: 30% 100%;
padding: 0 24px;
}

.right {
Flex: row flex-end center;
Size: 70% 100%;
padding: 0 24px;
}

/* .editorTopBar {
Size: 100% 32px;
background: var(--darkBlue1);
box-shadow: var(--shadow0);
z-index: 4;
position: relative;
} */

.inner {
height: 100%;
display: flex;
align-items: center;
width: 100%;
z-index: 4;
position: relative;
}

.logo {
font-weight: 700;
}
File renamed without changes.
@@ -0,0 +1,25 @@
import * as React from 'react'
import { action, computed } from 'mobx'
import { Link } from 'react-router-dom'

import { createComponent } from '#utilities/createComponent'
import { lastInArray as last } from '#utilities/lastInArray'

import './TopBar.css'

export const TopBar = createComponent((self) => {
return () => (
<div styleName="TopBar">
<div styleName="inner">
<div styleName="left" data-white-theme>
<Link to="/">
<p styleName="logo">bytesized</p>
</Link>
</div>
<div styleName="right" data-dark-theme>
<p>...</p>
</div>
</div>
</div>
)
})
@@ -6,5 +6,5 @@ import { TopBar } from '../index'
const { it, expect } = global

it('works and stuff', () => {
expect(true).toBe(true)
expect(true).toBe(true)
})
@@ -0,0 +1 @@
export { TopBar } from './TopBar'
File renamed without changes.
@@ -0,0 +1,26 @@
import * as React from 'react'
import { Controlled as CodeMirror } from 'react-codemirror2'
import { observer } from 'mobx-react'

import { EDITOR_OPTIONS } from './consts'
import './Editor.css'

type PropsT = {
content: string,
onChange(): void
}

export const Editor = (props: PropsT) => {
return (
<div styleName="Editor">
<CodeMirror
autoFocus
autoCursor
value={props.content}
options={EDITOR_OPTIONS}
className="bytesize-CodeMirror"
onBeforeChange={props.onChange}
/>
</div>
)
}
@@ -6,5 +6,5 @@ import { Editor } from '../index'
const { it, expect } = global

it('works and stuff', () => {
expect(true).toBe(true)
expect(true).toBe(true)
})
@@ -0,0 +1,10 @@
export const EDITOR_OPTIONS = {
autoCloseBrackets: true,
cursorScrollMargin: 48,
mode: 'javascript',
theme: 'shit',
lineNumbers: true,
indentUnit: 2,
tabSize: 2,
styleActiveLine: true
}
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,31 @@
import * as React from 'react'
import { render } from 'react-dom'

import { AppContainer } from 'react-hot-loader'
import { App } from '#components/App'

import './styles/normalize.css'
import './styles/index.css'
import 'react-mde/lib/styles/css/react-mde-all.css'

const _render = (Component) => {
render(
<AppContainer>
<Component />
</AppContainer>,
document.getElementById('mountPoint')
)
}

// TODO: Ensure this gets eliminated with minification.
if (__DEV__) {
console.log('!!__DEV__')
if (module.hot) {
console.log('!!module.hot')
module.hot.accept('./components/App/index.js', () => {
_render(require('./components/App/index.js').App)
})
}
}

_render(App)
File renamed without changes.
@@ -0,0 +1,45 @@
import * as React from 'react'
import { inject, observer } from 'mobx-react'

import { SideBar } from '#components/SideBar'
import { ContentSection } from '#components/ContentSection'

import './AuthScene.css'

const stateTreeSelector = (tree) => {
return {
$auth: tree.state.auth
}
}

@inject(stateTreeSelector)
@observer
export class AuthScene extends React.Component {
async componentDidMount() {
// const done = await this.props.$auth.handleAuth()
const auth = this.props.$auth.auth0

auth.auth0.parseHash(async (err, authResult) => {
if (err) reject(err)

const response = await fetch(
`$SERVER_ADDRESS$$API_PATH$/users/${authResult.idTokenPayload.nickname}`
)

const user = await response.json()
console.log({ user })
this.props.$auth.setAuthData(authResult, user)
// this.props.$auth.setUserData(user)
this.props.history.push('/dashboard')
})
}

render() {
return (
<main styleName="AuthScene">
<SideBar>wait a mment</SideBar>
<ContentSection>were authing</ContentSection>
</main>
)
}
}
File renamed without changes.
File renamed without changes.
@@ -7,11 +7,11 @@ import { select, user, editor } from '#state/selectors'
import './CommunityView.css'

export class CommunityView extends React.Component {
render() {
return (
<div styleName="CommunityView">
<p>CommunityView</p>
</div>
)
}
render() {
return (
<div styleName="CommunityView">
<p>CommunityView</p>
</div>
)
}
}
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,54 @@
import * as React from 'react'
import { observer, inject } from 'mobx-react'
import { Route, Link, Switch } from 'react-router-dom'
import { observable, action } from 'mobx'
import { select, user, editor, auth } from '#state/selectors'

import { SideBar as SideBarWrapper } from '#components/SideBar'
import { ContentSection } from '#components/ContentSection'
import { SideBar } from './SideBar'
import './DashboardScene.css'
import { ModulesView } from './ModulesView'

@inject((tree) => {
return {
$user: tree.state.user
}
})
@observer
export class DashboardScene extends React.Component {
@observable view = ''
@action
setView = (which) => {
this.view = which
}
render() {
return (
<main styleName="DashboardScene">
<SideBarWrapper>
<SideBar setView={this.setView} />
</SideBarWrapper>
<ContentSection>
<div styleName="sideBar">
<Choose>
<When condition={this.view.includes('modules')}>
<ModulesView user={this.props.$user} />
</When>
<When condition={this.view.includes('community')}>
<p>community</p>
</When>
<When condition={this.view.includes('settings')}>
<p>settings</p>
</When>
<Otherwise>
<ModulesView user={this.props.$user} />
</Otherwise>
</Choose>
</div>
</ContentSection>
</main>
)
}
}
@@ -6,5 +6,5 @@ import { ModuleScene } from '../index'
const { it, expect } = global

it('works and stuff', () => {
expect(true).toBe(true)
expect(true).toBe(true)
})
File renamed without changes.
@@ -0,0 +1,41 @@
import * as React from 'react'
import { observer, inject } from 'mobx-react'
import { Route, Link, Switch } from 'react-router-dom'
import { observable, action } from 'mobx'
import { select, user, editor } from '#state/selectors'
import './ModulesView.css'

const Module = (props) => {
return (
<div>
<Link to={`/modules/${props.userName}/${props.module.uid}`}>
<h3>{props.module.title}</h3>
</Link>
</div>
)
}

@observer
export class ModulesView extends React.Component {
@observable loading = true

render() {
console.log(
'RENDERRRR',
{ props: this.props.user },
this.props.user.userName
)
return (
<div styleName="ModulesView">
<h2 styleName="title">Modules</h2>
<For each="module" of={this.props.user.modules}>
<Module
key={module.uid}
userName={this.props.user.userName}
module={module}
/>
</For>
</div>
)
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -7,11 +7,11 @@ import { select, user, editor } from '#state/selectors'
import './SettingsView.css'

export class SettingsView extends React.Component {
render() {
return (
<div styleName="SettingsView">
<p>SettingsView</p>
</div>
)
}
render() {
return (
<div styleName="SettingsView">
<p>SettingsView</p>
</div>
)
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,16 @@
import * as React from 'react'
import './Selection.css'

export const Selection = (props) => {
return (
<div onClick={() => props.setView(props.routeTo)} styleName="Item">
<div styleName="Thing">
<props.icon />
</div>
<div styleName="info">
<h3>{props.title}</h3>
<p>{props.info}</p>
</div>
</div>
)
}
File renamed without changes.
@@ -0,0 +1,38 @@
import * as React from 'react'
import { observer, inject } from 'mobx-react'
import { observable, action } from 'mobx'

import { Selection } from './Selection'
import ListIcon from '#assets/svgs/list-0.svg'
import SocialIcon from '#assets/svgs/social-0.svg'
import SettingsIcon from '#assets/svgs/settings-0.svg'
import './SideBar.css'

export const SideBar = (props) => {
return (
<div styleName="sideBar">
<h1>Dashboard</h1>
<Selection
icon={ListIcon}
setView={props.setView}
routeTo={'modules'}
title="Your Modules"
info="View, edit, and review your modules."
/>
<Selection
icon={SocialIcon}
setView={props.setView}
routeTo={'community'}
title="Your Circle"
info="See what others in your community are doing."
/>
<Selection
icon={SettingsIcon}
setView={props.setView}
routeTo={'settings'}
title="Your Settings"
info="I promise we don't sell your shit like Zuck."
/>
</div>
)
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,51 @@
import * as React from 'react'
import { observable, action, computed } from 'mobx'
import { observer } from 'mobx-react'
import { Link } from 'react-router-dom'
import './Authenticate.css'

@observer
export class Authenticate extends React.Component {
@observable userName = ''
@observable password0 = ''
@observable password1 = ''

@action
setUserName = (event) => {
this.userName = event.target.value
}

@action
setPassword0 = (event) => {
this.password0 = event.target.value
}

@action
setPassword1 = (event) => {
this.password1 = event.target.value
}

render() {
return (
<div styleName="Authenticate">
<h1>Log In</h1>
{/* <input
styleName="input"
placeholder="user name"
value={this.userName}
onChange={this.setUserName}
/>
<input
styleName="input"
type="password"
placeholder="password"
value={this.password0}
onChange={this.setPassword0}
/> */}
<button styleName="submitButton" onClick={this.props.login}>
Log In With Github
</button>
</div>
)
}
}
File renamed without changes.
File renamed without changes.
@@ -8,26 +8,26 @@ import { Intro } from './Intro'
import { Authenticate } from './Authenticate'
import './HomeScene.css'

const selector = stateTree => {
console.log({ stateTree })
return {
$auth: stateTree.state.auth
}
const selector = (stateTree) => {
console.log({ stateTree })
return {
$auth: stateTree.state.auth
}
}

@inject(selector)
@observer
export class HomeScene extends React.Component {
render() {
return (
<main styleName="HomeScene">
<SideBar>
<Intro />
</SideBar>
<ContentSection>
<Authenticate login={this.props.$auth.logIn} />
</ContentSection>
</main>
)
}
render() {
return (
<main styleName="HomeScene">
<SideBar>
<Intro />
</SideBar>
<ContentSection>
<Authenticate login={this.props.$auth.logIn} />
</ContentSection>
</main>
)
}
}
@@ -6,5 +6,5 @@ import { ModuleScene } from '../index'
const { it, expect } = global

it('works and stuff', () => {
expect(true).toBe(true)
expect(true).toBe(true)
})
File renamed without changes.
@@ -0,0 +1,13 @@
import * as React from 'react'

import './Intro.css'

export const Intro = (props) => {
return (
<div styleName="Intro">
<h1>
Join a community built on micro code lessons, passion, and friendliness.
</h1>
</div>
)
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,42 @@
export const CUSTOM_BUTTON_CLASS = 'rte-editor-custom-button'

export const STYLE_MAP = {
STRIKETHROUGH: {
textDecoration: 'line-through'
},
CODE: {
padding: '8px',
background: '#f2f2f2'
}
}

export const TOOLBAR_CONFIG = {
// Optionally specify the groups to display (displayed in the order listed).
display: [
'INLINE_STYLE_BUTTONS',
'BLOCK_TYPE_BUTTONS',
'LINK_BUTTONS',
'BLOCK_TYPE_DROPDOWN',
'HISTORY_BUTTONS'
],
INLINE_STYLE_BUTTONS: [
{
label: 'Bold',
style: 'BOLD',
className: CUSTOM_BUTTON_CLASS
},
{ label: 'Italic', style: 'ITALIC', className: CUSTOM_BUTTON_CLASS },
{ label: 'Underline', style: 'UNDERLINE', className: CUSTOM_BUTTON_CLASS },
{ label: 'Monospace', style: 'CODE', className: CUSTOM_BUTTON_CLASS }
],
BLOCK_TYPE_DROPDOWN: [
{ label: 'Normal', style: 'unstyled' },
{ label: 'Heading Large', style: 'header-one' },
{ label: 'Heading Medium', style: 'header-two' },
{ label: 'Heading Small', style: 'header-three' }
],
BLOCK_TYPE_BUTTONS: [
{ label: 'UL', style: 'unordered-list-item' },
{ label: 'OL', style: 'ordered-list-item' }
]
}
@@ -0,0 +1,33 @@
import * as React from 'react'
import { Controlled as CodeMirror } from 'react-codemirror2'
import { Observer } from 'mobx-react'

type SelfT = {
props: {}
}

export const EDITOR_OPTIONS = {
autoCloseBrackets: true,
cursorScrollMargin: 48,
mode: 'javascript',
theme: 'shit',
lineNumbers: true,
indentUnit: 2,
tabSize: 2,
styleActiveLine: true,
readOnly: true
}

export const CodeRenderer = (props) => {
return (
<div>
<CodeMirror
value={props.value}
className="bytesize-CodeMirror ofh"
options={EDITOR_OPTIONS}
autoCursor
autoFocus
/>
</div>
)
}
File renamed without changes.
@@ -7,7 +7,7 @@
}

.actions {
height: 84px !important;
height: 60px !important;
Flex: row center center;
flex-shrink: 0;
background: #f8f8f8;
@@ -0,0 +1,54 @@
import * as React from 'react'
import Markdown from 'react-markdown'
import { observer, inject } from 'mobx-react'
import { observable, action, computed } from 'mobx'

import { CodeRenderer } from './CodeRenderer'
import { LessonEditor } from './LessonEditor'
import { StateInjector } from '#utilities/StateInjector'
import EditIcon from '#assets/svgs/pencil.svg'

import './InstructionPanel.css'

const stateSelector = (tree) => {
return {
$lesson: tree.state.lesson
}
}

@inject(stateSelector)
@observer
// TODO: Make not a class, man.
export class InstructionPanel extends React.Component {
render() {
return (
<div
styleName={`InstructionPanel light`}
className={`bytesize-light-theme`}
>
<Choose>
<When condition={!this.props.$lesson.editing}>
<Markdown
source={this.props.$lesson.content}
styleName="markdown"
renderers={{
code: CodeRenderer
}}
/>
<div styleName="actions">
<If condition={this.props.$lesson.isEditableByUser}>
<EditIcon
styleName="editButton"
onClick={this.props.$lesson.toggleEditing}
/>
</If>
</div>
</When>
<Otherwise>
<LessonEditor lesson={this.props.$lesson} />
</Otherwise>
</Choose>
</div>
)
}
}
@@ -6,5 +6,5 @@ import { InstructionPanel } from './InstructionPanel'
const { it, expect } = global

it('works and stuff', () => {
expect(true).toBe(true)
expect(true).toBe(true)
})
@@ -57,8 +57,8 @@
.lessonEditorControls {
margin-top: auto;
padding: 12px;
Size: 100% 84px;
height: 84px !important;
width: 100%;
height: 60px !important;
flex-shrink: 0;
background: #f8f8f8;
Flex: row space-around center;
@@ -0,0 +1,84 @@
import * as React from 'react'
import { createComponent } from '#utilities/createComponent'
import ReactMde from 'react-mde'
import * as Showdown from 'showdown'
import { inject, observer } from 'mobx-react'
import { EditorState } from 'draft-js'
import { CodeRenderer } from '../CodeRenderer'
import Markdown from 'react-markdown'

import './LessonEditor.css'

// NOTE: Experimental alternative React API I came up with.
// NOTE: I really like this API......
export const LessonEditor = createComponent((self) => {
const { props } = self
const { lesson } = props

const converter = new Showdown.Converter({
tables: true,
simplifiedAutoLink: true
})

self.state = {
editorState: {
markdown: lesson.editedContent
}
}

const onChange = (editorState) => {
lesson.setContent(editorState)
self.setState((state) => ({ editorState }))
}

const generator = (markdown) => {
return Promise.resolve(converter.makeHtml(markdown))
}

const styleName = (props) => {
return lesson.previewing ? 'previewing' : lesson.editing ? 'editing' : ''
}

return () => {
return (
<div styleName={`LessonEditor ${styleName(self.props)}`}>
<Choose>
<When condition={lesson.previewing}>
<MD markdown={lesson.editedContent} />
</When>
<Otherwise>
<ReactMde
onChange={onChange}
editorState={self.state.editorState}
generateMarkdownPreview={generator}
/>
</Otherwise>
</Choose>
<div styleName="lessonEditorControls">
<i className="fas fa-ban fa-lg" onClick={() => {}} />
<span onClick={lesson.togglePreviewing}>
<i className="fas fa-eye fa-lg" />
</span>
<span onClick={lesson.saveContent}>
<i className="fas fa-save fa-lg" />
</span>
</div>
</div>
)
}
})

class MD extends React.PureComponent {
render() {
return (
<div styleName="lessonPreview">
<Markdown
source={this.props.markdown}
renderers={{
code: CodeRenderer
}}
/>
</div>
)
}
}
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,21 @@
import * as React from 'react'

import './TagBox.css'

const Tag = (props) => {
return (
<span styleName="Tag">
<small>{props.name}</small>
</span>
)
}

export const TagBox = (props) => {
return (
<div styleName="TagBox">
<For each="tag" of={props.tags} index="index">
<Tag key={tag.name} name={tag.name} />
</For>
</div>
)
}
File renamed without changes.
File renamed without changes.
@@ -85,13 +85,13 @@ Example: \`remmy comp MyNewComponent\`
`

export const MOCK_TAGS = [
{
name: 'javascript'
},
{
name: 'promises'
},
{
name: 'beginner'
}
{
name: 'javascript'
},
{
name: 'promises'
},
{
name: 'beginner'
}
]
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,55 @@
import * as React from 'react'
import PanelGroup from 'react-panelgroup'
import { inject, observer } from 'mobx-react'
import MarkdownInput from '@opuscapita/react-markdown'

import { Editor } from '#features/Editor'
import { InstructionPanel } from './InstructionPanel'
import { OutputPanel } from './OutputPanel'

import { PANEL_SETTINGS, ROW_PANEL_SETTINGS } from './consts'
import './ModuleScene.css'

const selector = (tree) => {
return {
$main: tree.state,
$editor: tree.state.editor
}
}

@inject(selector)
@observer
export class ModuleScene extends React.Component {
componentDidMount() {
this.props.$main.activateModule(this.props.match.params)
}

render() {
return (
<div styleName="ModuleScene">
<InstructionPanel
source={this.props.$editor.instructions}
setInstructions={this.props.$editor.setInstructions}
/>
<div styleName="right">
<PanelGroup
direction="column"
borderColor={'transparent'}
panelWidths={PANEL_SETTINGS}
>
<Editor
content={this.props.$editor.content}
onChange={(editor, data, content) => {
this.props.$editor.setContent(content)
}}
/>
<OutputPanel
ref={this.OutputPanel}
format={this.props.$editor.formatContent}
/>
</PanelGroup>
</div>
</div>
)
}
}
@@ -6,5 +6,5 @@ import { ModuleScene } from '../index'
const { it, expect } = global

it('works and stuff', () => {
expect(true).toBe(true)
expect(true).toBe(true)
})
File renamed without changes.
@@ -0,0 +1,23 @@
import * as React from 'react'
import moment from 'moment'
import { inject, observer } from 'mobx-react'

import './ControlPanel.css'

const Control = (props) => {
return (
<div onClick={props.action} styleName="Control">
<small>{props.title}</small>
</div>
)
}

export const ControlPanel = (props) => {
return (
<div styleName="ControlPanel">
<Control title="Execute" action={props.execute} />
<Control title="Format" action={props.format} />
<Control title="Clear" action={props.clear} />
</div>
)
}
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,61 @@
import * as React from 'react'
import moment from 'moment'
import { inject, observer } from 'mobx-react'
import StayScrolled from 'react-stay-scrolled'
import Inspector from 'react-inspector'

import PlayButton from '#assets/svgs/play-0.svg'
import OptionsButton from '#assets/svgs/more-0.svg'

import ErrorIcon from '#assets/svgs/alert-0.svg'
import WarningIcon from '#assets/svgs/alert-1.svg'
import NormalIcon from '#assets/svgs/alert-2.svg'

import { theme, errorTheme } from '../theme'
import { warningTheme } from './warningTheme'
import './OutputBlock.css'

import { ObjectRootLabel } from 'react-inspector'
import { ObjectLabel } from 'react-inspector'

const LogIcon = (props) => {
switch (props.log.type) {
case 'ERROR':
return <ErrorIcon styleName="LogIcon" />
case 'STDERR':
return <ErrorIcon styleName="LogIcon" />
case 'WARNING':
return <WarningIcon styleName="LogIcon" />
default:
return <NormalIcon styleName="LogIcon" />
}
}

const getLogTheme = (log) => {
if (log.type === 'WARNING') {
return warningTheme
}

return ['INFO', 'STDOUT'].includes(log.type) ? theme : errorTheme
}

export const OutputBlock = (props) => {
return (
<div styleName={`OutputBlock ${props.log.type}`}>
<Choose>
<When condition={props.log.type === 'INFO'}>
<LogIcon log={props.log} />
<p key={props.log.uid}>{props.log.message}</p>
</When>
<Otherwise>
<LogIcon log={props.log} />
<Inspector
key={props.log.uid}
theme={getLogTheme(props.log)}
data={props.log.message}
/>
</Otherwise>
</Choose>
</div>
)
}
File renamed without changes.
@@ -0,0 +1,48 @@
export const warningTheme = {
BASE_FONT_FAMILY: 'Fira Code, monospace',
BASE_FONT_SIZE: '22px',
BASE_LINE_HEIGHT: '1.9',

// Background color matches TopBar.
BASE_BACKGROUND_COLOR: 'transparent',
// "Base font color."
BASE_COLOR: 'rgba(255, 255, 255, 0.7)',
// rgb(255, 239, 110)
// object.PROPERTY color.
OBJECT_NAME_COLOR: '#fff',

OBJECT_VALUE_NULL_COLOR: 'rgb(222, 127, 127)',
OBJECT_VALUE_UNDEFINED_COLOR: 'rgb(127, 127, 127)',
OBJECT_VALUE_REGEXP_COLOR: '#fa9119',
OBJECT_VALUE_STRING_COLOR: '#fa9119',
OBJECT_VALUE_SYMBOL_COLOR: '#fa9119',
OBJECT_VALUE_NUMBER_COLOR: 'rgb(134, 255, 128)',
OBJECT_VALUE_BOOLEAN_COLOR: 'rgb(51, 213, 243)',
OBJECT_VALUE_FUNCTION_KEYWORD_COLOR: 'rgb(242, 85, 217)',

HTML_TAG_COLOR: 'rgb(93, 176, 215)',
HTML_TAGNAME_COLOR: 'rgb(93, 176, 215)',
HTML_TAGNAME_TEXT_TRANSFORM: 'lowercase',
HTML_ATTRIBUTE_NAME_COLOR: 'rgb(155, 187, 220)',
HTML_ATTRIBUTE_VALUE_COLOR: 'rgb(242, 151, 102)',
HTML_COMMENT_COLOR: 'rgb(137, 137, 137)',
HTML_DOCTYPE_COLOR: 'rgb(192, 192, 192)',

ARROW_COLOR: 'rgb(145, 145, 145)',
ARROW_MARGIN_RIGHT: 3,
ARROW_FONT_SIZE: 12,

// Each <li>
TREENODE_FONT_FAMILY: 'Fira code, monospace',
TREENODE_FONT_SIZE: '13px',
TREENODE_LINE_HEIGHT: '1.7',
TREENODE_PADDING_LEFT: 24,

TABLE_BORDER_COLOR: 'rgb(85, 85, 85)',
TABLE_TH_BACKGROUND_COLOR: 'rgb(44, 44, 44)',
TABLE_TH_HOVER_COLOR: 'rgb(48, 48, 48)',
TABLE_SORT_ICON_COLOR: 'black',
TABLE_DATA_BACKGROUND_IMAGE:
'linear-gradient(rgba(255, 255, 255, 0), rgba(255, 255, 255, 0) 50%, rgba(51, 139, 255, 0.0980392) 50%, rgba(51, 139, 255, 0.0980392))',
TABLE_DATA_BACKGROUND_SIZE: '128px 32px'
}
@@ -1,6 +1,6 @@
import { ObjectRootLabel } from 'react-inspector'
import { ObjectLabel } from 'react-inspector'

export const OutputError = props => {
return <ObjectRootLabel name={name} data={data} />
export const OutputError = (props) => {
return <ObjectRootLabel name={name} data={data} />
}
@@ -0,0 +1,19 @@
import { ObjectRootLabel } from 'react-inspector'
import { ObjectLabel } from 'react-inspector'

const defaultNodeRenderer = ({
depth,
name,
data,
isNonenumerable,
expanded
}) =>
depth === 0 ? (
<ObjectRootLabel name={name} data={data} />
) : (
<ObjectLabel name={name} data={data} isNonenumerable={isNonenumerable} />
)

export const OutputError = (props) => {
return <ObjectRootLabel name={name} data={data} />
}
File renamed without changes.
@@ -0,0 +1,78 @@
import * as React from 'react'
import { inject, observer } from 'mobx-react'

import { createSocket } from './socket'
import PlayButton from '#assets/svgs/play-0.svg'
import MagicWand from '#assets/svgs/magic-0.svg'
import OptionsButton from '#assets/svgs/more-0.svg'
import { OutputBlock } from './OutputBlock'
import { ControlPanel } from './ControlPanel'

import './OutputPanel.css'

import { ObjectRootLabel } from 'react-inspector'

export const OutputError = (props) => {
return <ObjectRootLabel name={props.name} data={props.data} />
}

const stateTreeSelector = (tree) => {
return {
$editor: tree.state.editor,
$output: tree.state.output
}
}

@inject(stateTreeSelector)
@observer
export class OutputPanel extends React.Component {
$output = this.props.$output

componentWillMount() {
this.props.$output.clearLogs()
this.scrollBox = React.createRef()
this.socket = createSocket(this)
}

componentWillUnmount() {
this.props.$output.clearLogs()
this.socket.close()
}

componentDidUpdate(oldProps) {
// TODO: Add condition to maintain scroll position if the user
// has manually scrolled from the bottom.
this.scrollBox.current.scrollTop = 9999
}

render() {
return (
<div styleName="OutputPanel" data-bytesize-output-panel>
<ControlPanel
format={this.props.format}
execute={this.socket.execute}
clear={this.$output.clearLogs}
/>
<div styleName="output" ref={this.scrollBox}>
<Choose>
<When condition={!this.$output.logCount}>
<OutputBlock
log={{
type: 'INFO',
message: '# output will appear here',
uid: '4y9fjuaeu3q9'
}}
key={'4y9fjuaeu3q9'}
/>
</When>
<Otherwise>
<For each="log" of={this.$output.logs} index="index">
<OutputBlock log={log} key={log.uid} />
</For>
</Otherwise>
</Choose>
</div>
</div>
)
}
}
@@ -6,5 +6,5 @@ import { OutputPanel } from '../index'
const { it, expect } = global

it('works and stuff', () => {
expect(true).toBe(true)
expect(true).toBe(true)
})
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,54 @@
export const createSocket = (self) => {
let socket = new WebSocket('$SOCKET_ADDRESS$/run')

socket.addEventListener('open', (event) => {
console.log('socket "/run": connected')
})

socket.addEventListener('close', (event) => {
self.props.$output.addSocketDisconnectLog()
console.log('socket "/run": disconnected')
})

let timeout = null
const cache = []

const handleLog = (log) => {
timeout && (clearTimeout(timeout), console.log('clearing timeout'))
cache.push(log)

return new Promise((resolve, reject) => {
timeout = setTimeout(() => {
// console.log('done with timeout')
resolve(cache)
}, 350)
})
}

socket.addEventListener('message', (event) => {
const data = JSON.parse(event.data)
handleLog(data).then((cache) => self.props.$output.updateLogs(cache))
})

return {
close() {
socket.close()
},

execute() {
self.props.$output.addExecutionLog()

if (socket.readyState === 3) {
try {
socket = new WebSocket('$SOCKET_ADDRESS$/run')
socket.send(JSON.stringify({ code: self.props.$editor.content }))
} catch (error) {
self.props.$output.addSocketNotConnectedLog()
self.props.$output.addErrorLog(JSON.stringify(error))
}
} else {
socket.send(JSON.stringify({ code: self.props.$editor.content }))
}
}
}
}
@@ -0,0 +1,97 @@
export const theme = {
BASE_FONT_FAMILY: 'Fira Code, monospace',
BASE_FONT_SIZE: '14px',
BASE_LINE_HEIGHT: '1.9',

// Background color matches TopBar.
BASE_BACKGROUND_COLOR: 'rgba(0,0,0,0)',
// "Base font color."
BASE_COLOR: 'rgba(255, 255, 255, 0.7)',
// rgb(255, 239, 110)
// object.PROPERTY color.
OBJECT_NAME_COLOR: '#fff',

OBJECT_VALUE_NULL_COLOR: 'rgb(222, 127, 127)',
OBJECT_VALUE_UNDEFINED_COLOR: 'rgb(127, 127, 127)',
OBJECT_VALUE_REGEXP_COLOR: '#19fac5',
OBJECT_VALUE_STRING_COLOR: '#19fac5',
OBJECT_VALUE_SYMBOL_COLOR: '#19fac5',
OBJECT_VALUE_NUMBER_COLOR: 'rgb(134, 255, 128)',
OBJECT_VALUE_BOOLEAN_COLOR: 'rgb(51, 213, 243)',
OBJECT_VALUE_FUNCTION_KEYWORD_COLOR: 'rgb(242, 85, 217)',

HTML_TAG_COLOR: 'rgb(93, 176, 215)',
HTML_TAGNAME_COLOR: 'rgb(93, 176, 215)',
HTML_TAGNAME_TEXT_TRANSFORM: 'lowercase',
HTML_ATTRIBUTE_NAME_COLOR: 'rgb(155, 187, 220)',
HTML_ATTRIBUTE_VALUE_COLOR: 'rgb(242, 151, 102)',
HTML_COMMENT_COLOR: 'rgb(137, 137, 137)',
HTML_DOCTYPE_COLOR: 'rgb(192, 192, 192)',

ARROW_COLOR: 'rgb(145, 145, 145)',
ARROW_MARGIN_RIGHT: 3,
ARROW_FONT_SIZE: 12,

// Each <li>
TREENODE_FONT_FAMILY: 'Fira Code, monospace',
TREENODE_FONT_SIZE: '13px',
TREENODE_LINE_HEIGHT: '1.4',
TREENODE_PADDING_LEFT: 12,

TABLE_BORDER_COLOR: 'rgb(85, 85, 85)',
TABLE_TH_BACKGROUND_COLOR: 'rgb(44, 44, 44)',
TABLE_TH_HOVER_COLOR: 'rgb(48, 48, 48)',
TABLE_SORT_ICON_COLOR: 'black',
TABLE_DATA_BACKGROUND_IMAGE:
'linear-gradient(rgba(255, 255, 255, 0), rgba(255, 255, 255, 0) 50%, rgba(51, 139, 255, 0.0980392) 50%, rgba(51, 139, 255, 0.0980392))',
TABLE_DATA_BACKGROUND_SIZE: '128px 32px'
}

export const errorTheme = {
BASE_FONT_FAMILY: 'Fira Code, monospace',
BASE_FONT_SIZE: '22px',
BASE_LINE_HEIGHT: '1.9',

// Background color matches TopBar.
BASE_BACKGROUND_COLOR: '#1c1325',
// "Base font color."
BASE_COLOR: 'rgba(255, 255, 255, 0.7)',
// rgb(255, 239, 110)
// object.PROPERTY color.
OBJECT_NAME_COLOR: '#fff',

OBJECT_VALUE_NULL_COLOR: 'rgb(222, 127, 127)',
OBJECT_VALUE_UNDEFINED_COLOR: 'rgb(127, 127, 127)',
OBJECT_VALUE_REGEXP_COLOR: '#F86291',
OBJECT_VALUE_STRING_COLOR: '#F86291',
OBJECT_VALUE_SYMBOL_COLOR: '#F86291',
OBJECT_VALUE_NUMBER_COLOR: 'rgb(134, 255, 128)',
OBJECT_VALUE_BOOLEAN_COLOR: 'rgb(51, 213, 243)',
OBJECT_VALUE_FUNCTION_KEYWORD_COLOR: 'rgb(242, 85, 217)',

HTML_TAG_COLOR: 'rgb(93, 176, 215)',
HTML_TAGNAME_COLOR: 'rgb(93, 176, 215)',
HTML_TAGNAME_TEXT_TRANSFORM: 'lowercase',
HTML_ATTRIBUTE_NAME_COLOR: 'rgb(155, 187, 220)',
HTML_ATTRIBUTE_VALUE_COLOR: 'rgb(242, 151, 102)',
HTML_COMMENT_COLOR: 'rgb(137, 137, 137)',
HTML_DOCTYPE_COLOR: 'rgb(192, 192, 192)',

ARROW_COLOR: 'rgb(145, 145, 145)',
ARROW_MARGIN_RIGHT: 3,
ARROW_FONT_SIZE: 12,

// Each <li>
TREENODE_FONT_FAMILY: 'Fira code, monospace',
TREENODE_FONT_SIZE: '13px',
TREENODE_LINE_HEIGHT: '1.7',
TREENODE_PADDING_LEFT: 24,

TABLE_BORDER_COLOR: 'rgb(85, 85, 85)',
TABLE_TH_BACKGROUND_COLOR: 'rgb(44, 44, 44)',
TABLE_TH_HOVER_COLOR: 'rgb(48, 48, 48)',
TABLE_SORT_ICON_COLOR: 'black',
TABLE_DATA_BACKGROUND_IMAGE:
'linear-gradient(rgba(255, 255, 255, 0), rgba(255, 255, 255, 0) 50%, rgba(51, 139, 255, 0.0980392) 50%, rgba(51, 139, 255, 0.0980392))',
TABLE_DATA_BACKGROUND_SIZE: '128px 32px'
}
File renamed without changes.
@@ -0,0 +1,9 @@
export const PANEL_SETTINGS = [
{ size: '50', resize: 'stretch' },
{ size: '50', resize: 'stretch' }
]

export const ROW_PANEL_SETTINGS = [
{ size: '50%', minSize: 500, resize: 'stretch' },
{ size: '50%', minSize: 500, resize: 'stretch' }
]
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,56 @@
import { types, flow, getParent } from 'mobx-state-tree'
import queryString from 'query-string'

import { auth } from './auth0'

const model = {
authenticated: types.optional(types.boolean, false),
accessToken: types.optional(types.string, ''),
expiresIn: types.optional(types.number, 0),
idToken: types.optional(types.string, ''),
state: types.optional(types.string, ''),
tokenType: types.optional(types.string, '')
}

const actions = (self) => {
return {
logIn() {
auth.logIn()
},

logOut() {
auth.logOut()
},

setAuthData(authData, user) {
self.authenticated = true
self.accessToken = authData.accessToken
self.expiresIn = authData.expiresIn
self.tokenType = authData.tokenType
self.idToken = authData.idToken
self.state = authData.state

auth.setLocalStorageAuth(authData)
getParent(self, 1).user.setData(user)
}
}
}

const views = (self) => {
return {
get isAuthenticated() {
return self.authenticated
? new Date().getTime() < JSON.parse(localStorage.getItem('expires_at'))
: false
},

get auth0() {
return auth
}
}
}

export default types
.model(model)
.actions(actions)
.views(views)
@@ -0,0 +1,42 @@
import auth0 from 'auth0-js'

class Auth {
auth0 = new auth0.WebAuth({
domain: 'bytesized.auth0.com',
clientID: 'wt4u1X_a7fPOkQ_3RUDJoHdMvFSyOGPo',
redirectUri: 'http://localhost:9000/authenticating',
audience: 'https://bytesized.auth0.com/userinfo',
responseType: 'token id_token',
scope: 'openid profile'
})

logIn = () => {
this.auth0.authorize()
}

logOut = () => {
localStorage.removeItem('accessToken')
localStorage.removeItem('idToken')
localStorage.removeItem('expires_at')
window.location.assign('/')
}

handleAuth = () => {
return this.auth0.parseHash((err, authResult) => {
return err ? { error: err } : authResult
})
}

setLocalStorageAuth = (authData) => {
localStorage.setItem('accessToken', authData.accessToken)
localStorage.setItem('idToken', authData.idToken)
localStorage.setItem('userData', JSON.stringify(authData.idTokenPayload))

localStorage.setItem(
'expires_at',
JSON.stringify(authData.expiresIn * 1000 + new Date().getTime())
)
}
}

export const auth = new Auth()
File renamed without changes.
@@ -0,0 +1,17 @@
import { types } from 'mobx-state-tree'
import queryString from 'query-string'

const model = {}

const actions = (self) => {
return {}
}

const views = (self) => {
return {}
}

export default types
.model(model)
.actions(actions)
.views(views)
File renamed without changes.
@@ -0,0 +1,34 @@
import { types, flow } from 'mobx-state-tree'
import { prettier } from '#utilities/api/prettier'

import { INFINITE_LOOP_DEFAULT_CONTENT } from './consts'

const model = {
content: types.optional(types.string, '// placeholder')
}

const actions = (self) => {
const history = []

return {
setContent(content) {
self.content = content
},

formatContent: flow(function*() {
const { code, error } = yield prettier(self.content)
self.setContent(code)
})
}
}

const views = (self) => ({
get characterCount() {
return self.content.length
}
})

export default types
.model(model)
.actions(actions)
.views(views)
File renamed without changes.
@@ -0,0 +1,65 @@
import { types, getParent } from 'mobx-state-tree'
import { autorun } from 'mobx'
import uuid from 'uuid/v4'
import { Editor, EditorState } from 'draft-js'

const model = {
uid: types.optional(types.string, () => uuid()),
moduleUid: types.optional(types.string, '0'),
editing: types.optional(types.boolean, false),
previewing: types.optional(types.boolean, false),
editedContent: types.optional(types.string, () => '# hello'),
content: types.optional(types.string, () => '# hello')
}

const actions = (self) => {
return {
toggleEditing() {
const editing = !self.editing
self.editing = editing
editing && (self.previewing = false)
},

togglePreviewing() {
self.previewing = !self.previewing
},

setContent(content) {
self.editing
? (self.editedContent = content.markdown)
: (self.content = content.markdown)
},

setModuleUid(uid) {
console.log('setting module uid', uid)
self.moduleUid = String(uid)
},

saveContent() {
self.editing = false
self.content = self.editedContent
// console.log('SAVED')
}
}
}

const views = (self) => {
return {
get markdown() {
return self.editing ? self.editedContent : self.content
},

// TODO: Figure out why this won't work as a view.
isEditableByUser() {
return getParent(self, 1).user.modules.some((module) => {
return module.uid == self.moduleUid
})
// return JSON.parse(localStorage.getItem('userData')).uid
}
}
}

export const LessonState = types
.model(model)
.actions(actions)
.views(views)
File renamed without changes.
@@ -3,9 +3,9 @@ import { autorun } from 'mobx'
import uuid from 'uuid/v4'

const model = {
uid: types.optional(types.string, () => uuid()),
type: types.string,
message: types.frozen
uid: types.optional(types.string, () => uuid()),
type: types.string,
message: types.frozen
}

export const LogState = types.model(model)
File renamed without changes.
@@ -12,20 +12,20 @@ console.log({ EditorState, LessonState, OutputState, UserState, AuthState })
import * as common from './common'

const model = {
editor: types.optional(EditorState, {}),
lesson: types.optional(LessonState, {}),
output: types.optional(OutputState, {}),
user: types.optional(UserState, {}),
auth: types.optional(AuthState, {})
editor: types.optional(EditorState, {}),
lesson: types.optional(LessonState, {}),
output: types.optional(OutputState, {}),
user: types.optional(UserState, {}),
auth: types.optional(AuthState, {})
}

const actions = (self) => ({
activateModule: common.activateModule(self)
activateModule: common.activateModule(self)
})

export const MainState = makeInspectable(
types
.model(model)
.actions(actions)
.create({})
types
.model(model)
.actions(actions)
.create({})
)
@@ -0,0 +1,14 @@
import { flow } from 'mobx-state-tree'
import * as utilities from '../utilities'

export const activateModule = (self) =>
flow(function*(options) {
const response = yield fetch(utilities.modulePath(options))
const data = yield response.json()

console.log('activateModule', { data })

self.editor.setContent(data.module.editorContent)
self.lesson.setContent(data.module.lessonContent)
self.lesson.setModuleUid(data.module.uid)
})
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,3 @@
export const modulePath = ({ userName, id }) => {
return '$SERVER_ADDRESS$$API_PATH$/module/' + userName + '/' + id
}
@@ -0,0 +1,23 @@
import { types } from 'mobx-state-tree'
import queryString from 'query-string'

const model = {
editorContent: types.optional(types.string, ''),
lessonContent: types.optional(types.string, ''),
title: types.optional(types.string, ''),
uid: types.optional(types.number, 0),
_id: types.optional(types.string, '')
}

const actions = (self) => {
return {}
}

const views = (self) => {
return {}
}

export default types
.model(model)
.actions(actions)
.views(views)
File renamed without changes.
@@ -0,0 +1,108 @@
import { types } from 'mobx-state-tree'
import { autorun } from 'mobx'

import moment from 'moment'
import { applyPatch } from 'mobx-state-tree'
import { LogState } from '../Log'

const model = {
logs: types.optional(types.array(LogState), [])
}

const actions = (self) => {
return {
clearLogs() {
self.logs.forEach((log) => self.logs.pop())
},

updateLogs(newLogs) {
newLogs.forEach((log) => {
log.messages.forEach((message) => {
if (self.logs.length > 35) {
self.logs.shift()
}

self.logs.push({
type: log.type,
message: message
})
})
})
},

removeOldestLog() {
if (self.logsCount > 50) {
self.logs.shift()
}
},

addLog(log) {
log.messages.forEach((message) => {
self.logs.push({
type: log.type,
message: message
})
})
},

addInfoLog(log) {
self.logs.push({
type: 'INFO',
message: log
})
},

addErrorLog(error) {
self.logs.push({
type: 'ERROR',
message: error
})
},

addExecutionLog() {
self.logs.push({
type: 'INFO',
message: `[${moment().format('h:mm:ss')}] executing`
})
},

addSocketNotConnectedLog() {
self.logs.push({
type: 'WARNING',
message: `[${moment().format('h:mm:ss')}] socket not connected`
})
},

addSocketDisconnectLog() {
self.logs.push({
type: 'WARNING',
message: `[${moment().format('h:mm:ss')}] socket disconnected`
})
},

addCustomLog(type, message) {
self.logs.push({
type: type,
message
})
}
}
}

const views = (self) => ({
get logCount() {
return self.logs.length
},

get textValue() {
return self.logs.reduce((final, log) => {
final += `${log.value}\n`
return final
}, '')
}
})

export const OutputState = types
.model(model)
.actions(actions)
.views(views)
File renamed without changes.
@@ -0,0 +1,44 @@
import { types, flow } from 'mobx-state-tree'

import { ModuleState } from '../Module'

const model = {
nickname: types.optional(types.string, ''),
name: types.optional(types.string, ''),
picture: types.optional(types.string, ''),
updated_at: types.optional(types.string, ''),
iss: types.optional(types.string, ''),
sub: types.optional(types.string, ''),
aud: types.optional(types.string, ''),
iat: types.optional(types.number, 0),
exp: types.optional(types.number, 0),
at_hash: types.optional(types.string, ''),
nonce: types.optional(types.string, ''),
userName: types.optional(types.string, ''),
uid: types.optional(types.string, ''),
_id: types.optional(types.string, ''),
modules: types.optional(types.array(ModuleState), [])
}

const actions = (self) => {
return {
setData(user) {
self.userName = user.userName
self.uid = String(user.uid)
self._id = String(user._id)

user.modules.forEach((module) => {
self.modules.push(ModuleState.create(module))
})
}
}
}

const views = (self) => {
return {}
}

export default types
.model(model)
.actions(actions)
.views(views)
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,24 @@
export const auth = (tree) => {
return { $auth: tree.state.auth }
}

export const user = (tree) => {
return { $user: tree.state.auth.user }
}

export const editor = (tree) => {
return { $editor: tree.state.editor }
}

export const output = (tree) => {
return { $output: tree.state.editor.output }
}

export const select = (selectors: []) => {
return (tree) => {
return selectors.reduce((final, selector) => {
final = { ...final, ...selector(tree) }
return final
}, {})
}
}
@@ -0,0 +1,34 @@
@import './variables.css';
@import './themes.css';

* {
box-sizing: border-box;
padding: 0;
margin: 0;
}

:global #mountPoint {
background: #f2f2f2;
width: 100vw;
height: 100vh;
max-width: 100vw;
max-height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}

body {
width: 100vw;
height: 100vh;
max-width: 100vw;
max-height: 100vh;
display: flex;
flex-direction: column;
font-family: 'Raleway', 'Open Sans', sans-serif;
}

a {
text-decoration: none;
color: inherit;
}
@@ -0,0 +1,20 @@
Size(width, height = width) {
width: width;
height: height;
}

Flex($dir, $justify, $align) {
display: flex;
flex-direction: $dir;
justify-content: $justify;
align-items: $align;
}

Font(which) {
if (which is button) {
font-family: 'Open Sans', sans-serif;
text-transform: uppercase;
letter-spacing: 2px;
font-size: 12px;
}
}
File renamed without changes.
Empty file.
@@ -0,0 +1,252 @@
@import '~codemirror/lib/codemirror.css';
@import '~codemirror/theme/material.css';

* {
box-sizing: border-box;
margin: 0;
padding: 0;
}

html,
body,
#root {
}

#root {
font-feature-settings: 'liga' 1;
}

/* .CodeMirror-vscrollbar::-webkit-scrollbar {
background-color: rgba(0,0,0,0.15);
width: 100%;
}

*/

.CodeMirror-vscrollbar::-webkit-scrollbar {
background-color: rgba(0, 0, 0, 0.1);
}

.CodeMirror-vscrollbar::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.2);
}

#cool-widget {
background: #e1def1;
position: absolute;
width: 100%;
color: var(--black);
height: 24px;
padding: 0 16px;
font-family: 'Open Sans';
}

.CodeMirror {
/* background: radial-gradient(circle at 50% -70%, #57589e40, transparent 65%), */
/* linear-gradient(200deg, #3b3c6b -10%, #292947); */
/* background: linear-gradient(200deg, #3B3C6B -10%, #292947); */
background-size: 100%;
padding: 0px 0;
/* background: #32335a; */
background: #292d45;
/* font-family: 'Overpass Mono', monospace !important; */
/* font-family: 'Inconsolata', monospace !important; */
/* font-family: 'Source Code Pro', monospace !important; */
/* font-family: 'Overpass Mono', monospace !important; */
font-family: 'Fira Mono', monospace !important;
/* font-family: 'Roboto Mono', monospace !important; */
font-size: 14px;
font-weight: 500;
letter-spacing: 0.75px;
line-height: 1.7;
text-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25);
}

.CodeMirror-code {
/* padding: 8px 0 0 0; */
}

.CodeMirror-code div pre.CodeMirror-line {
left: 2px;
position: relative;
}

.cm-matchhighlight {
background: #4f4c62;
}

.CodeMirror-gutters {
background: var(--bgColor);
border-right: none !important;
/* padding-right: 2px; */
padding: 4px;
background: transparent;
}

.CodeMirror-code div pre.CodeMirror-line {
padding: 0 0 0 8px;
}

.CodeMirror-cursors {
color: #8e74ca;
}

.cm-s-shit .CodeMirror-selected {
background: #1d1f313b;
}

.cm-s-shit .CodeMirror-activeline-gutter {
background: #1d1f313b;
}

.CodeMirror-linenumbers {
display: flex;
justify-content: center;
text-align: center;
background: transparent;
}

.bytesize-CodeMirror {
height: 100%;
max-height: 100%;
/* overflow-y: scroll; */
}
/* delimiter tokens */
.cm-s-shit {
color: #ffffff;
height: 100%;
max-height: 100%;
}

/* let, const, if, else, this, new, function ... */
.cm-s-shit .cm-keyword {
color: #b475ff;
/* font-style: oblique; */

/* color: #465d83; */
}

/* e.g. #id */
.cm-s-shit .cm-atom {
color: #00f3f3;
}

/* e.g. 20 */
.cm-s-shit .cm-number {
color: #f7f616;
}

.cm-s-shit .CodeMirror-cursor {
/* border-color: #49499c; */
border-color: #02ebec;
}

.cm-s-shit .CodeMirror-activeline-background {
/* background: linear-gradient(45deg, rgba(32, 34, 54, 0), rgba(32, 34, 54, 0.25)); */
/* margin: -1px; */
/* box-shadow: 0 0 8px rgba(0,0,0,0.025); */
background: #1d1f313b;
}

/* e.g. px, em */
.cm-s-shit .cm-unit {
color: #ff0000;
}

.cm-s-shit .cm-type {
color: green;
}

/* identifiers */
.cm-s-shit .cm-def {
color: #fff;
font-weight: 600;
}

/* e.g. $, i */
.cm-s-shit .cm-variable {
color: #ff68c4;
}

/* e.g. for key in [this], Markdown li */
.cm-s-shit .cm-variable-2 {
color: #ff9d14;
}

/* e.g. getElementbyId, CSS properties */
.cm-s-shit .cm-property {
color: #709eff;
/* color: #ff506d; */
}

/* e.g. CoffeeScript ->, CSS colons */
.cm-s-shit .cm-operator {
color: #729cf3;
font-weight: 700;
/* text-shadow: 0px 1px 16px rgba(38, 247, 230, 0.25); */
}

.ofh .cm-s-shit.CodeMirror {
overflow: hidden;
height: 100%;
}

/* e.g. // comments */
.cm-s-shit .cm-comment {
color: #5b4e8b;
}

/* "strings" and 'strings' */
.cm-s-shit .cm-string {
color: #76f0dd;
}

/* `template strings */
.cm-s-shit .cm-string-2 {
color: #84d8ff;
}

/* e.g. @font-face */
.cm-s-shit .cm-meta {
color: #79ffa5;
}

/* */
.cm-s-shit .cm-header {
color: #49d320;
}

/* e.g. HTML elements */
.cm-s-shit .cm-tag {
color: #60ee02;
}

/* e.g. class="" id="" */
.cm-s-shit .cm-attribute {
color: #17eb04;
}

/* not sure if used */
.cm-s-shit .cm-strong {
color: #345038;
}

/* not sure if used */
.cm-s-shit .cm-em {
color: #2aee24;
}

/* e.g. .box */
.cm-s-shit .cm-qualifier {
color: #51e624;
}

/* not sure if used */
.cm-s-shit .cm-builtin {
color: #16f016;
}

.CodeMirror-linenumber {
color: #5c5a96;
opacity: 0.75;
}
@@ -0,0 +1,222 @@
@import '~codemirror/lib/codemirror.css';
@import '~codemirror/theme/material.css';

* {
box-sizing: border-box;
margin: 0;
padding: 0;
}

html,
body,
#root {
}

#root {
font-feature-settings: 'liga' 1;
}

/* TODO: Make specific to terminal */
.CodeMirror-vscrollbar::-webkit-scrollbar {
background-color: rgba(0, 0, 0, 0.1);
}

.CodeMirror-vscrollbar::-webkit-scrollbar-thumb {
background-color: rgb(46, 40, 67);
}

#cool-widget {
background: #e1def1;
position: absolute;
width: 100%;
color: var(--black);
height: 24px;
padding: 0 16px;
font-family: 'Open Sans';
}

.CodeMirror {
background: linear-gradient(215deg, #3b3c64, #1d1f31);
background-size: 100%;
padding: 0px 0;
font-family: 'Overpass Mono', monospace !important;
font-size: 14px;
font-weight: 300;
letter-spacing: 1px;
line-height: 1.75;
}

.CodeMirror-code {
/* padding: 8px 0 0 0; */
}

.CodeMirror-code div pre.CodeMirror-line {
left: 2px;
position: relative;
}

.cm-matchhighlight {
background: #4f4c62;
}

.CodeMirror-gutters {
background: transparent;
border-right: none !important;
/* padding-right: 2px; */
padding: 4px;
}

.CodeMirror-cursors {
color: #8e74ca;
}

.cm-s-shellShit .CodeMirror-selected {
background: #382b54 !important;
}

.CodeMirror-linenumbers {
display: flex;
justify-content: center;
text-align: center;
background: transparent;
}

.bytesize-CodeMirror-shell {
height: 100%;
max-height: 100%;
padding: 0 0 0 16px;
/* overflow-y: scroll; */
}

/* delimiter tokens */
.cm-s-shellShit {
color: #ffffff;
background: transparent;
height: 100%;
max-height: 100%;
/* margin: 4px 0; */
padding: 8px 0;
}

/* let, const, if, else, this, new, function ... */
.cm-s-shellShit .cm-keyword {
color: #5e5eb9;
font-style: oblique;

/* color: #465d83; */
}

/* e.g. #id */
.cm-s-shellShit .cm-atom {
color: #00f3f3;
}

/* e.g. 20 */
.cm-s-shellShit .cm-number {
color: #fc41ae;
}

.cm-s-shellShit .CodeMirror-cursor {
border-color: transparent;
}

.cm-s-shellShit .CodeMirror-activeline-background {
background: #2f2845;
margin: -1px;
box-shadow: 0 0 12px rgba(0, 0, 0, 0.05);
}

/* e.g. px, em */
.cm-s-shellShit .cm-unit {
color: #ff0000;
}

.cm-s-shellShit .cm-type {
color: green;
}

/* identifiers */
.cm-s-shellShit .cm-def {
color: #cf6e9c;
}

/* e.g. $, i */
.cm-s-shellShit .cm-variable {
color: #b173eb;
}

/* e.g. for key in [this], Markdown li */
.cm-s-shellShit .cm-variable-2 {
color: #fd8738;
}

/* e.g. getElementbyId, CSS properties */
.cm-s-shellShit .cm-property {
color: #ff506d;
}

/* e.g. CoffeeScript ->, CSS colons */
.cm-s-shellShit .cm-operator {
color: #5e5eb9;
/* text-shadow: 0px 1px 16px rgba(38, 247, 230, 0.25); */
}

/* e.g. // comments */
.cm-s-shellShit .cm-comment {
color: #5b4e8b;
}

/* "strings" and 'strings' */
.cm-s-shellShit .cm-string {
color: #55ece0;
}

/* `template strings */
.cm-s-shellShit .cm-string-2 {
color: #84d8ff;
}

/* e.g. @font-face */
.cm-s-shellShit .cm-meta {
color: #79ffa5;
}

/* */
.cm-s-shellShit .cm-header {
color: #49d320;
}

/* e.g. HTML elements */
.cm-s-shellShit .cm-tag {
color: #60ee02;
}

/* e.g. class="" id="" */
.cm-s-shellShit .cm-attribute {
color: #17eb04;
}

/* not sure if used */
.cm-s-shellShit .cm-strong {
color: #345038;
}

/* not sure if used */
.cm-s-shellShit .cm-em {
color: #2aee24;
}

/* e.g. .box */
.cm-s-shellShit .cm-qualifier {
color: #51e624;
}

/* not sure if used */
.cm-s-shellShit .cm-builtin {
color: #16f016;
}

.CodeMirror-linenumber {
color: #978bb8;
opacity: 0.75;
}
@@ -0,0 +1,330 @@
.mde-header {
padding: 10px;
}
.mde-header button:focus {
outline: 0;
}
.mde-header ul.mde-header-group {
display: inline-block;
margin: 0 1rem 0 0;
padding: 0;
list-style: none;
}
.mde-header ul.mde-header-group li.mde-header-item {
display: inline-block;
position: relative;
margin: 0 4px;
}
.mde-header ul.mde-header-group li.mde-header-item button {
text-align: left;
cursor: pointer;
height: 22px;
padding: 4px;
margin: 0;
border: none;
background: none;
color: #242729;
}

@keyframes tooltip-appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.mde-header
ul.mde-header-group
li.mde-header-item
button.tooltipped:hover::before {
animation-name: tooltip-appear;
animation-duration: 0.2s;
animation-delay: 0.5s;
animation-fill-mode: forwards;
opacity: 0;
position: absolute;
z-index: 1000001;
width: 0;
height: 0;
color: rgba(0, 0, 0, 0.8);
pointer-events: none;
content: '';
border: 5px solid transparent;
top: -5px;
right: 50%;
bottom: auto;
margin-right: -5px;
border-top-color: rgba(0, 0, 0, 0.8);
}
.mde-header
ul.mde-header-group
li.mde-header-item
button.tooltipped:hover::after {
animation-name: tooltip-appear;
animation-duration: 0.2s;
animation-delay: 0.5s;
animation-fill-mode: forwards;
font-size: 11px;
opacity: 0;
position: absolute;
z-index: 1000000;
padding: 5px 8px;
color: #fff;
pointer-events: none;
content: attr(aria-label);
background: rgba(0, 0, 0, 0.8);
border-radius: 3px;
right: 50%;
bottom: 100%;
transform: translateX(50%);
margin-bottom: 5px;
white-space: nowrap;
}
.mde-header ul.mde-header-group li.mde-header-item ul.react-mde-dropdown {
position: absolute;
left: 0;
top: 30px;
background-color: white;
border: 1px solid #c8ccd0;
padding: 5px;
z-index: 2;
transform: translateX(-9px);
}
.mde-header ul.mde-header-group li.mde-header-item ul.react-mde-dropdown li {
margin: 0;
white-space: nowrap;
list-style: none;
display: block;
}
.mde-header
ul.mde-header-group
li.mde-header-item
ul.react-mde-dropdown
li
button {
display: block;
}
.mde-header
ul.mde-header-group
li.mde-header-item
ul.react-mde-dropdown
li
button
p {
display: block;
margin: 0;
padding: 0;
font-weight: bold;
line-height: 1em;
background: none;
border: 0;
text-align: left;
}
.mde-header
ul.mde-header-group
li.mde-header-item
ul.react-mde-dropdown
li
button
p:hover {
color: #4078c0;
}
.mde-header
ul.mde-header-group
li.mde-header-item
ul.react-mde-dropdown
li
button
p.header-1 {
font-size: 20px;
}
.mde-header
ul.mde-header-group
li.mde-header-item
ul.react-mde-dropdown
li
button
p.header-2 {
font-size: 18px;
}
.mde-header
ul.mde-header-group
li.mde-header-item
ul.react-mde-dropdown
li
button
p.header-3 {
font-size: 14px;
}
.mde-header
ul.mde-header-group
li.mde-header-item
ul.react-mde-dropdown::before {
position: absolute;
content: '';
width: 0;
height: 0;
border: 8px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.15);
top: -16px;
left: 3px;
transform: translateX(50%);
}
.mde-header
ul.mde-header-group
li.mde-header-item
ul.react-mde-dropdown::after {
position: absolute;
content: '';
width: 0;
height: 0;
border: 7px solid transparent;
border-bottom-color: white;
top: -14px;
left: 5px;
transform: translateX(50%);
}

.mde-text {
border-width: 1px 0 1px 0;
border-style: solid;
border-color: #c8ccd0;
}
.mde-text .public-DraftEditor-content {
box-sizing: border-box;
width: 100%;
min-height: 200px;
max-height: 200px;
padding: 10px;
overflow-y: scroll;
}

.mde-preview .mde-preview-content {
padding: 10px 10px 20px 10px;
}
.mde-preview .mde-preview-content p,
.mde-preview .mde-preview-content blockquote,
.mde-preview .mde-preview-content ul,
.mde-preview .mde-preview-content ol,
.mde-preview .mde-preview-content dl,
.mde-preview .mde-preview-content table,
.mde-preview .mde-preview-content pre {
margin-top: 0;
margin-bottom: 16px;
}
.mde-preview .mde-preview-content h1,
.mde-preview .mde-preview-content h2,
.mde-preview .mde-preview-content h3 {
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
border-bottom: 1px solid #eee;
padding-bottom: 0.3em;
}
.mde-preview .mde-preview-content h1 {
font-size: 1.6em;
}
.mde-preview .mde-preview-content h2 {
font-size: 1.4em;
}
.mde-preview .mde-preview-content h3 {
font-size: 1.2em;
}
.mde-preview .mde-preview-content ul,
.mde-preview .mde-preview-content ol {
padding-left: 2em;
}
.mde-preview .mde-preview-content blockquote {
margin-left: 0;
padding: 0 1em;
color: #777;
border-left: 0.25em solid #ddd;
}
.mde-preview .mde-preview-content blockquote > :first-child {
margin-top: 0;
}
.mde-preview .mde-preview-content blockquote > :last-child {
margin-bottom: 0;
}
.mde-preview .mde-preview-content code {
padding: 0.2em 0 0.2em 0;
margin: 0;
font-size: 90%;
background-color: rgba(0, 0, 0, 0.04);
border-radius: 3px;
}
.mde-preview .mde-preview-content code::before,
.mde-preview .mde-preview-content code::after {
letter-spacing: -0.2em;
content: '\00a0';
}
.mde-preview .mde-preview-content pre {
padding: 16px;
overflow: auto;
font-size: 85%;
line-height: 1.45;
background-color: #f7f7f7;
border-radius: 3px;
}
.mde-preview .mde-preview-content pre code {
display: inline;
padding: 0;
margin: 0;
overflow: visible;
line-height: inherit;
word-wrap: normal;
background-color: transparent;
border: 0;
}
.mde-preview .mde-preview-content pre code::before,
.mde-preview .mde-preview-content pre code::after {
content: none;
}
.mde-preview .mde-preview-content pre > code {
padding: 0;
margin: 0;
font-size: 100%;
word-break: normal;
white-space: pre;
background: transparent;
border: 0;
}
.mde-preview .mde-preview-content a {
color: #4078c0;
text-decoration: none;
}
.mde-preview .mde-preview-content a:hover {
text-decoration: underline;
}
.mde-preview .mde-preview-content > *:first-child {
margin-top: 0 !important;
}
.mde-preview .mde-preview-content > *:last-child {
margin-bottom: 0 !important;
}
.mde-preview .mde-preview-content::after {
display: table;
clear: both;
content: '';
}
.mde-preview .mde-preview-content table {
display: block;
width: 100%;
border-spacing: 0;
border-collapse: collapse;
}
.mde-preview .mde-preview-content table thead th {
font-weight: bold;
}
.mde-preview .mde-preview-content table th,
.mde-preview .mde-preview-content table td {
padding: 6px 13px;
border: 1px solid #c8ccd0;
}

.react-mde {
border: 1px solid #c8ccd0;
border-radius: 2px;
}
@@ -0,0 +1,9 @@
:global [data-dark-theme] {
background: #202231;
color: #ffffff;
}

:global [data-white-theme] {
background: #ffffff;
color: #202231;
}
@@ -0,0 +1,18 @@
:root {
/* --black: #18161b; */
/* --black: #1a1a20; */
/* --black: #180e22; */
--black: #191726;
--otherBlack: #131116;
--gray: #1e1e1e;
--purple0: #612bd3;
}

:root {
/* --bgColor: #1d1922; */
--bgColor: linear-gradient(-150deg, rgb(47, 41, 68), rgb(24, 27, 43));
--bgColorReverse: linear-gradient(150deg, rgb(41, 44, 68), rgb(24, 27, 43));
--darkBlue0: #181b2b;
--darkBlue1: #272c42;
--shadow0: 0px 0px 8px 0px rgba(24, 27, 43, 1);
}
@@ -0,0 +1,10 @@
import * as React from 'react'
import { inject, observer } from 'mobx-react'

export class StateInjector extends React.Component {
render() {
const Injector = inject(this.props.selector)(observer(this.props.children))
Injector.displayName = 'StateInjector'
return <Injector {...this.props} />
}
}
@@ -0,0 +1,16 @@
const headers = new Headers({
'Content-Type': 'application/json'
})

export const prettier = async (code) => {
const response = await fetch('$SERVER_ADDRESS$$API_PATH$/prettier', {
headers,
method: 'POST',
body: JSON.stringify({
config: null,
code: code
})
})

return await response.json()
}
@@ -0,0 +1,33 @@
import React from 'react'
import { observable, action, computed } from 'mobx'
import { observer, inject } from 'mobx-react'

const withObservableData = (component) => {
component.hasOwnProperty('$data') &&
(component.$data = observable.box(component.$data))
return component
}

export const createComponent = (creator) => {
return inject('state')((props) => {
const component = new React.Component(props)

Object.defineProperty(component, 'reactiveData', {
enumerable: false,
value: (data) => {
component.data = observable(data)
return component.data
}
})

Object.defineProperty(component, 'action', {
enumerable: false,
value: (func) => {
return action((...args) => func(...args))
}
})

component.render = creator(component)
return observer(component)
})
}
Empty file.
@@ -0,0 +1,16 @@
export const gorgeous = async (code) => {
const response = await fetch('https://gorgeous-jvkoayanir.now.sh/', {
headers: new Headers({
'Content-Type': 'application/json'
}),
method: 'POST',
body: JSON.stringify({
config: null,
code
})
})

const json = await response.json()
// json.error && (throw new Error(json.error))
return json.pretty
}
@@ -0,0 +1,3 @@
export const lastInArray = (target: any) => {
return target[target.length - 1]
}