feat: Upgrade editor (#1227)

* WIP

* document migration

* fix: Handle clashing keyboard events

* fix: convert getSummary

* fix: parseDocumentIds

* lint

* fix: Remove unused plugin

* Move editor version to header
Add editor version check for API endpoints

* fix: Editor update auto-reload
Bump RME

* test

* bump rme

* Remove slate flow types, improve themeing, bump rme

* bump rme

* fix: parseDocumentIds returning duplicate ID's, improved regression tests

* test

* fix: Missing code styles

* lint

* chore: Upgrade v2 migration to use AST

* Bump RME

* Update welcome doc

* add highlight to keyboard shortcuts ref

* theming improvements

* fix: Code comments show as headings, closes #1255

* loop

* fix: TOC highlighting

* lint

* add: Automated backup of docs before migration

* Update embeds to new format

* fix: React warning

* bump to final editor version 10.0.0

* test
This commit is contained in:
Tom Moor 2020-05-19 20:39:34 -07:00 committed by GitHub
parent 400a1c87bb
commit 9274005cbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
97 changed files with 1199 additions and 3266 deletions

View File

@ -9,10 +9,6 @@
.*/node_modules/polished/.* .*/node_modules/polished/.*
.*/node_modules/react-side-effect/.* .*/node_modules/react-side-effect/.*
.*/node_modules/fbjs/.* .*/node_modules/fbjs/.*
.*/node_modules/slate-edit-code/example/.*
.*/node_modules/slate-edit-code/lib/.*
.*/node_modules/slate-edit-list/.*
.*/node_modules/slate-prism/.*
.*/node_modules/config-chain/.* .*/node_modules/config-chain/.*
.*/server/scripts/.* .*/server/scripts/.*
*.test.js *.test.js

View File

@ -109,7 +109,7 @@ Outline is composed of separate backend and frontend application which are both
Outline's frontend is a React application compiled with [Webpack](https://webpack.js.org/). It uses [Mobx](https://mobx.js.org/) for state management and [Styled Components](https://www.styled-components.com/) for component styles. Unless global, state logic and styles are always co-located with React components together with their subcomponents to make the component tree easier to manage. Outline's frontend is a React application compiled with [Webpack](https://webpack.js.org/). It uses [Mobx](https://mobx.js.org/) for state management and [Styled Components](https://www.styled-components.com/) for component styles. Unless global, state logic and styles are always co-located with React components together with their subcomponents to make the component tree easier to manage.
The editor itself is built ontop of [Slate](https://github.com/ianstormtaylor/slate) and hosted in a separate repository to encourage reuse: [rich-markdown-editor](https://github.com/outline/rich-markdown-editor) The editor itself is built on [Prosemirror](https://github.com/prosemirror) and hosted in a separate repository to encourage reuse: [rich-markdown-editor](https://github.com/outline/rich-markdown-editor)
- `app/` - Frontend React application - `app/` - Frontend React application
- `app/scenes` - Full page views - `app/scenes` - Full page views

View File

@ -4,16 +4,16 @@ import { withRouter, type RouterHistory } from 'react-router-dom';
import { observable } from 'mobx'; import { observable } from 'mobx';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { lighten } from 'polished'; import { lighten } from 'polished';
import styled, { withTheme, createGlobalStyle } from 'styled-components'; import styled, { withTheme } from 'styled-components';
import RichMarkdownEditor from 'rich-markdown-editor'; import RichMarkdownEditor from 'rich-markdown-editor';
import Placeholder from 'rich-markdown-editor/lib/components/Placeholder';
import { uploadFile } from 'utils/uploadFile'; import { uploadFile } from 'utils/uploadFile';
import isInternalUrl from 'utils/isInternalUrl'; import isInternalUrl from 'utils/isInternalUrl';
import Tooltip from 'components/Tooltip'; import Tooltip from 'components/Tooltip';
import UiStore from 'stores/UiStore'; import UiStore from 'stores/UiStore';
import Embed from './Embed';
import embeds from '../../embeds'; import embeds from '../../embeds';
const EMPTY_ARRAY = [];
type Props = { type Props = {
id: string, id: string,
defaultValue?: string, defaultValue?: string,
@ -65,37 +65,17 @@ class Editor extends React.Component<Props> {
this.props.ui.showToast(message); this.props.ui.showToast(message);
}; };
getLinkComponent = node => {
if (this.props.disableEmbeds) return;
const url = node.data.get('href');
const keys = Object.keys(embeds);
for (const key of keys) {
const component = embeds[key];
for (const host of component.ENABLED) {
const matches = url.match(host);
if (matches) return Embed;
}
}
};
render() { render() {
return ( return (
<React.Fragment> <StyledEditor
<PrismStyles /> ref={this.props.forwardedRef}
<StyledEditor uploadImage={this.onUploadImage}
ref={this.props.forwardedRef} onClickLink={this.onClickLink}
uploadImage={this.onUploadImage} onShowToast={this.onShowToast}
onClickLink={this.onClickLink} embeds={this.props.disableEmbeds ? EMPTY_ARRAY : embeds}
onShowToast={this.onShowToast} tooltip={EditorTooltip}
getLinkComponent={this.getLinkComponent} {...this.props}
tooltip={EditorTooltip} />
toc={false}
{...this.props}
/>
</React.Fragment>
); );
} }
} }
@ -108,17 +88,6 @@ const StyledEditor = styled(RichMarkdownEditor)`
transition: ${props => props.theme.backgroundTransition}; transition: ${props => props.theme.backgroundTransition};
} }
p {
${Placeholder} {
visibility: hidden;
}
}
p:nth-child(1):last-child {
${Placeholder} {
visibility: visible;
}
}
p { p {
a { a {
color: ${props => props.theme.link}; color: ${props => props.theme.link};
@ -131,157 +100,6 @@ const StyledEditor = styled(RichMarkdownEditor)`
} }
} }
} }
h1:first-child,
h2:first-child,
h3:first-child,
h4:first-child,
h5:first-child,
h6:first-child {
margin-top: 0;
}
`;
/*
Based on Prism template by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/prism/)
Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)
*/
const PrismStyles = createGlobalStyle`
code[class*="language-"],
pre[class*="language-"] {
-webkit-font-smoothing: initial;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 13px;
line-height: 1.375;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
color: #24292e;
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #6a737d;
}
.token.punctuation {
color: #5e6687;
}
.token.namespace {
opacity: .7;
}
.token.operator,
.token.boolean,
.token.number {
color: #d73a49;
}
.token.property {
color: #c08b30;
}
.token.tag {
color: #3d8fd1;
}
.token.string {
color: #032f62;
}
.token.selector {
color: #6679cc;
}
.token.attr-name {
color: #c76b29;
}
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #22a2c9;
}
.token.attr-value,
.token.keyword,
.token.control,
.token.directive,
.token.unit {
color: #d73a49;
}
.token.function {
color: #6f42c1;
}
.token.statement,
.token.regex,
.token.atrule {
color: #22a2c9;
}
.token.placeholder,
.token.variable {
color: #3d8fd1;
}
.token.deleted {
text-decoration: line-through;
}
.token.inserted {
border-bottom: 1px dotted #202746;
text-decoration: none;
}
.token.italic {
font-style: italic;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.important {
color: #c94922;
}
.token.entity {
cursor: help;
}
pre > code.highlight {
outline: 0.4em solid #c94922;
outline-offset: .4em;
}
`; `;
const EditorTooltip = ({ children, ...props }) => ( const EditorTooltip = ({ children, ...props }) => (

View File

@ -6,7 +6,7 @@ import embeds from '../../embeds';
export default class Embed extends React.Component<*> { export default class Embed extends React.Component<*> {
get url(): string { get url(): string {
return this.props.node.data.get('href'); return this.props.attrs.href;
} }
getMatchResults(): ?{ component: *, matches: string[] } { getMatchResults(): ?{ component: *, matches: string[] } {
@ -26,16 +26,12 @@ export default class Embed extends React.Component<*> {
const result = this.getMatchResults(); const result = this.getMatchResults();
if (!result) return null; if (!result) return null;
const { attributes, isSelected, children } = this.props; const { isSelected, children } = this.props;
const { component, matches } = result; const { component, matches } = result;
const EmbedComponent = component; const EmbedComponent = component;
return ( return (
<Container <Container contentEditable={false} isSelected={isSelected}>
contentEditable={false}
isSelected={isSelected}
{...attributes}
>
<EmbedComponent matches={matches} url={this.url} /> <EmbedComponent matches={matches} url={this.url} />
{children} {children}
</Container> </Container>

View File

@ -71,8 +71,9 @@ class Layout extends React.Component<Props> {
window.document.body.style.background = this.props.theme.background; window.document.body.style.background = this.props.theme.background;
} }
@keydown(['/', 't', 'meta+k']) @keydown(['t', '/', 'meta+k'])
goToSearch(ev) { goToSearch(ev) {
if (this.props.ui.editMode) return;
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
this.redirectTo = searchUrl(); this.redirectTo = searchUrl();
@ -80,6 +81,7 @@ class Layout extends React.Component<Props> {
@keydown('d') @keydown('d')
goToDashboard() { goToDashboard() {
if (this.props.ui.editMode) return;
this.redirectTo = homeUrl(); this.redirectTo = homeUrl();
} }

View File

@ -40,6 +40,8 @@ class Collections extends React.Component<Props> {
@keydown('n') @keydown('n')
goToNewDocument() { goToNewDocument() {
if (this.props.ui.editMode) return;
const { activeCollectionId } = this.props.ui; const { activeCollectionId } = this.props.ui;
if (!activeCollectionId) return; if (!activeCollectionId) return;

View File

@ -2,10 +2,12 @@
import * as React from 'react'; import * as React from 'react';
import Frame from './components/Frame'; import Frame from './components/Frame';
type Props = { type Props = {|
url: string, attrs: {|
matches: string[], href: string,
}; matches: string[],
|},
|};
export default class Abstract extends React.Component<Props> { export default class Abstract extends React.Component<Props> {
static ENABLED = [ static ENABLED = [
@ -14,7 +16,7 @@ export default class Abstract extends React.Component<Props> {
]; ];
render() { render() {
const { matches } = this.props; const { matches } = this.props.attrs;
const shareId = matches[1]; const shareId = matches[1];
return ( return (

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import Abstract from './Abstract';
const { Abstract } = embeds;
describe('Abstract', () => { describe('Abstract', () => {
const match = Abstract.ENABLED[0]; const match = Abstract.ENABLED[0];

View File

@ -4,16 +4,18 @@ import Frame from './components/Frame';
const URL_REGEX = new RegExp('https://airtable.com/(?:embed/)?(shr.*)$'); const URL_REGEX = new RegExp('https://airtable.com/(?:embed/)?(shr.*)$');
type Props = { type Props = {|
url: string, attrs: {|
matches: string[], href: string,
}; matches: string[],
|},
|};
export default class Airtable extends React.Component<Props> { export default class Airtable extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
render() { render() {
const { matches } = this.props; const { matches } = this.props.attrs;
const shareId = matches[1]; const shareId = matches[1];
return ( return (

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import Airtable from './Airtable';
const { Airtable } = embeds;
describe('Airtable', () => { describe('Airtable', () => {
const match = Airtable.ENABLED[0]; const match = Airtable.ENABLED[0];

View File

@ -4,15 +4,18 @@ import Frame from './components/Frame';
const URL_REGEX = new RegExp('^https://codepen.io/(.*?)/(pen|embed)/(.*)$'); const URL_REGEX = new RegExp('^https://codepen.io/(.*?)/(pen|embed)/(.*)$');
type Props = { type Props = {|
url: string, attrs: {|
}; href: string,
matches: string[],
|},
|};
export default class Codepen extends React.Component<Props> { export default class Codepen extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
render() { render() {
const normalizedUrl = this.props.url.replace(/\/pen\//, '/embed/'); const normalizedUrl = this.props.attrs.href.replace(/\/pen\//, '/embed/');
return <Frame src={normalizedUrl} title="Codepen Embed" />; return <Frame src={normalizedUrl} title="Codepen Embed" />;
} }

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import Codepen from './Codepen';
const { Codepen } = embeds;
describe('Codepen', () => { describe('Codepen', () => {
const match = Codepen.ENABLED[0]; const match = Codepen.ENABLED[0];

View File

@ -6,9 +6,12 @@ const URL_REGEX = new RegExp(
'https://([w.-]+.)?figma.com/(file|proto)/([0-9a-zA-Z]{22,128})(?:/.*)?$' 'https://([w.-]+.)?figma.com/(file|proto)/([0-9a-zA-Z]{22,128})(?:/.*)?$'
); );
type Props = { type Props = {|
url: string, attrs: {|
}; href: string,
matches: string[],
|},
|};
export default class Figma extends React.Component<Props> { export default class Figma extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
@ -17,7 +20,7 @@ export default class Figma extends React.Component<Props> {
return ( return (
<Frame <Frame
src={`https://www.figma.com/embed?embed_host=outline&url=${ src={`https://www.figma.com/embed?embed_host=outline&url=${
this.props.url this.props.attrs.href
}`} }`}
title="Figma Embed" title="Figma Embed"
border border

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import Figma from './Figma';
const { Figma } = embeds;
describe('Figma', () => { describe('Figma', () => {
const match = Figma.ENABLED[0]; const match = Figma.ENABLED[0];

View File

@ -4,14 +4,17 @@ import Frame from './components/Frame';
const URL_REGEX = new RegExp('^https://framer.cloud/(.*)$'); const URL_REGEX = new RegExp('^https://framer.cloud/(.*)$');
type Props = { type Props = {|
url: string, attrs: {|
}; href: string,
matches: string[],
|},
|};
export default class Framer extends React.Component<Props> { export default class Framer extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
render() { render() {
return <Frame src={this.props.url} title="Framer Embed" border />; return <Frame src={this.props.attrs.href} title="Framer Embed" border />;
} }
} }

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import Framer from './Framer';
const { Framer } = embeds;
describe('Framer', () => { describe('Framer', () => {
const match = Framer.ENABLED[0]; const match = Framer.ENABLED[0];

View File

@ -5,9 +5,12 @@ const URL_REGEX = new RegExp(
'^https://gist.github.com/([a-zd](?:[a-zd]|-(?=[a-zd])){0,38})/(.*)$' '^https://gist.github.com/([a-zd](?:[a-zd]|-(?=[a-zd])){0,38})/(.*)$'
); );
type Props = { type Props = {|
url: string, attrs: {|
}; href: string,
matches: string[],
|},
|};
class Gist extends React.Component<Props> { class Gist extends React.Component<Props> {
iframeNode: ?HTMLIFrameElement; iframeNode: ?HTMLIFrameElement;
@ -19,7 +22,7 @@ class Gist extends React.Component<Props> {
} }
get id() { get id() {
const gistUrl = new URL(this.props.url); const gistUrl = new URL(this.props.attrs.href);
return gistUrl.pathname.split('/')[2]; return gistUrl.pathname.split('/')[2];
} }

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import Gist from './Gist';
const { Gist } = embeds;
describe('Gist', () => { describe('Gist', () => {
const match = Gist.ENABLED[0]; const match = Gist.ENABLED[0];

View File

@ -6,14 +6,19 @@ const URL_REGEX = new RegExp(
'^https?://docs.google.com/document/d/(.*)/pub(.*)$' '^https?://docs.google.com/document/d/(.*)/pub(.*)$'
); );
type Props = { type Props = {|
url: string, attrs: {|
}; href: string,
matches: string[],
|},
|};
export default class GoogleDocs extends React.Component<Props> { export default class GoogleDocs extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
render() { render() {
return <Frame src={this.props.url} title="Google Docs Embed" border />; return (
<Frame src={this.props.attrs.href} title="Google Docs Embed" border />
);
} }
} }

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import GoogleDocs from './GoogleDocs';
const { GoogleDocs } = embeds;
describe('GoogleDocs', () => { describe('GoogleDocs', () => {
const match = GoogleDocs.ENABLED[0]; const match = GoogleDocs.ENABLED[0];

View File

@ -6,14 +6,19 @@ const URL_REGEX = new RegExp(
'^https?://docs.google.com/spreadsheets/d/(.*)/pub(.*)$' '^https?://docs.google.com/spreadsheets/d/(.*)/pub(.*)$'
); );
type Props = { type Props = {|
url: string, attrs: {|
}; href: string,
matches: string[],
|},
|};
export default class GoogleSlides extends React.Component<Props> { export default class GoogleSlides extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
render() { render() {
return <Frame src={this.props.url} title="Google Sheets Embed" border />; return (
<Frame src={this.props.attrs.href} title="Google Sheets Embed" border />
);
} }
} }

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import GoogleSheets from './GoogleSheets';
const { GoogleSheets } = embeds;
describe('GoogleSheets', () => { describe('GoogleSheets', () => {
const match = GoogleSheets.ENABLED[0]; const match = GoogleSheets.ENABLED[0];

View File

@ -6,9 +6,12 @@ const URL_REGEX = new RegExp(
'^https?://docs.google.com/presentation/d/(.*)/pub(.*)$' '^https?://docs.google.com/presentation/d/(.*)/pub(.*)$'
); );
type Props = { type Props = {|
url: string, attrs: {|
}; href: string,
matches: string[],
|},
|};
export default class GoogleSlides extends React.Component<Props> { export default class GoogleSlides extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
@ -16,7 +19,7 @@ export default class GoogleSlides extends React.Component<Props> {
render() { render() {
return ( return (
<Frame <Frame
src={this.props.url.replace('/pub', '/embed')} src={this.props.attrs.href.replace('/pub', '/embed')}
title="Google Slides Embed" title="Google Slides Embed"
border border
/> />

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import GoogleSlides from './GoogleSlides';
const { GoogleSlides } = embeds;
describe('GoogleSlides', () => { describe('GoogleSlides', () => {
const match = GoogleSlides.ENABLED[0]; const match = GoogleSlides.ENABLED[0];

View File

@ -11,19 +11,22 @@ const IMAGE_REGEX = new RegExp(
'^https://(opal.invisionapp.com/static-signed/live-embed/.*)$' '^https://(opal.invisionapp.com/static-signed/live-embed/.*)$'
); );
type Props = { type Props = {|
url: string, attrs: {|
}; href: string,
matches: string[],
|},
|};
export default class InVision extends React.Component<Props> { export default class InVision extends React.Component<Props> {
static ENABLED = [IFRAME_REGEX, IMAGE_REGEX]; static ENABLED = [IFRAME_REGEX, IMAGE_REGEX];
render() { render() {
if (IMAGE_REGEX.test(this.props.url)) { if (IMAGE_REGEX.test(this.props.attrs.href)) {
return ( return (
<ImageZoom <ImageZoom
image={{ image={{
src: this.props.url, src: this.props.attrs.href,
alt: 'InVision Embed', alt: 'InVision Embed',
style: { style: {
maxWidth: '100%', maxWidth: '100%',
@ -34,6 +37,6 @@ export default class InVision extends React.Component<Props> {
/> />
); );
} }
return <Frame src={this.props.url} title="InVision Embed" />; return <Frame src={this.props.attrs.href} title="InVision Embed" />;
} }
} }

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import InVision from './InVision';
const { InVision } = embeds;
describe('InVision', () => { describe('InVision', () => {
const match = InVision.ENABLED[0]; const match = InVision.ENABLED[0];

View File

@ -4,15 +4,18 @@ import Frame from './components/Frame';
const URL_REGEX = /^https:\/\/(www\.)?(use)?loom.com\/(embed|share)\/(.*)$/; const URL_REGEX = /^https:\/\/(www\.)?(use)?loom.com\/(embed|share)\/(.*)$/;
type Props = { type Props = {|
url: string, attrs: {|
}; href: string,
matches: string[],
|},
|};
export default class Loom extends React.Component<Props> { export default class Loom extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
render() { render() {
const normalizedUrl = this.props.url.replace('share', 'embed'); const normalizedUrl = this.props.attrs.href.replace('share', 'embed');
return <Frame src={normalizedUrl} title="Loom Embed" />; return <Frame src={normalizedUrl} title="Loom Embed" />;
} }

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import Loom from './Loom';
const { Loom } = embeds;
describe('Loom', () => { describe('Loom', () => {
const match = Loom.ENABLED[0]; const match = Loom.ENABLED[0];

View File

@ -4,16 +4,18 @@ import Frame from './components/Frame';
const URL_REGEX = /^https?:\/\/(www\.)?lucidchart.com\/documents\/(embeddedchart|view)\/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})(?:\/.*)?$/; const URL_REGEX = /^https?:\/\/(www\.)?lucidchart.com\/documents\/(embeddedchart|view)\/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})(?:\/.*)?$/;
type Props = { type Props = {|
url: string, attrs: {|
matches: string[], href: string,
}; matches: string[],
|},
|};
export default class Lucidchart extends React.Component<Props> { export default class Lucidchart extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
render() { render() {
const { matches } = this.props; const { matches } = this.props.attrs;
const chartId = matches[3]; const chartId = matches[3];
return ( return (

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import Lucidchart from './Lucidchart';
const { Lucidchart } = embeds;
describe('Lucidchart', () => { describe('Lucidchart', () => {
const match = Lucidchart.ENABLED[0]; const match = Lucidchart.ENABLED[0];

View File

@ -4,14 +4,17 @@ import Frame from './components/Frame';
const URL_REGEX = new RegExp('^https://marvelapp.com/([A-Za-z0-9-]{6})/?$'); const URL_REGEX = new RegExp('^https://marvelapp.com/([A-Za-z0-9-]{6})/?$');
type Props = { type Props = {|
url: string, attrs: {|
}; href: string,
matches: string[],
|},
|};
export default class Marvel extends React.Component<Props> { export default class Marvel extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
render() { render() {
return <Frame src={this.props.url} title="Marvel Embed" border />; return <Frame src={this.props.attrs.href} title="Marvel Embed" border />;
} }
} }

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import Marvel from './Marvel';
const { Marvel } = embeds;
describe('Marvel', () => { describe('Marvel', () => {
const match = Marvel.ENABLED[0]; const match = Marvel.ENABLED[0];

View File

@ -6,16 +6,18 @@ const URL_REGEX = new RegExp(
'^https://([w.-]+.)?(mindmeister.com|mm.tt)(/maps/public_map_shell)?/(\\d+)(\\?t=.*)?(/.*)?$' '^https://([w.-]+.)?(mindmeister.com|mm.tt)(/maps/public_map_shell)?/(\\d+)(\\?t=.*)?(/.*)?$'
); );
type Props = { type Props = {|
url: string, attrs: {|
matches: string[], href: string,
}; matches: string[],
|},
|};
export default class Mindmeister extends React.Component<Props> { export default class Mindmeister extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
render() { render() {
const chartId = this.props.matches[4] + this.props.matches[6]; const chartId = this.props.attrs.matches[4] + this.props.attrs.matches[6];
return ( return (
<Frame <Frame

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import Mindmeister from './Mindmeister';
const { Mindmeister } = embeds;
describe('Mindmeister', () => { describe('Mindmeister', () => {
const match = Mindmeister.ENABLED[0]; const match = Mindmeister.ENABLED[0];

View File

@ -4,16 +4,18 @@ import Frame from './components/Frame';
const URL_REGEX = /^https:\/\/(?:realtimeboard|miro).com\/app\/board\/(.*)$/; const URL_REGEX = /^https:\/\/(?:realtimeboard|miro).com\/app\/board\/(.*)$/;
type Props = { type Props = {|
url: string, attrs: {|
matches: string[], href: string,
}; matches: string[],
|},
|};
export default class RealtimeBoard extends React.Component<Props> { export default class RealtimeBoard extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
render() { render() {
const { matches } = this.props; const { matches } = this.props.attrs;
const boardId = matches[1]; const boardId = matches[1];
return ( return (

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import Miro from './Miro';
const { Miro } = embeds;
describe('Miro', () => { describe('Miro', () => {
const match = Miro.ENABLED[0]; const match = Miro.ENABLED[0];

View File

@ -6,16 +6,19 @@ const URL_REGEX = new RegExp(
'^https://([w.-]+.)?modeanalytics.com/(.*)/reports/(.*)$' '^https://([w.-]+.)?modeanalytics.com/(.*)/reports/(.*)$'
); );
type Props = { type Props = {|
url: string, attrs: {|
}; href: string,
matches: string[],
|},
|};
export default class ModeAnalytics extends React.Component<Props> { export default class ModeAnalytics extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
render() { render() {
// Allow users to paste embed or standard urls and handle them the same // Allow users to paste embed or standard urls and handle them the same
const normalizedUrl = this.props.url.replace(/\/embed$/, ''); const normalizedUrl = this.props.attrs.href.replace(/\/embed$/, '');
return ( return (
<Frame src={`${normalizedUrl}/embed`} title="Mode Analytics Embed" /> <Frame src={`${normalizedUrl}/embed`} title="Mode Analytics Embed" />

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import ModeAnalytics from './ModeAnalytics';
const { ModeAnalytics } = embeds;
describe('ModeAnalytics', () => { describe('ModeAnalytics', () => {
const match = ModeAnalytics.ENABLED[0]; const match = ModeAnalytics.ENABLED[0];

View File

@ -1,22 +0,0 @@
// @flow
import * as React from 'react';
import Frame from './components/Frame';
const URL_REGEX = new RegExp('https://([w.-]+.)?numeracy.co/(.*)/(.*)$');
type Props = {
url: string,
};
export default class Numeracy extends React.Component<Props> {
static ENABLED = [URL_REGEX];
render() {
// Allow users to paste embed or standard urls and handle them the same
const normalizedUrl = this.props.url.replace(/\.embed$/, '');
return (
<Frame src={`${normalizedUrl}.embed`} title="Numeracy Embed" border />
);
}
}

View File

@ -1,22 +0,0 @@
/* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.';
const { Numeracy } = embeds;
describe('Numeracy', () => {
const match = Numeracy.ENABLED[0];
test('to be enabled on share link', () => {
expect('https://numeracy.co/outline/n8ZIVOC2OS'.match(match)).toBeTruthy();
});
test('to be enabled on embed link', () => {
expect(
'https://numeracy.co/outline/n8ZIVOC2OS.embed'.match(match)
).toBeTruthy();
});
test('to not be enabled elsewhere', () => {
expect('https://numeracy.co'.match(match)).toBe(null);
expect('https://numeracy.co/outline'.match(match)).toBe(null);
});
});

View File

@ -4,15 +4,18 @@ import Frame from './components/Frame';
const URL_REGEX = new RegExp('^https://prezi.com/view/(.*)$'); const URL_REGEX = new RegExp('^https://prezi.com/view/(.*)$');
type Props = { type Props = {|
url: string, attrs: {|
}; href: string,
matches: string[],
|},
|};
export default class Prezi extends React.Component<Props> { export default class Prezi extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
render() { render() {
const url = this.props.url.replace(/\/embed$/, ''); const url = this.props.attrs.href.replace(/\/embed$/, '');
return <Frame src={`${url}/embed`} title="Prezi Embed" border />; return <Frame src={`${url}/embed`} title="Prezi Embed" border />;
} }

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import Prezi from './Prezi';
const { Prezi } = embeds;
describe('Prezi', () => { describe('Prezi', () => {
const match = Prezi.ENABLED[0]; const match = Prezi.ENABLED[0];

View File

@ -4,16 +4,18 @@ import Frame from './components/Frame';
const URL_REGEX = new RegExp('https?://open.spotify.com/(.*)$'); const URL_REGEX = new RegExp('https?://open.spotify.com/(.*)$');
type Props = { type Props = {|
url: string, attrs: {|
}; href: string,
matches: string[],
|},
|};
export default class Spotify extends React.Component<Props> { export default class Spotify extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
get pathname() { get pathname() {
try { try {
const parsed = new URL(this.props.url); const parsed = new URL(this.props.attrs.href);
return parsed.pathname; return parsed.pathname;
} catch (err) { } catch (err) {
return ''; return '';

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import Spotify from './Spotify';
const { Spotify } = embeds;
describe('Spotify', () => { describe('Spotify', () => {
const match = Spotify.ENABLED[0]; const match = Spotify.ENABLED[0];

View File

@ -2,18 +2,20 @@
import * as React from 'react'; import * as React from 'react';
import Frame from './components/Frame'; import Frame from './components/Frame';
const URL_REGEX = /^https:\/\/trello.com\/(c|b)\/(.*)$/; const URL_REGEX = /^https:\/\/trello.com\/(c|b)\/([^/]*)(.*)?$/;
type Props = { type Props = {|
url: string, attrs: {|
matches: string[], href: string,
}; matches: string[],
|},
|};
export default class Trello extends React.Component<Props> { export default class Trello extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
render() { render() {
const { matches } = this.props; const { matches } = this.props.attrs;
const objectId = matches[2]; const objectId = matches[2];
if (matches[1] === 'c') { if (matches[1] === 'c') {

View File

@ -6,14 +6,17 @@ const URL_REGEX = new RegExp(
'^https://([A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?).typeform.com/to/(.*)$' '^https://([A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?).typeform.com/to/(.*)$'
); );
type Props = { type Props = {|
url: string, attrs: {|
}; href: string,
matches: string[],
|},
|};
export default class Typeform extends React.Component<Props> { export default class Typeform extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
render() { render() {
return <Frame src={this.props.url} title="Typeform Embed" />; return <Frame src={this.props.attrs.href} title="Typeform Embed" />;
} }
} }

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import Typeform from './Typeform';
const { Typeform } = embeds;
describe('Typeform', () => { describe('Typeform', () => {
const match = Typeform.ENABLED[0]; const match = Typeform.ENABLED[0];

View File

@ -4,16 +4,18 @@ import Frame from './components/Frame';
const URL_REGEX = /(http|https)?:\/\/(www\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^\/]*)\/videos\/|)(\d+)(?:|\/\?)/; const URL_REGEX = /(http|https)?:\/\/(www\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^\/]*)\/videos\/|)(\d+)(?:|\/\?)/;
type Props = { type Props = {|
url: string, attrs: {|
matches: string[], href: string,
}; matches: string[],
|},
|};
export default class Vimeo extends React.Component<Props> { export default class Vimeo extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
render() { render() {
const { matches } = this.props; const { matches } = this.props.attrs;
const videoId = matches[4]; const videoId = matches[4];
return ( return (

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import Vimeo from './Vimeo';
const { Vimeo } = embeds;
describe('Vimeo', () => { describe('Vimeo', () => {
const match = Vimeo.ENABLED[0]; const match = Vimeo.ENABLED[0];

View File

@ -4,16 +4,18 @@ import Frame from './components/Frame';
const URL_REGEX = /(?:https?:\/\/)?(?:www\.)?youtu\.?be(?:\.com)?\/?.*(?:watch|embed)?(?:.*v=|v\/|\/)([a-zA-Z0-9_-]{11})$/i; const URL_REGEX = /(?:https?:\/\/)?(?:www\.)?youtu\.?be(?:\.com)?\/?.*(?:watch|embed)?(?:.*v=|v\/|\/)([a-zA-Z0-9_-]{11})$/i;
type Props = { type Props = {|
url: string, attrs: {|
matches: string[], href: string,
}; matches: string[],
|},
|};
export default class YouTube extends React.Component<Props> { export default class YouTube extends React.Component<Props> {
static ENABLED = [URL_REGEX]; static ENABLED = [URL_REGEX];
render() { render() {
const { matches } = this.props; const { matches } = this.props.attrs;
const videoId = matches[1]; const videoId = matches[1];
return ( return (

View File

@ -1,7 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import embeds from '.'; import YouTube from './YouTube';
const { YouTube } = embeds;
describe('YouTube', () => { describe('YouTube', () => {
const match = YouTube.ENABLED[0]; const match = YouTube.ENABLED[0];

View File

@ -35,7 +35,7 @@ class Frame extends React.Component<Props> {
const { const {
border, border,
width = '100%', width = '100%',
height = '400', height = '400px',
forwardedRef, forwardedRef,
...rest ...rest
} = this.props; } = this.props;

View File

@ -1,4 +1,6 @@
// @flow // @flow
import * as React from 'react';
import styled from 'styled-components';
import Abstract from './Abstract'; import Abstract from './Abstract';
import Airtable from './Airtable'; import Airtable from './Airtable';
import Codepen from './Codepen'; import Codepen from './Codepen';
@ -15,7 +17,6 @@ import Marvel from './Marvel';
import Mindmeister from './Mindmeister'; import Mindmeister from './Mindmeister';
import Miro from './Miro'; import Miro from './Miro';
import ModeAnalytics from './ModeAnalytics'; import ModeAnalytics from './ModeAnalytics';
import Numeracy from './Numeracy';
import Prezi from './Prezi'; import Prezi from './Prezi';
import Spotify from './Spotify'; import Spotify from './Spotify';
import Trello from './Trello'; import Trello from './Trello';
@ -23,28 +24,176 @@ import Typeform from './Typeform';
import Vimeo from './Vimeo'; import Vimeo from './Vimeo';
import YouTube from './YouTube'; import YouTube from './YouTube';
export default { function matcher(Component) {
Abstract, return (url: string) => {
Airtable, const regexes = Component.ENABLED;
Codepen, for (const regex of regexes) {
Figma, const result = url.match(regex);
Framer, if (result) {
Gist, return result;
GoogleDocs, }
GoogleSheets, }
GoogleSlides, };
InVision, }
Loom,
Lucidchart, const Img = styled.img`
Marvel, margin: 4px;
Mindmeister, width: 18px;
Miro, height: 18px;
ModeAnalytics, `;
Numeracy,
Prezi, export default [
Spotify, {
Trello, title: 'Abstract',
Typeform, keywords: 'design',
Vimeo, icon: () => <Img src="/images/abstract.png" />,
YouTube, component: Abstract,
}; matcher: matcher(Abstract),
},
{
title: 'Airtable',
keywords: 'spreadsheet',
icon: () => <Img src="/images/airtable.png" />,
component: Airtable,
matcher: matcher(Airtable),
},
{
title: 'Codepen',
keywords: 'code editor',
icon: () => <Img src="/images/codepen.png" />,
component: Codepen,
matcher: matcher(Codepen),
},
{
title: 'Figma',
keywords: 'design svg vector',
icon: () => <Img src="/images/figma.png" />,
component: Figma,
matcher: matcher(Figma),
},
{
title: 'Framer',
keywords: 'design prototyping',
icon: () => <Img src="/images/framer.png" />,
component: Framer,
matcher: matcher(Framer),
},
{
title: 'GitHub Gist',
keywords: 'code',
icon: () => <Img src="/images/github-gist.png" />,
component: Gist,
matcher: matcher(Gist),
},
{
title: 'Google Docs',
icon: () => <Img src="/images/google-docs.png" />,
component: GoogleDocs,
matcher: matcher(GoogleDocs),
},
{
title: 'Google Sheets',
keywords: 'excel spreadsheet',
icon: () => <Img src="/images/google-sheets.png" />,
component: GoogleSheets,
matcher: matcher(GoogleSheets),
},
{
title: 'Google Slides',
keywords: 'presentation slideshow',
icon: () => <Img src="/images/google-slides.png" />,
component: GoogleSlides,
matcher: matcher(GoogleSlides),
},
{
title: 'InVision',
keywords: 'design prototype',
icon: () => <Img src="/images/invision.png" />,
component: InVision,
matcher: matcher(InVision),
},
{
title: 'Loom',
keywords: 'video screencast',
icon: () => <Img src="/images/loom.png" />,
component: Loom,
matcher: matcher(Loom),
},
{
title: 'Lucidchart',
keywords: 'chart',
icon: () => <Img src="/images/lucidchart.png" />,
component: Lucidchart,
matcher: matcher(Lucidchart),
},
{
title: 'Marvel',
keywords: 'design prototype',
icon: () => <Img src="/images/marvel.png" />,
component: Marvel,
matcher: matcher(Marvel),
},
{
title: 'Mindmeister',
keywords: 'mindmap',
icon: () => <Img src="/images/mindmeister.png" />,
component: Mindmeister,
matcher: matcher(Mindmeister),
},
{
title: 'Miro',
keywords: 'whiteboard',
icon: () => <Img src="/images/miro.png" />,
component: Miro,
matcher: matcher(Miro),
},
{
title: 'Mode',
keywords: 'analytics',
icon: () => <Img src="/images/mode-analytics.png" />,
component: ModeAnalytics,
matcher: matcher(ModeAnalytics),
},
{
title: 'Prezi',
keywords: 'presentation',
icon: () => <Img src="/images/prezi.png" />,
component: Prezi,
matcher: matcher(Prezi),
},
{
title: 'Spotify',
keywords: 'music',
icon: () => <Img src="/images/spotify.png" />,
component: Spotify,
matcher: matcher(Spotify),
},
{
title: 'Trello',
keywords: 'kanban',
icon: () => <Img src="/images/trello.png" />,
component: Trello,
matcher: matcher(Trello),
},
{
title: 'Typeform',
keywords: 'form survey',
icon: () => <Img src="/images/typeform.png" />,
component: Typeform,
matcher: matcher(Typeform),
},
{
title: 'Vimeo',
keywords: 'video',
icon: () => <Img src="/images/vimeo.png" />,
component: Vimeo,
matcher: matcher(Vimeo),
},
{
title: 'YouTube',
keywords: 'google video',
icon: () => <Img src="/images/youtube.png" />,
component: YouTube,
matcher: matcher(YouTube),
},
];

View File

@ -3,7 +3,6 @@ import { action, set, observable, computed } from 'mobx';
import addDays from 'date-fns/add_days'; import addDays from 'date-fns/add_days';
import invariant from 'invariant'; import invariant from 'invariant';
import { client } from 'utils/ApiClient'; import { client } from 'utils/ApiClient';
import getHeadingsForText from 'shared/utils/getHeadingsForText';
import parseTitle from 'shared/utils/parseTitle'; import parseTitle from 'shared/utils/parseTitle';
import unescape from 'shared/utils/unescape'; import unescape from 'shared/utils/unescape';
import BaseModel from 'models/BaseModel'; import BaseModel from 'models/BaseModel';
@ -45,11 +44,6 @@ export default class Document extends BaseModel {
return emoji; return emoji;
} }
@computed
get headings() {
return getHeadingsForText(this.text);
}
@computed @computed
get isOnlyTitle(): boolean { get isOnlyTitle(): boolean {
return !this.text.trim(); return !this.text.trim();

View File

@ -1,6 +1,4 @@
// @flow // @flow
import { computed } from 'mobx';
import getHeadingsForText from 'shared/utils/getHeadingsForText';
import BaseModel from './BaseModel'; import BaseModel from './BaseModel';
import User from './User'; import User from './User';
@ -11,11 +9,6 @@ class Revision extends BaseModel {
text: string; text: string;
createdAt: string; createdAt: string;
createdBy: User; createdBy: User;
@computed
get headings() {
return getHeadingsForText(this.text);
}
} }
export default Revision; export default Revision;

View File

@ -5,18 +5,14 @@ import breakpoint from 'styled-components-breakpoint';
import useWindowScrollPosition from '@rehooks/window-scroll-position'; import useWindowScrollPosition from '@rehooks/window-scroll-position';
import HelpText from 'components/HelpText'; import HelpText from 'components/HelpText';
import styled from 'styled-components'; import styled from 'styled-components';
import Document from 'models/Document';
import Revision from 'models/Revision';
const HEADING_OFFSET = 20; const HEADING_OFFSET = 20;
type Props = { type Props = {
document: Revision | Document, headings: { title: string, level: number, id: string }[],
}; };
export default function Contents({ document }: Props) { export default function Contents({ headings }: Props) {
const headings = document.headings;
// $FlowFixMe // $FlowFixMe
const [activeSlug, setActiveSlug] = React.useState(); const [activeSlug, setActiveSlug] = React.useState();
const position = useWindowScrollPosition({ throttle: 100 }); const position = useWindowScrollPosition({ throttle: 100 });
@ -27,20 +23,20 @@ export default function Contents({ document }: Props) {
for (let key = 0; key < headings.length; key++) { for (let key = 0; key < headings.length; key++) {
const heading = headings[key]; const heading = headings[key];
const element = window.document.getElementById( const element = window.document.getElementById(
decodeURIComponent(heading.slug) decodeURIComponent(heading.id)
); );
if (element) { if (element) {
const bounding = element.getBoundingClientRect(); const bounding = element.getBoundingClientRect();
if (bounding.top > HEADING_OFFSET) { if (bounding.top > HEADING_OFFSET) {
const last = headings[Math.max(0, key - 1)]; const last = headings[Math.max(0, key - 1)];
setActiveSlug(last.slug); setActiveSlug(last.id);
break; return;
} }
} }
} }
}, },
[position] [position, headings]
); );
// calculate the minimum heading level and adjust all the headings to make // calculate the minimum heading level and adjust all the headings to make
@ -60,11 +56,11 @@ export default function Contents({ document }: Props) {
<List> <List>
{headings.map(heading => ( {headings.map(heading => (
<ListItem <ListItem
key={heading.slug} key={heading.id}
level={heading.level - headingAdjustment} level={heading.level - headingAdjustment}
active={activeSlug === heading.slug} active={activeSlug === heading.id}
> >
<Link href={`#${heading.slug}`}>{heading.title}</Link> <Link href={`#${heading.id}`}>{heading.title}</Link>
</ListItem> </ListItem>
))} ))}
</List> </List>

View File

@ -5,7 +5,6 @@ import styled from 'styled-components';
import breakpoint from 'styled-components-breakpoint'; import breakpoint from 'styled-components-breakpoint';
import { observable } from 'mobx'; import { observable } from 'mobx';
import { observer, inject } from 'mobx-react'; import { observer, inject } from 'mobx-react';
import { schema } from 'rich-markdown-editor';
import { Prompt, Route, withRouter } from 'react-router-dom'; import { Prompt, Route, withRouter } from 'react-router-dom';
import type { Location, RouterHistory } from 'react-router-dom'; import type { Location, RouterHistory } from 'react-router-dom';
import keydown from 'react-keydown'; import keydown from 'react-keydown';
@ -65,6 +64,7 @@ type Props = {
@observer @observer
class DocumentScene extends React.Component<Props> { class DocumentScene extends React.Component<Props> {
@observable editor: ?any;
getEditorText: () => string = () => this.props.document.text; getEditorText: () => string = () => this.props.document.text;
@observable editorComponent = EditorImport; @observable editorComponent = EditorImport;
@ -88,6 +88,8 @@ class DocumentScene extends React.Component<Props> {
@keydown('m') @keydown('m')
goToMove(ev) { goToMove(ev) {
if (!this.props.readOnly) return;
ev.preventDefault(); ev.preventDefault();
const { document, abilities } = this.props; const { document, abilities } = this.props;
@ -98,6 +100,8 @@ class DocumentScene extends React.Component<Props> {
@keydown('e') @keydown('e')
goToEdit(ev) { goToEdit(ev) {
if (!this.props.readOnly) return;
ev.preventDefault(); ev.preventDefault();
const { document, abilities } = this.props; const { document, abilities } = this.props;
@ -116,6 +120,8 @@ class DocumentScene extends React.Component<Props> {
@keydown('h') @keydown('h')
goToHistory(ev) { goToHistory(ev) {
if (!this.props.readOnly) return;
ev.preventDefault(); ev.preventDefault();
const { document, revision } = this.props; const { document, revision } = this.props;
@ -356,9 +362,18 @@ class DocumentScene extends React.Component<Props> {
)} )}
<Flex auto={!readOnly}> <Flex auto={!readOnly}>
{ui.tocVisible && {ui.tocVisible &&
readOnly && <Contents document={revision || document} />} readOnly && (
<Contents
headings={this.editor ? this.editor.getHeadings() : []}
/>
)}
<Editor <Editor
id={document.id} id={document.id}
ref={ref => {
if (ref) {
this.editor = ref;
}
}}
isDraft={document.isDraft} isDraft={document.isDraft}
key={disableEmbeds ? 'embeds-disabled' : 'embeds-enabled'} key={disableEmbeds ? 'embeds-disabled' : 'embeds-enabled'}
title={revision ? revision.title : this.title} title={revision ? revision.title : this.title}
@ -375,7 +390,6 @@ class DocumentScene extends React.Component<Props> {
onCancel={this.goBack} onCancel={this.goBack}
readOnly={readOnly || document.isArchived} readOnly={readOnly || document.isArchived}
ui={this.props.ui} ui={this.props.ui}
schema={schema}
/> />
</Flex> </Flex>
{readOnly && {readOnly &&

View File

@ -0,0 +1,35 @@
// @flow
import * as React from 'react';
import styled from 'styled-components';
import { inject } from 'mobx-react';
import ViewsStore from 'stores/ViewsStore';
import Document from 'models/Document';
import PublishingInfo from 'components/PublishingInfo';
type Props = {|
views: ViewsStore,
document: Document,
isDraft: boolean,
|};
function DocumentMeta({ views, isDraft, document }: Props) {
const totalViews = views.countForDocument(document.id);
return (
<Meta document={document}>
{totalViews && !isDraft ? (
<React.Fragment>
&nbsp;&middot; Viewed{' '}
{totalViews === 1 ? 'once' : `${totalViews} times`}
</React.Fragment>
) : null}
</Meta>
);
}
const Meta = styled(PublishingInfo)`
margin: -12px 0 2em 0;
font-size: 14px;
`;
export default inject('views')(DocumentMeta);

View File

@ -2,25 +2,22 @@
import * as React from 'react'; import * as React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import Textarea from 'react-autosize-textarea'; import Textarea from 'react-autosize-textarea';
import { inject, observer } from 'mobx-react'; import { observer } from 'mobx-react';
import Editor from 'components/Editor'; import Editor from 'components/Editor';
import PublishingInfo from 'components/PublishingInfo';
import ClickablePadding from 'components/ClickablePadding'; import ClickablePadding from 'components/ClickablePadding';
import Flex from 'shared/components/Flex'; import Flex from 'shared/components/Flex';
import parseTitle from 'shared/utils/parseTitle'; import parseTitle from 'shared/utils/parseTitle';
import ViewsStore from 'stores/ViewsStore';
import Document from 'models/Document'; import Document from 'models/Document';
import plugins from './plugins'; import DocumentMeta from './DocumentMeta';
type Props = {| type Props = {
onChangeTitle: (event: SyntheticInputEvent<>) => void, onChangeTitle: (event: SyntheticInputEvent<>) => void,
title: string, title: string,
defaultValue: string, defaultValue: string,
document: Document, document: Document,
views: ViewsStore,
isDraft: boolean, isDraft: boolean,
readOnly?: boolean, readOnly?: boolean,
|}; };
@observer @observer
class DocumentEditor extends React.Component<Props> { class DocumentEditor extends React.Component<Props> {
@ -38,6 +35,14 @@ class DocumentEditor extends React.Component<Props> {
} }
}; };
getHeadings = () => {
if (this.editor) {
return this.editor.getHeadings();
}
return [];
};
handleTitleKeyDown = (event: SyntheticKeyboardEvent<>) => { handleTitleKeyDown = (event: SyntheticKeyboardEvent<>) => {
if (event.key === 'Enter' || event.key === 'Tab') { if (event.key === 'Enter' || event.key === 'Tab') {
event.preventDefault(); event.preventDefault();
@ -46,15 +51,7 @@ class DocumentEditor extends React.Component<Props> {
}; };
render() { render() {
const { const { document, title, onChangeTitle, isDraft, readOnly } = this.props;
views,
document,
title,
onChangeTitle,
isDraft,
readOnly,
} = this.props;
const totalViews = views.countForDocument(document.id);
const { emoji } = parseTitle(title); const { emoji } = parseTitle(title);
const startsWithEmojiAndSpace = !!( const startsWithEmojiAndSpace = !!(
emoji && title.match(new RegExp(`^${emoji}\\s`)) emoji && title.match(new RegExp(`^${emoji}\\s`))
@ -68,24 +65,16 @@ class DocumentEditor extends React.Component<Props> {
onKeyDown={this.handleTitleKeyDown} onKeyDown={this.handleTitleKeyDown}
placeholder="Start with a title…" placeholder="Start with a title…"
value={!title && readOnly ? 'Untitled' : title} value={!title && readOnly ? 'Untitled' : title}
offsetLeft={startsWithEmojiAndSpace} style={startsWithEmojiAndSpace ? { marginLeft: '-1.2em' } : undefined}
readOnly={readOnly} readOnly={readOnly}
autoFocus={!title} autoFocus={!title}
maxlength={100} maxLength={100}
/> />
<Meta document={document}> <DocumentMeta isDraft={isDraft} document={document} />
{totalViews && !isDraft ? (
<React.Fragment>
&nbsp;&middot; Viewed{' '}
{totalViews === 1 ? 'once' : `${totalViews} times`}
</React.Fragment>
) : null}
</Meta>
<Editor <Editor
ref={ref => (this.editor = ref)} ref={ref => (this.editor = ref)}
autoFocus={title && !this.props.defaultValue} autoFocus={title && !this.props.defaultValue}
placeholder="…the rest is up to you" placeholder="…the rest is up to you"
plugins={plugins}
grow grow
{...this.props} {...this.props}
/> />
@ -95,11 +84,6 @@ class DocumentEditor extends React.Component<Props> {
} }
} }
const Meta = styled(PublishingInfo)`
margin: -12px 0 2em 0;
font-size: 14px;
`;
const Title = styled(Textarea)` const Title = styled(Textarea)`
z-index: 1; z-index: 1;
line-height: 1.25; line-height: 1.25;
@ -108,7 +92,6 @@ const Title = styled(Textarea)`
text: ${props => props.theme.text}; text: ${props => props.theme.text};
background: ${props => props.theme.background}; background: ${props => props.theme.background};
transition: ${props => props.theme.backgroundTransition}; transition: ${props => props.theme.backgroundTransition};
margin-left: ${props => (props.offsetLeft ? '-1.2em' : 0)};
color: ${props => props.theme.text}; color: ${props => props.theme.text};
font-size: 2.25em; font-size: 2.25em;
font-weight: 500; font-weight: 500;
@ -122,4 +105,4 @@ const Title = styled(Textarea)`
} }
`; `;
export default inject('views')(DocumentEditor); export default DocumentEditor;

View File

@ -1,15 +0,0 @@
// @flow
import { Editor } from 'slate';
import isModKey from 'rich-markdown-editor/lib/lib/isModKey';
export default [
{
onKeyDown(ev: SyntheticKeyboardEvent<>, editor: Editor, next: Function) {
if (ev.key === 'p' && ev.shiftKey && isModKey(ev)) {
return editor.props.onPublish(ev);
}
return next();
},
},
];

View File

@ -148,6 +148,8 @@ function KeyboardShortcuts() {
<Label>Strikethrough</Label> <Label>Strikethrough</Label>
<Keys>{'`code`'}</Keys> <Keys>{'`code`'}</Keys>
<Label>Inline code</Label> <Label>Inline code</Label>
<Keys>==highlight==</Keys>
<Label>highlight</Label>
</List> </List>
</Flex> </Flex>
); );

View File

@ -1,564 +0,0 @@
// flow-typed signature: 0a20db42510ecb339c149ca69c52342b
// flow-typed version: <<STUB>>/rich-markdown-editor_v^6.1.1/flow_v0.86.0
/**
* This is an autogenerated libdef stub for:
*
* 'rich-markdown-editor'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'rich-markdown-editor' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'rich-markdown-editor/lib/animations' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/changes' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/BlockInsert' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/ClickablePadding' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Code' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Contents' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/CopyButton' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/CopyToClipboard' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Flex' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Heading' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/HorizontalRule' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/BackIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/BlockQuoteIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/BoldIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/BulletedListIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/CheckboxIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/CloseIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/CodeIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/CollapsedIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/CollectionIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/DocumentIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/EditIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/GoToIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/Heading1Icon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/Heading2Icon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/HomeIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/HorizontalRuleIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/Icon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/ImageIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/index' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/ItalicIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/LinkIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/MenuIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/MoreIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/NextIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/OpenIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/OrderedListIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/PlusIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/SearchIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/SettingsIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/StrikethroughIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/TableIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/TodoListIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Icon/TrashIcon' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Image' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/InlineCode' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Link' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/ListItem' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Paragraph' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Placeholder' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Text' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/TodoItem' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/TodoList' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Toolbar/BlockToolbar' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Toolbar/DocumentResult' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Toolbar/FormattingToolbar' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Toolbar/index' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Toolbar/LinkSearchResult' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Toolbar/LinkToolbar' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/components/Toolbar/ToolbarButton' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/constants' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/index' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/lib/getDataTransferFiles' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/lib/headingToSlug' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/lib/isModKey' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/marks' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/nodes' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/plugins' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/plugins/EditList' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/plugins/Ellipsis' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/plugins/KeyboardShortcuts' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/plugins/MarkdownPaste' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/plugins/MarkdownShortcuts' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/schema' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/serializer' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/theme' {
declare module.exports: any;
}
declare module 'rich-markdown-editor/lib/types' {
declare module.exports: any;
}
// Filename aliases
declare module 'rich-markdown-editor/lib/animations.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/animations'>;
}
declare module 'rich-markdown-editor/lib/changes.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/changes'>;
}
declare module 'rich-markdown-editor/lib/components/BlockInsert.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/BlockInsert'>;
}
declare module 'rich-markdown-editor/lib/components/ClickablePadding.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/ClickablePadding'>;
}
declare module 'rich-markdown-editor/lib/components/Code.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Code'>;
}
declare module 'rich-markdown-editor/lib/components/Contents.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Contents'>;
}
declare module 'rich-markdown-editor/lib/components/CopyButton.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/CopyButton'>;
}
declare module 'rich-markdown-editor/lib/components/CopyToClipboard.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/CopyToClipboard'>;
}
declare module 'rich-markdown-editor/lib/components/Flex.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Flex'>;
}
declare module 'rich-markdown-editor/lib/components/Heading.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Heading'>;
}
declare module 'rich-markdown-editor/lib/components/HorizontalRule.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/HorizontalRule'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/BackIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/BackIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/BlockQuoteIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/BlockQuoteIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/BoldIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/BoldIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/BulletedListIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/BulletedListIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/CheckboxIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/CheckboxIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/CloseIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/CloseIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/CodeIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/CodeIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/CollapsedIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/CollapsedIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/CollectionIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/CollectionIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/DocumentIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/DocumentIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/EditIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/EditIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/GoToIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/GoToIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/Heading1Icon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/Heading1Icon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/Heading2Icon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/Heading2Icon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/HomeIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/HomeIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/HorizontalRuleIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/HorizontalRuleIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/Icon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/Icon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/ImageIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/ImageIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/index.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/index'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/ItalicIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/ItalicIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/LinkIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/LinkIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/MenuIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/MenuIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/MoreIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/MoreIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/NextIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/NextIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/OpenIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/OpenIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/OrderedListIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/OrderedListIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/PlusIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/PlusIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/SearchIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/SearchIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/SettingsIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/SettingsIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/StrikethroughIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/StrikethroughIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/TableIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/TableIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/TodoListIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/TodoListIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Icon/TrashIcon.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Icon/TrashIcon'>;
}
declare module 'rich-markdown-editor/lib/components/Image.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Image'>;
}
declare module 'rich-markdown-editor/lib/components/InlineCode.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/InlineCode'>;
}
declare module 'rich-markdown-editor/lib/components/Link.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Link'>;
}
declare module 'rich-markdown-editor/lib/components/ListItem.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/ListItem'>;
}
declare module 'rich-markdown-editor/lib/components/Paragraph.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Paragraph'>;
}
declare module 'rich-markdown-editor/lib/components/Placeholder.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Placeholder'>;
}
declare module 'rich-markdown-editor/lib/components/Text.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Text'>;
}
declare module 'rich-markdown-editor/lib/components/TodoItem.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/TodoItem'>;
}
declare module 'rich-markdown-editor/lib/components/TodoList.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/TodoList'>;
}
declare module 'rich-markdown-editor/lib/components/Toolbar/BlockToolbar.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Toolbar/BlockToolbar'>;
}
declare module 'rich-markdown-editor/lib/components/Toolbar/DocumentResult.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Toolbar/DocumentResult'>;
}
declare module 'rich-markdown-editor/lib/components/Toolbar/FormattingToolbar.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Toolbar/FormattingToolbar'>;
}
declare module 'rich-markdown-editor/lib/components/Toolbar/index.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Toolbar/index'>;
}
declare module 'rich-markdown-editor/lib/components/Toolbar/LinkSearchResult.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Toolbar/LinkSearchResult'>;
}
declare module 'rich-markdown-editor/lib/components/Toolbar/LinkToolbar.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Toolbar/LinkToolbar'>;
}
declare module 'rich-markdown-editor/lib/components/Toolbar/ToolbarButton.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/components/Toolbar/ToolbarButton'>;
}
declare module 'rich-markdown-editor/lib/constants.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/constants'>;
}
declare module 'rich-markdown-editor/lib/index.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/index'>;
}
declare module 'rich-markdown-editor/lib/lib/getDataTransferFiles.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/lib/getDataTransferFiles'>;
}
declare module 'rich-markdown-editor/lib/lib/headingToSlug.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/lib/headingToSlug'>;
}
declare module 'rich-markdown-editor/lib/lib/isModKey.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/lib/isModKey'>;
}
declare module 'rich-markdown-editor/lib/marks.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/marks'>;
}
declare module 'rich-markdown-editor/lib/nodes.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/nodes'>;
}
declare module 'rich-markdown-editor/lib/plugins.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/plugins'>;
}
declare module 'rich-markdown-editor/lib/plugins/EditList.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/plugins/EditList'>;
}
declare module 'rich-markdown-editor/lib/plugins/Ellipsis.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/plugins/Ellipsis'>;
}
declare module 'rich-markdown-editor/lib/plugins/KeyboardShortcuts.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/plugins/KeyboardShortcuts'>;
}
declare module 'rich-markdown-editor/lib/plugins/MarkdownPaste.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/plugins/MarkdownPaste'>;
}
declare module 'rich-markdown-editor/lib/plugins/MarkdownShortcuts.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/plugins/MarkdownShortcuts'>;
}
declare module 'rich-markdown-editor/lib/schema.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/schema'>;
}
declare module 'rich-markdown-editor/lib/serializer.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/serializer'>;
}
declare module 'rich-markdown-editor/lib/theme.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/theme'>;
}
declare module 'rich-markdown-editor/lib/types.js' {
declare module.exports: $Exports<'rich-markdown-editor/lib/types'>;
}

View File

@ -1,46 +0,0 @@
// flow-typed signature: fb8acb4981f8f021dade0e38b6631063
// flow-typed version: <<STUB>>/slate-collapse-on-escape_v^0.6.0/flow_v0.71.0
/**
* This is an autogenerated libdef stub for:
*
* 'slate-collapse-on-escape'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'slate-collapse-on-escape' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'slate-collapse-on-escape/dist/slate-collapse-on-escape' {
declare module.exports: any;
}
declare module 'slate-collapse-on-escape/dist/slate-collapse-on-escape.min' {
declare module.exports: any;
}
declare module 'slate-collapse-on-escape/lib/index' {
declare module.exports: any;
}
// Filename aliases
declare module 'slate-collapse-on-escape/dist/slate-collapse-on-escape.js' {
declare module.exports: $Exports<'slate-collapse-on-escape/dist/slate-collapse-on-escape'>;
}
declare module 'slate-collapse-on-escape/dist/slate-collapse-on-escape.min.js' {
declare module.exports: $Exports<'slate-collapse-on-escape/dist/slate-collapse-on-escape.min'>;
}
declare module 'slate-collapse-on-escape/lib/index.js' {
declare module.exports: $Exports<'slate-collapse-on-escape/lib/index'>;
}

View File

@ -1,599 +0,0 @@
// flow-typed signature: 10bebc2e6b3bb07eca5614fd2b7f6e87
// flow-typed version: <<STUB>>/slate-edit-code_v^0.14.0/flow_v0.71.0
/**
* This is an autogenerated libdef stub for:
*
* 'slate-edit-code'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'slate-edit-code' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'slate-edit-code/dist/changes/dedentLines' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/changes/indentLines' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/changes/index' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/changes/toggleCodeBlock' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/changes/unwrapCodeBlock' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/changes/unwrapCodeBlockByKey' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/changes/wrapCodeBlock' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/changes/wrapCodeBlockByKey' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/core' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/deserializeCode' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/getCurrentCode' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/getCurrentIndent' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/getIndent' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/handlers/index' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/handlers/onBackspace' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/handlers/onEnter' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/handlers/onKeyDown' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/handlers/onModEnter' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/handlers/onPaste' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/handlers/onSelectAll' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/handlers/onShiftTab' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/handlers/onTab' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/index' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/isInCodeBlock' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/makeSchema' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/onBackspace' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/onEnter' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/onModEnter' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/onSelectAll' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/onShiftTab' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/onTab' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/options' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/transforms/dedentLines' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/transforms/indentLines' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/transforms/toggleCodeBlock' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/transforms/unwrapCodeBlock' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/transforms/unwrapCodeBlockByKey' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/transforms/wrapCodeBlock' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/transforms/wrapCodeBlockByKey' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/utils/deserializeCode' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/utils/getCurrentCode' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/utils/getCurrentIndent' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/utils/getIndent' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/utils/index' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/utils/isInCodeBlock' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/validation/index' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/validation/schema' {
declare module.exports: any;
}
declare module 'slate-edit-code/dist/validation/validateNode' {
declare module.exports: any;
}
declare module 'slate-edit-code/example/bundle' {
declare module.exports: any;
}
declare module 'slate-edit-code/example/main' {
declare module.exports: any;
}
declare module 'slate-edit-code/example/value' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/all' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/backspace-empty-code/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/backspace-start/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/enter-end-offset/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/enter-mid-offset/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/enter-start-offset/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/enter-with-indent/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/isincodeblock-true/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/on-exit-block/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/paste-middle/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/schema-multiline-text/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/schema-no-marks/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/schema-no-orphan-line/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/schema-only-text-2/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/schema-only-text/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/schema-single-text/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/select-all/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/shift-tab-middle-offset/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/shift-tab-multilines/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/simulate-key' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/tab-middle-offset/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/tab-multilines/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/tab-start-offset/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/togglecodeblock-code/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/togglecodeblock-normal/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/unwrapcodeblock-multi-lines/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/unwrapcodeblock-normal/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/unwrapcodeblock-selection/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/wrapcodeblock-normal/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/wrapcodeblock-selection/change' {
declare module.exports: any;
}
declare module 'slate-edit-code/tests/wrapcodeblock-with-inline/change' {
declare module.exports: any;
}
// Filename aliases
declare module 'slate-edit-code/dist/changes/dedentLines.js' {
declare module.exports: $Exports<'slate-edit-code/dist/changes/dedentLines'>;
}
declare module 'slate-edit-code/dist/changes/indentLines.js' {
declare module.exports: $Exports<'slate-edit-code/dist/changes/indentLines'>;
}
declare module 'slate-edit-code/dist/changes/index.js' {
declare module.exports: $Exports<'slate-edit-code/dist/changes/index'>;
}
declare module 'slate-edit-code/dist/changes/toggleCodeBlock.js' {
declare module.exports: $Exports<'slate-edit-code/dist/changes/toggleCodeBlock'>;
}
declare module 'slate-edit-code/dist/changes/unwrapCodeBlock.js' {
declare module.exports: $Exports<'slate-edit-code/dist/changes/unwrapCodeBlock'>;
}
declare module 'slate-edit-code/dist/changes/unwrapCodeBlockByKey.js' {
declare module.exports: $Exports<'slate-edit-code/dist/changes/unwrapCodeBlockByKey'>;
}
declare module 'slate-edit-code/dist/changes/wrapCodeBlock.js' {
declare module.exports: $Exports<'slate-edit-code/dist/changes/wrapCodeBlock'>;
}
declare module 'slate-edit-code/dist/changes/wrapCodeBlockByKey.js' {
declare module.exports: $Exports<'slate-edit-code/dist/changes/wrapCodeBlockByKey'>;
}
declare module 'slate-edit-code/dist/core.js' {
declare module.exports: $Exports<'slate-edit-code/dist/core'>;
}
declare module 'slate-edit-code/dist/deserializeCode.js' {
declare module.exports: $Exports<'slate-edit-code/dist/deserializeCode'>;
}
declare module 'slate-edit-code/dist/getCurrentCode.js' {
declare module.exports: $Exports<'slate-edit-code/dist/getCurrentCode'>;
}
declare module 'slate-edit-code/dist/getCurrentIndent.js' {
declare module.exports: $Exports<'slate-edit-code/dist/getCurrentIndent'>;
}
declare module 'slate-edit-code/dist/getIndent.js' {
declare module.exports: $Exports<'slate-edit-code/dist/getIndent'>;
}
declare module 'slate-edit-code/dist/handlers/index.js' {
declare module.exports: $Exports<'slate-edit-code/dist/handlers/index'>;
}
declare module 'slate-edit-code/dist/handlers/onBackspace.js' {
declare module.exports: $Exports<'slate-edit-code/dist/handlers/onBackspace'>;
}
declare module 'slate-edit-code/dist/handlers/onEnter.js' {
declare module.exports: $Exports<'slate-edit-code/dist/handlers/onEnter'>;
}
declare module 'slate-edit-code/dist/handlers/onKeyDown.js' {
declare module.exports: $Exports<'slate-edit-code/dist/handlers/onKeyDown'>;
}
declare module 'slate-edit-code/dist/handlers/onModEnter.js' {
declare module.exports: $Exports<'slate-edit-code/dist/handlers/onModEnter'>;
}
declare module 'slate-edit-code/dist/handlers/onPaste.js' {
declare module.exports: $Exports<'slate-edit-code/dist/handlers/onPaste'>;
}
declare module 'slate-edit-code/dist/handlers/onSelectAll.js' {
declare module.exports: $Exports<'slate-edit-code/dist/handlers/onSelectAll'>;
}
declare module 'slate-edit-code/dist/handlers/onShiftTab.js' {
declare module.exports: $Exports<'slate-edit-code/dist/handlers/onShiftTab'>;
}
declare module 'slate-edit-code/dist/handlers/onTab.js' {
declare module.exports: $Exports<'slate-edit-code/dist/handlers/onTab'>;
}
declare module 'slate-edit-code/dist/index.js' {
declare module.exports: $Exports<'slate-edit-code/dist/index'>;
}
declare module 'slate-edit-code/dist/isInCodeBlock.js' {
declare module.exports: $Exports<'slate-edit-code/dist/isInCodeBlock'>;
}
declare module 'slate-edit-code/dist/makeSchema.js' {
declare module.exports: $Exports<'slate-edit-code/dist/makeSchema'>;
}
declare module 'slate-edit-code/dist/onBackspace.js' {
declare module.exports: $Exports<'slate-edit-code/dist/onBackspace'>;
}
declare module 'slate-edit-code/dist/onEnter.js' {
declare module.exports: $Exports<'slate-edit-code/dist/onEnter'>;
}
declare module 'slate-edit-code/dist/onModEnter.js' {
declare module.exports: $Exports<'slate-edit-code/dist/onModEnter'>;
}
declare module 'slate-edit-code/dist/onSelectAll.js' {
declare module.exports: $Exports<'slate-edit-code/dist/onSelectAll'>;
}
declare module 'slate-edit-code/dist/onShiftTab.js' {
declare module.exports: $Exports<'slate-edit-code/dist/onShiftTab'>;
}
declare module 'slate-edit-code/dist/onTab.js' {
declare module.exports: $Exports<'slate-edit-code/dist/onTab'>;
}
declare module 'slate-edit-code/dist/options.js' {
declare module.exports: $Exports<'slate-edit-code/dist/options'>;
}
declare module 'slate-edit-code/dist/transforms/dedentLines.js' {
declare module.exports: $Exports<'slate-edit-code/dist/transforms/dedentLines'>;
}
declare module 'slate-edit-code/dist/transforms/indentLines.js' {
declare module.exports: $Exports<'slate-edit-code/dist/transforms/indentLines'>;
}
declare module 'slate-edit-code/dist/transforms/toggleCodeBlock.js' {
declare module.exports: $Exports<'slate-edit-code/dist/transforms/toggleCodeBlock'>;
}
declare module 'slate-edit-code/dist/transforms/unwrapCodeBlock.js' {
declare module.exports: $Exports<'slate-edit-code/dist/transforms/unwrapCodeBlock'>;
}
declare module 'slate-edit-code/dist/transforms/unwrapCodeBlockByKey.js' {
declare module.exports: $Exports<'slate-edit-code/dist/transforms/unwrapCodeBlockByKey'>;
}
declare module 'slate-edit-code/dist/transforms/wrapCodeBlock.js' {
declare module.exports: $Exports<'slate-edit-code/dist/transforms/wrapCodeBlock'>;
}
declare module 'slate-edit-code/dist/transforms/wrapCodeBlockByKey.js' {
declare module.exports: $Exports<'slate-edit-code/dist/transforms/wrapCodeBlockByKey'>;
}
declare module 'slate-edit-code/dist/utils/deserializeCode.js' {
declare module.exports: $Exports<'slate-edit-code/dist/utils/deserializeCode'>;
}
declare module 'slate-edit-code/dist/utils/getCurrentCode.js' {
declare module.exports: $Exports<'slate-edit-code/dist/utils/getCurrentCode'>;
}
declare module 'slate-edit-code/dist/utils/getCurrentIndent.js' {
declare module.exports: $Exports<'slate-edit-code/dist/utils/getCurrentIndent'>;
}
declare module 'slate-edit-code/dist/utils/getIndent.js' {
declare module.exports: $Exports<'slate-edit-code/dist/utils/getIndent'>;
}
declare module 'slate-edit-code/dist/utils/index.js' {
declare module.exports: $Exports<'slate-edit-code/dist/utils/index'>;
}
declare module 'slate-edit-code/dist/utils/isInCodeBlock.js' {
declare module.exports: $Exports<'slate-edit-code/dist/utils/isInCodeBlock'>;
}
declare module 'slate-edit-code/dist/validation/index.js' {
declare module.exports: $Exports<'slate-edit-code/dist/validation/index'>;
}
declare module 'slate-edit-code/dist/validation/schema.js' {
declare module.exports: $Exports<'slate-edit-code/dist/validation/schema'>;
}
declare module 'slate-edit-code/dist/validation/validateNode.js' {
declare module.exports: $Exports<'slate-edit-code/dist/validation/validateNode'>;
}
declare module 'slate-edit-code/example/bundle.js' {
declare module.exports: $Exports<'slate-edit-code/example/bundle'>;
}
declare module 'slate-edit-code/example/main.js' {
declare module.exports: $Exports<'slate-edit-code/example/main'>;
}
declare module 'slate-edit-code/example/value.js' {
declare module.exports: $Exports<'slate-edit-code/example/value'>;
}
declare module 'slate-edit-code/tests/all.js' {
declare module.exports: $Exports<'slate-edit-code/tests/all'>;
}
declare module 'slate-edit-code/tests/backspace-empty-code/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/backspace-empty-code/change'>;
}
declare module 'slate-edit-code/tests/backspace-start/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/backspace-start/change'>;
}
declare module 'slate-edit-code/tests/enter-end-offset/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/enter-end-offset/change'>;
}
declare module 'slate-edit-code/tests/enter-mid-offset/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/enter-mid-offset/change'>;
}
declare module 'slate-edit-code/tests/enter-start-offset/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/enter-start-offset/change'>;
}
declare module 'slate-edit-code/tests/enter-with-indent/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/enter-with-indent/change'>;
}
declare module 'slate-edit-code/tests/isincodeblock-true/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/isincodeblock-true/change'>;
}
declare module 'slate-edit-code/tests/on-exit-block/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/on-exit-block/change'>;
}
declare module 'slate-edit-code/tests/paste-middle/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/paste-middle/change'>;
}
declare module 'slate-edit-code/tests/schema-multiline-text/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/schema-multiline-text/change'>;
}
declare module 'slate-edit-code/tests/schema-no-marks/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/schema-no-marks/change'>;
}
declare module 'slate-edit-code/tests/schema-no-orphan-line/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/schema-no-orphan-line/change'>;
}
declare module 'slate-edit-code/tests/schema-only-text-2/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/schema-only-text-2/change'>;
}
declare module 'slate-edit-code/tests/schema-only-text/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/schema-only-text/change'>;
}
declare module 'slate-edit-code/tests/schema-single-text/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/schema-single-text/change'>;
}
declare module 'slate-edit-code/tests/select-all/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/select-all/change'>;
}
declare module 'slate-edit-code/tests/shift-tab-middle-offset/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/shift-tab-middle-offset/change'>;
}
declare module 'slate-edit-code/tests/shift-tab-multilines/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/shift-tab-multilines/change'>;
}
declare module 'slate-edit-code/tests/simulate-key.js' {
declare module.exports: $Exports<'slate-edit-code/tests/simulate-key'>;
}
declare module 'slate-edit-code/tests/tab-middle-offset/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/tab-middle-offset/change'>;
}
declare module 'slate-edit-code/tests/tab-multilines/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/tab-multilines/change'>;
}
declare module 'slate-edit-code/tests/tab-start-offset/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/tab-start-offset/change'>;
}
declare module 'slate-edit-code/tests/togglecodeblock-code/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/togglecodeblock-code/change'>;
}
declare module 'slate-edit-code/tests/togglecodeblock-normal/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/togglecodeblock-normal/change'>;
}
declare module 'slate-edit-code/tests/unwrapcodeblock-multi-lines/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/unwrapcodeblock-multi-lines/change'>;
}
declare module 'slate-edit-code/tests/unwrapcodeblock-normal/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/unwrapcodeblock-normal/change'>;
}
declare module 'slate-edit-code/tests/unwrapcodeblock-selection/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/unwrapcodeblock-selection/change'>;
}
declare module 'slate-edit-code/tests/wrapcodeblock-normal/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/wrapcodeblock-normal/change'>;
}
declare module 'slate-edit-code/tests/wrapcodeblock-selection/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/wrapcodeblock-selection/change'>;
}
declare module 'slate-edit-code/tests/wrapcodeblock-with-inline/change.js' {
declare module.exports: $Exports<'slate-edit-code/tests/wrapcodeblock-with-inline/change'>;
}

View File

@ -1,515 +0,0 @@
// flow-typed signature: bf6ce33431fda5bd9034a47299c20afd
// flow-typed version: <<STUB>>/slate-edit-list_v^0.7.0/flow_v0.49.1
/**
* This is an autogenerated libdef stub for:
*
* 'slate-edit-list'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'slate-edit-list' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'slate-edit-list/dist/getCurrentItem' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/getCurrentList' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/getItemDepth' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/getItemsAtRange' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/getListForItem' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/getPreviousItem' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/index' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/isList' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/isSelectionInList' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/makeSchema' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/onBackspace' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/onEnter' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/onTab' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/options' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/transforms/decreaseItemDepth' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/transforms/increaseItemDepth' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/transforms/splitListItem' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/transforms/unwrapInList' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/transforms/unwrapList' {
declare module.exports: any;
}
declare module 'slate-edit-list/dist/transforms/wrapInList' {
declare module.exports: any;
}
declare module 'slate-edit-list/example/bundle' {
declare module.exports: any;
}
declare module 'slate-edit-list/example/main' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/all' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/backspace-empty-between-inline/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/backspace-end-of-inline/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/backspace-start-of-inline/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/backspace-start-of-item/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/decrease-item-depth-basic/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/decrease-item-depth-long-sublist-with-data/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/decrease-item-depth-long-sublist/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/enter-empty-block-in-item/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/enter-empty-item/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/enter-middle-item/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/enter-nested-item/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/get-current-item/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/get-previous-item/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/increase-item-depth-basic-with-data/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/increase-item-depth-basic/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/increase-item-depth-complex/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/increase-item-depth-existing-sublist/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/increase-item-depth-fifth/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/schema-items-are-list-children/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/schema-items-contain-blocks-2/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/schema-items-contain-blocks/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/schema-lists-contain-items/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/schema-nested-lists/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/schema-ul-ul-li-li/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/shift-enter-middle-item/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/split-item-end/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/split-item-offset/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/split-item-start/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/split-item-sublist-deep/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/split-item-sublist/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/undo-decrease-item-depth-long-sublist/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/undo-increase-item-depth-complex/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/undo-split-item-sublist-deep/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/undo-unwrap-long-list/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/undo-wrap-in-list/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/unwrap-list-multiple-nested/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/unwrap-list-multiple/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/unwrap-list/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/unwrap-long-list/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/unwrap-nested-list/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/wrap-in-list-list-with-data/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/wrap-in-list-list/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/wrap-in-list-multiple-with-data/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/wrap-in-list-multiple/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/wrap-in-list-with-data/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/wrap-in-list/transform' {
declare module.exports: any;
}
declare module 'slate-edit-list/tests/wrap-in-ol/transform' {
declare module.exports: any;
}
// Filename aliases
declare module 'slate-edit-list/dist/getCurrentItem.js' {
declare module.exports: $Exports<'slate-edit-list/dist/getCurrentItem'>;
}
declare module 'slate-edit-list/dist/getCurrentList.js' {
declare module.exports: $Exports<'slate-edit-list/dist/getCurrentList'>;
}
declare module 'slate-edit-list/dist/getItemDepth.js' {
declare module.exports: $Exports<'slate-edit-list/dist/getItemDepth'>;
}
declare module 'slate-edit-list/dist/getItemsAtRange.js' {
declare module.exports: $Exports<'slate-edit-list/dist/getItemsAtRange'>;
}
declare module 'slate-edit-list/dist/getListForItem.js' {
declare module.exports: $Exports<'slate-edit-list/dist/getListForItem'>;
}
declare module 'slate-edit-list/dist/getPreviousItem.js' {
declare module.exports: $Exports<'slate-edit-list/dist/getPreviousItem'>;
}
declare module 'slate-edit-list/dist/index.js' {
declare module.exports: $Exports<'slate-edit-list/dist/index'>;
}
declare module 'slate-edit-list/dist/isList.js' {
declare module.exports: $Exports<'slate-edit-list/dist/isList'>;
}
declare module 'slate-edit-list/dist/isSelectionInList.js' {
declare module.exports: $Exports<'slate-edit-list/dist/isSelectionInList'>;
}
declare module 'slate-edit-list/dist/makeSchema.js' {
declare module.exports: $Exports<'slate-edit-list/dist/makeSchema'>;
}
declare module 'slate-edit-list/dist/onBackspace.js' {
declare module.exports: $Exports<'slate-edit-list/dist/onBackspace'>;
}
declare module 'slate-edit-list/dist/onEnter.js' {
declare module.exports: $Exports<'slate-edit-list/dist/onEnter'>;
}
declare module 'slate-edit-list/dist/onTab.js' {
declare module.exports: $Exports<'slate-edit-list/dist/onTab'>;
}
declare module 'slate-edit-list/dist/options.js' {
declare module.exports: $Exports<'slate-edit-list/dist/options'>;
}
declare module 'slate-edit-list/dist/transforms/decreaseItemDepth.js' {
declare module.exports: $Exports<'slate-edit-list/dist/transforms/decreaseItemDepth'>;
}
declare module 'slate-edit-list/dist/transforms/increaseItemDepth.js' {
declare module.exports: $Exports<'slate-edit-list/dist/transforms/increaseItemDepth'>;
}
declare module 'slate-edit-list/dist/transforms/splitListItem.js' {
declare module.exports: $Exports<'slate-edit-list/dist/transforms/splitListItem'>;
}
declare module 'slate-edit-list/dist/transforms/unwrapInList.js' {
declare module.exports: $Exports<'slate-edit-list/dist/transforms/unwrapInList'>;
}
declare module 'slate-edit-list/dist/transforms/unwrapList.js' {
declare module.exports: $Exports<'slate-edit-list/dist/transforms/unwrapList'>;
}
declare module 'slate-edit-list/dist/transforms/wrapInList.js' {
declare module.exports: $Exports<'slate-edit-list/dist/transforms/wrapInList'>;
}
declare module 'slate-edit-list/example/bundle.js' {
declare module.exports: $Exports<'slate-edit-list/example/bundle'>;
}
declare module 'slate-edit-list/example/main.js' {
declare module.exports: $Exports<'slate-edit-list/example/main'>;
}
declare module 'slate-edit-list/tests/all.js' {
declare module.exports: $Exports<'slate-edit-list/tests/all'>;
}
declare module 'slate-edit-list/tests/backspace-empty-between-inline/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/backspace-empty-between-inline/transform'>;
}
declare module 'slate-edit-list/tests/backspace-end-of-inline/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/backspace-end-of-inline/transform'>;
}
declare module 'slate-edit-list/tests/backspace-start-of-inline/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/backspace-start-of-inline/transform'>;
}
declare module 'slate-edit-list/tests/backspace-start-of-item/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/backspace-start-of-item/transform'>;
}
declare module 'slate-edit-list/tests/decrease-item-depth-basic/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/decrease-item-depth-basic/transform'>;
}
declare module 'slate-edit-list/tests/decrease-item-depth-long-sublist-with-data/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/decrease-item-depth-long-sublist-with-data/transform'>;
}
declare module 'slate-edit-list/tests/decrease-item-depth-long-sublist/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/decrease-item-depth-long-sublist/transform'>;
}
declare module 'slate-edit-list/tests/enter-empty-block-in-item/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/enter-empty-block-in-item/transform'>;
}
declare module 'slate-edit-list/tests/enter-empty-item/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/enter-empty-item/transform'>;
}
declare module 'slate-edit-list/tests/enter-middle-item/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/enter-middle-item/transform'>;
}
declare module 'slate-edit-list/tests/enter-nested-item/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/enter-nested-item/transform'>;
}
declare module 'slate-edit-list/tests/get-current-item/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/get-current-item/transform'>;
}
declare module 'slate-edit-list/tests/get-previous-item/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/get-previous-item/transform'>;
}
declare module 'slate-edit-list/tests/increase-item-depth-basic-with-data/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/increase-item-depth-basic-with-data/transform'>;
}
declare module 'slate-edit-list/tests/increase-item-depth-basic/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/increase-item-depth-basic/transform'>;
}
declare module 'slate-edit-list/tests/increase-item-depth-complex/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/increase-item-depth-complex/transform'>;
}
declare module 'slate-edit-list/tests/increase-item-depth-existing-sublist/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/increase-item-depth-existing-sublist/transform'>;
}
declare module 'slate-edit-list/tests/increase-item-depth-fifth/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/increase-item-depth-fifth/transform'>;
}
declare module 'slate-edit-list/tests/schema-items-are-list-children/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/schema-items-are-list-children/transform'>;
}
declare module 'slate-edit-list/tests/schema-items-contain-blocks-2/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/schema-items-contain-blocks-2/transform'>;
}
declare module 'slate-edit-list/tests/schema-items-contain-blocks/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/schema-items-contain-blocks/transform'>;
}
declare module 'slate-edit-list/tests/schema-lists-contain-items/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/schema-lists-contain-items/transform'>;
}
declare module 'slate-edit-list/tests/schema-nested-lists/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/schema-nested-lists/transform'>;
}
declare module 'slate-edit-list/tests/schema-ul-ul-li-li/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/schema-ul-ul-li-li/transform'>;
}
declare module 'slate-edit-list/tests/shift-enter-middle-item/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/shift-enter-middle-item/transform'>;
}
declare module 'slate-edit-list/tests/split-item-end/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/split-item-end/transform'>;
}
declare module 'slate-edit-list/tests/split-item-offset/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/split-item-offset/transform'>;
}
declare module 'slate-edit-list/tests/split-item-start/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/split-item-start/transform'>;
}
declare module 'slate-edit-list/tests/split-item-sublist-deep/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/split-item-sublist-deep/transform'>;
}
declare module 'slate-edit-list/tests/split-item-sublist/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/split-item-sublist/transform'>;
}
declare module 'slate-edit-list/tests/undo-decrease-item-depth-long-sublist/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/undo-decrease-item-depth-long-sublist/transform'>;
}
declare module 'slate-edit-list/tests/undo-increase-item-depth-complex/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/undo-increase-item-depth-complex/transform'>;
}
declare module 'slate-edit-list/tests/undo-split-item-sublist-deep/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/undo-split-item-sublist-deep/transform'>;
}
declare module 'slate-edit-list/tests/undo-unwrap-long-list/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/undo-unwrap-long-list/transform'>;
}
declare module 'slate-edit-list/tests/undo-wrap-in-list/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/undo-wrap-in-list/transform'>;
}
declare module 'slate-edit-list/tests/unwrap-list-multiple-nested/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/unwrap-list-multiple-nested/transform'>;
}
declare module 'slate-edit-list/tests/unwrap-list-multiple/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/unwrap-list-multiple/transform'>;
}
declare module 'slate-edit-list/tests/unwrap-list/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/unwrap-list/transform'>;
}
declare module 'slate-edit-list/tests/unwrap-long-list/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/unwrap-long-list/transform'>;
}
declare module 'slate-edit-list/tests/unwrap-nested-list/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/unwrap-nested-list/transform'>;
}
declare module 'slate-edit-list/tests/wrap-in-list-list-with-data/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/wrap-in-list-list-with-data/transform'>;
}
declare module 'slate-edit-list/tests/wrap-in-list-list/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/wrap-in-list-list/transform'>;
}
declare module 'slate-edit-list/tests/wrap-in-list-multiple-with-data/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/wrap-in-list-multiple-with-data/transform'>;
}
declare module 'slate-edit-list/tests/wrap-in-list-multiple/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/wrap-in-list-multiple/transform'>;
}
declare module 'slate-edit-list/tests/wrap-in-list-with-data/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/wrap-in-list-with-data/transform'>;
}
declare module 'slate-edit-list/tests/wrap-in-list/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/wrap-in-list/transform'>;
}
declare module 'slate-edit-list/tests/wrap-in-ol/transform.js' {
declare module.exports: $Exports<'slate-edit-list/tests/wrap-in-ol/transform'>;
}

View File

@ -1,46 +0,0 @@
// flow-typed signature: e22a6c4c1a866149f81423ac17cc25b0
// flow-typed version: <<STUB>>/slate-md-serializer_v3.0.3/flow_v0.71.0
/**
* This is an autogenerated libdef stub for:
*
* 'slate-md-serializer'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'slate-md-serializer' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'slate-md-serializer/lib/parser' {
declare module.exports: any;
}
declare module 'slate-md-serializer/lib/renderer' {
declare module.exports: any;
}
declare module 'slate-md-serializer/lib/urls' {
declare module.exports: any;
}
// Filename aliases
declare module 'slate-md-serializer/lib/parser.js' {
declare module.exports: $Exports<'slate-md-serializer/lib/parser'>;
}
declare module 'slate-md-serializer/lib/renderer.js' {
declare module.exports: $Exports<'slate-md-serializer/lib/renderer'>;
}
declare module 'slate-md-serializer/lib/urls.js' {
declare module.exports: $Exports<'slate-md-serializer/lib/urls'>;
}

View File

@ -1,46 +0,0 @@
// flow-typed signature: b7fa5ed1be9584d1ddeb578c01f150fb
// flow-typed version: <<STUB>>/slate-paste-linkify_v^0.5.0/flow_v0.71.0
/**
* This is an autogenerated libdef stub for:
*
* 'slate-paste-linkify'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'slate-paste-linkify' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'slate-paste-linkify/dist/slate-paste-linkify' {
declare module.exports: any;
}
declare module 'slate-paste-linkify/dist/slate-paste-linkify.min' {
declare module.exports: any;
}
declare module 'slate-paste-linkify/lib/index' {
declare module.exports: any;
}
// Filename aliases
declare module 'slate-paste-linkify/dist/slate-paste-linkify.js' {
declare module.exports: $Exports<'slate-paste-linkify/dist/slate-paste-linkify'>;
}
declare module 'slate-paste-linkify/dist/slate-paste-linkify.min.js' {
declare module.exports: $Exports<'slate-paste-linkify/dist/slate-paste-linkify.min'>;
}
declare module 'slate-paste-linkify/lib/index.js' {
declare module.exports: $Exports<'slate-paste-linkify/lib/index'>;
}

View File

@ -1,53 +0,0 @@
// flow-typed signature: 53d8f4be85173c4fe3a45c9b015c1643
// flow-typed version: <<STUB>>/slate-plain-serializer_v0.5.4/flow_v0.71.0
/**
* This is an autogenerated libdef stub for:
*
* 'slate-plain-serializer'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'slate-plain-serializer' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'slate-plain-serializer/dist/slate-plain-serializer' {
declare module.exports: any;
}
declare module 'slate-plain-serializer/dist/slate-plain-serializer.min' {
declare module.exports: any;
}
declare module 'slate-plain-serializer/lib/slate-plain-serializer.es' {
declare module.exports: any;
}
declare module 'slate-plain-serializer/lib/slate-plain-serializer' {
declare module.exports: any;
}
// Filename aliases
declare module 'slate-plain-serializer/dist/slate-plain-serializer.js' {
declare module.exports: $Exports<'slate-plain-serializer/dist/slate-plain-serializer'>;
}
declare module 'slate-plain-serializer/dist/slate-plain-serializer.min.js' {
declare module.exports: $Exports<'slate-plain-serializer/dist/slate-plain-serializer.min'>;
}
declare module 'slate-plain-serializer/lib/slate-plain-serializer.es.js' {
declare module.exports: $Exports<'slate-plain-serializer/lib/slate-plain-serializer.es'>;
}
declare module 'slate-plain-serializer/lib/slate-plain-serializer.js' {
declare module.exports: $Exports<'slate-plain-serializer/lib/slate-plain-serializer'>;
}

View File

@ -1,88 +0,0 @@
// flow-typed signature: 0c893165f0eaff8d04ce05629cdb5482
// flow-typed version: <<STUB>>/slate-prism_v^0.5.0/flow_v0.71.0
/**
* This is an autogenerated libdef stub for:
*
* 'slate-prism'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'slate-prism' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'slate-prism/dist/index' {
declare module.exports: any;
}
declare module 'slate-prism/dist/options' {
declare module.exports: any;
}
declare module 'slate-prism/dist/TOKEN_MARK' {
declare module.exports: any;
}
declare module 'slate-prism/example/bundle' {
declare module.exports: any;
}
declare module 'slate-prism/example/main' {
declare module.exports: any;
}
declare module 'slate-prism/example/value' {
declare module.exports: any;
}
declare module 'slate-prism/lib/index' {
declare module.exports: any;
}
declare module 'slate-prism/lib/options' {
declare module.exports: any;
}
declare module 'slate-prism/lib/TOKEN_MARK' {
declare module.exports: any;
}
// Filename aliases
declare module 'slate-prism/dist/index.js' {
declare module.exports: $Exports<'slate-prism/dist/index'>;
}
declare module 'slate-prism/dist/options.js' {
declare module.exports: $Exports<'slate-prism/dist/options'>;
}
declare module 'slate-prism/dist/TOKEN_MARK.js' {
declare module.exports: $Exports<'slate-prism/dist/TOKEN_MARK'>;
}
declare module 'slate-prism/example/bundle.js' {
declare module.exports: $Exports<'slate-prism/example/bundle'>;
}
declare module 'slate-prism/example/main.js' {
declare module.exports: $Exports<'slate-prism/example/main'>;
}
declare module 'slate-prism/example/value.js' {
declare module.exports: $Exports<'slate-prism/example/value'>;
}
declare module 'slate-prism/lib/index.js' {
declare module.exports: $Exports<'slate-prism/lib/index'>;
}
declare module 'slate-prism/lib/options.js' {
declare module.exports: $Exports<'slate-prism/lib/options'>;
}
declare module 'slate-prism/lib/TOKEN_MARK.js' {
declare module.exports: $Exports<'slate-prism/lib/TOKEN_MARK'>;
}

View File

@ -1,53 +0,0 @@
// flow-typed signature: de15916bf0f775d67e74afc87e297d01
// flow-typed version: <<STUB>>/slate-react_v^0.12.3/flow_v0.71.0
/**
* This is an autogenerated libdef stub for:
*
* 'slate-react'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'slate-react' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'slate-react/dist/slate-react' {
declare module.exports: any;
}
declare module 'slate-react/dist/slate-react.min' {
declare module.exports: any;
}
declare module 'slate-react/lib/slate-react.es' {
declare module.exports: any;
}
declare module 'slate-react/lib/slate-react' {
declare module.exports: any;
}
// Filename aliases
declare module 'slate-react/dist/slate-react.js' {
declare module.exports: $Exports<'slate-react/dist/slate-react'>;
}
declare module 'slate-react/dist/slate-react.min.js' {
declare module.exports: $Exports<'slate-react/dist/slate-react.min'>;
}
declare module 'slate-react/lib/slate-react.es.js' {
declare module.exports: $Exports<'slate-react/lib/slate-react.es'>;
}
declare module 'slate-react/lib/slate-react.js' {
declare module.exports: $Exports<'slate-react/lib/slate-react'>;
}

View File

@ -1,74 +0,0 @@
// flow-typed signature: 8f61874faf7ff2706ad3ad031096ae72
// flow-typed version: <<STUB>>/slate-trailing-block_v^0.5.0/flow_v0.71.0
/**
* This is an autogenerated libdef stub for:
*
* 'slate-trailing-block'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'slate-trailing-block' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'slate-trailing-block/dist/focusAtEnd' {
declare module.exports: any;
}
declare module 'slate-trailing-block/dist/index' {
declare module.exports: any;
}
declare module 'slate-trailing-block/tests/all' {
declare module.exports: any;
}
declare module 'slate-trailing-block/tests/ensure-trailing-multi/change' {
declare module.exports: any;
}
declare module 'slate-trailing-block/tests/ensure-trailing-other/change' {
declare module.exports: any;
}
declare module 'slate-trailing-block/tests/ensure-trailing/change' {
declare module.exports: any;
}
declare module 'slate-trailing-block/tests/just-the-trailing-block/change' {
declare module.exports: any;
}
// Filename aliases
declare module 'slate-trailing-block/dist/focusAtEnd.js' {
declare module.exports: $Exports<'slate-trailing-block/dist/focusAtEnd'>;
}
declare module 'slate-trailing-block/dist/index.js' {
declare module.exports: $Exports<'slate-trailing-block/dist/index'>;
}
declare module 'slate-trailing-block/tests/all.js' {
declare module.exports: $Exports<'slate-trailing-block/tests/all'>;
}
declare module 'slate-trailing-block/tests/ensure-trailing-multi/change.js' {
declare module.exports: $Exports<'slate-trailing-block/tests/ensure-trailing-multi/change'>;
}
declare module 'slate-trailing-block/tests/ensure-trailing-other/change.js' {
declare module.exports: $Exports<'slate-trailing-block/tests/ensure-trailing-other/change'>;
}
declare module 'slate-trailing-block/tests/ensure-trailing/change.js' {
declare module.exports: $Exports<'slate-trailing-block/tests/ensure-trailing/change'>;
}
declare module 'slate-trailing-block/tests/just-the-trailing-block/change.js' {
declare module.exports: $Exports<'slate-trailing-block/tests/just-the-trailing-block/change'>;
}

View File

@ -1,53 +0,0 @@
// flow-typed signature: 3342867a1417391927859a9c905d5366
// flow-typed version: <<STUB>>/slate_v^0.32.5/flow_v0.71.0
/**
* This is an autogenerated libdef stub for:
*
* 'slate'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'slate' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'slate/dist/slate' {
declare module.exports: any;
}
declare module 'slate/dist/slate.min' {
declare module.exports: any;
}
declare module 'slate/lib/slate.es' {
declare module.exports: any;
}
declare module 'slate/lib/slate' {
declare module.exports: any;
}
// Filename aliases
declare module 'slate/dist/slate.js' {
declare module.exports: $Exports<'slate/dist/slate'>;
}
declare module 'slate/dist/slate.min.js' {
declare module.exports: $Exports<'slate/dist/slate.min'>;
}
declare module 'slate/lib/slate.es.js' {
declare module.exports: $Exports<'slate/lib/slate.es'>;
}
declare module 'slate/lib/slate.js' {
declare module.exports: $Exports<'slate/lib/slate'>;
}

View File

@ -140,16 +140,18 @@
"react-portal": "^4.0.0", "react-portal": "^4.0.0",
"react-router-dom": "^5.1.2", "react-router-dom": "^5.1.2",
"react-waypoint": "^9.0.2", "react-waypoint": "^9.0.2",
"rich-markdown-editor": "^9.11.1", "rich-markdown-editor": "^10.0.0",
"sequelize": "^5.21.1", "sequelize": "^5.21.1",
"sequelize-cli": "^5.5.0", "sequelize-cli": "^5.5.0",
"sequelize-encrypted": "^0.1.0", "sequelize-encrypted": "^0.1.0",
"slate": "0.45.0",
"slate-md-serializer": "5.5.0",
"slug": "^1.0.0", "slug": "^1.0.0",
"socket.io": "^2.2.0", "socket.io": "^2.2.0",
"socket.io-redis": "^5.2.0", "socket.io-redis": "^5.2.0",
"socketio-auth": "^0.1.1", "socketio-auth": "^0.1.1",
"string-replace-to-array": "^1.0.3", "string-replace-to-array": "^1.0.3",
"styled-components": "^4.3.2", "styled-components": "^5.0.0",
"styled-components-breakpoint": "^2.1.1", "styled-components-breakpoint": "^2.1.1",
"styled-components-grid": "^2.2.1", "styled-components-grid": "^2.2.1",
"styled-normalize": "^8.0.4", "styled-normalize": "^8.0.4",

BIN
public/images/framer.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 910 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 941 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 B

BIN
public/images/loom.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
public/images/spotify.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
public/images/typeform.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -2,7 +2,8 @@
"verbose": true, "verbose": true,
"rootDir": "..", "rootDir": "..",
"roots": [ "roots": [
"<rootDir>/server" "<rootDir>/server",
"<rootDir>/shared"
], ],
"setupFiles": [ "setupFiles": [
"<rootDir>/__mocks__/console.js", "<rootDir>/__mocks__/console.js",

View File

@ -0,0 +1,19 @@
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('documents', 'backup', {
type: Sequelize.TEXT,
allowNull: true,
});
await queryInterface.addColumn('revisions', 'backup', {
type: Sequelize.SMALLINT,
allowNull: true,
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.removeColumn('documents', 'backup');
await queryInterface.removeColumn('revisions', 'backup');
}
};

View File

@ -1,8 +1,7 @@
// @flow // @flow
import { map, find, compact, uniq } from 'lodash'; import { map, find, compact, uniq } from 'lodash';
import randomstring from 'randomstring';
import MarkdownSerializer from 'slate-md-serializer'; import MarkdownSerializer from 'slate-md-serializer';
import Plain from 'slate-plain-serializer'; import randomstring from 'randomstring';
import Sequelize, { type Transaction } from 'sequelize'; import Sequelize, { type Transaction } from 'sequelize';
import removeMarkdown from '@tommoor/remove-markdown'; import removeMarkdown from '@tommoor/remove-markdown';
@ -15,10 +14,10 @@ import slugify from '../utils/slugify';
import Revision from './Revision'; import Revision from './Revision';
const Op = Sequelize.Op; const Op = Sequelize.Op;
const Markdown = new MarkdownSerializer();
const URL_REGEX = /^[0-9a-zA-Z-_~]*-([a-zA-Z0-9]{10,15})$/; const URL_REGEX = /^[0-9a-zA-Z-_~]*-([a-zA-Z0-9]{10,15})$/;
const serializer = new MarkdownSerializer();
export const DOCUMENT_VERSION = 1; export const DOCUMENT_VERSION = 2;
const createRevision = (doc, options = {}) => { const createRevision = (doc, options = {}) => {
// we don't create revisions for autosaves // we don't create revisions for autosaves
@ -52,7 +51,9 @@ const createUrlId = doc => {
}; };
const beforeCreate = async doc => { const beforeCreate = async doc => {
doc.version = DOCUMENT_VERSION; if (doc.version === undefined) {
doc.version = DOCUMENT_VERSION;
}
return beforeSave(doc); return beforeSave(doc);
}; };
@ -99,6 +100,11 @@ const Document = sequelize.define(
version: DataTypes.SMALLINT, version: DataTypes.SMALLINT,
editorVersion: DataTypes.STRING, editorVersion: DataTypes.STRING,
text: DataTypes.TEXT, text: DataTypes.TEXT,
// backup contains a record of text at the moment it was converted to v2
// this is a safety measure during deployment of new editor and will be
// dropped in a future update
backup: DataTypes.TEXT,
isWelcome: { type: DataTypes.BOOLEAN, defaultValue: false }, isWelcome: { type: DataTypes.BOOLEAN, defaultValue: false },
revisionCount: { type: DataTypes.INTEGER, defaultValue: 0 }, revisionCount: { type: DataTypes.INTEGER, defaultValue: 0 },
archivedAt: DataTypes.DATE, archivedAt: DataTypes.DATE,
@ -453,11 +459,26 @@ Document.prototype.toMarkdown = function() {
}; };
Document.prototype.migrateVersion = function() { Document.prototype.migrateVersion = function() {
// migrate from document version 0 -> 1 means removing the title from the let migrated = false;
// document text attribute.
// migrate from document version 0 -> 1
if (!this.version) { if (!this.version) {
// removing the title from the document text attribute
this.text = this.text.replace(/^#\s(.*)\n/, ''); this.text = this.text.replace(/^#\s(.*)\n/, '');
this.version = 1; this.version = 1;
migrated = true;
}
// migrate from document version 1 -> 2
if (this.version === 1) {
const nodes = serializer.deserialize(this.text);
this.backup = this.text;
this.text = serializer.serialize(nodes, { version: 2 });
this.version = 2;
migrated = true;
}
if (migrated) {
return this.save({ silent: true, hooks: false }); return this.save({ silent: true, hooks: false });
} }
}; };
@ -588,10 +609,17 @@ Document.prototype.getTimestamp = function() {
}; };
Document.prototype.getSummary = function() { Document.prototype.getSummary = function() {
const value = Markdown.deserialize(this.text); const plain = removeMarkdown(unescape(this.text), {
const plain = Plain.serialize(value); stripHTML: false,
});
const lines = compact(plain.split('\n')); const lines = compact(plain.split('\n'));
return lines.length >= 1 ? lines[1] : ''; const notEmpty = lines.length >= 1;
if (this.version) {
return notEmpty ? lines[0] : '';
}
return notEmpty ? lines[1] : '';
}; };
Document.prototype.toJSON = function() { Document.prototype.toJSON = function() {

View File

@ -6,6 +6,155 @@ import { buildDocument, buildCollection, buildTeam } from '../test/factories';
beforeEach(flushdb); beforeEach(flushdb);
beforeEach(jest.resetAllMocks); beforeEach(jest.resetAllMocks);
describe('#getSummary', () => {
test('should strip markdown', async () => {
const document = await buildDocument({
version: 1,
text: `*paragraph*
paragraph 2`,
});
expect(document.getSummary()).toBe('paragraph');
});
test('should strip title when no version', async () => {
const document = await buildDocument({
version: null,
text: `# Heading
*paragraph*`,
});
expect(document.getSummary()).toBe('paragraph');
});
});
describe('#migrateVersion', () => {
test('should maintain empty paragraph under headings', async () => {
const document = await buildDocument({
version: 1,
text: `# Heading
paragraph`,
});
await document.migrateVersion();
expect(document.text).toBe(`# Heading
paragraph`);
});
test('should add breaks under headings with extra paragraphs', async () => {
const document = await buildDocument({
version: 1,
text: `# Heading
paragraph`,
});
await document.migrateVersion();
expect(document.text).toBe(`# Heading
\\
paragraph`);
});
test('should add breaks between paragraphs', async () => {
const document = await buildDocument({
version: 1,
text: `paragraph
paragraph`,
});
await document.migrateVersion();
expect(document.text).toBe(`paragraph
\\
paragraph`);
});
test('should add breaks for multiple empty paragraphs', async () => {
const document = await buildDocument({
version: 1,
text: `paragraph
paragraph`,
});
await document.migrateVersion();
expect(document.text).toBe(`paragraph
\\
\\
paragraph`);
});
test('should add breaks with non-latin characters', async () => {
const document = await buildDocument({
version: 1,
text: `除。
`,
});
await document.migrateVersion();
expect(document.text).toBe(`除。
\\
`);
});
test('should update task list formatting', async () => {
const document = await buildDocument({
version: 1,
text: `[ ] list item
`,
});
await document.migrateVersion();
expect(document.text).toBe(`- [ ] list item
`);
});
test('should update task list with multiple items', async () => {
const document = await buildDocument({
version: 1,
text: `[ ] list item
[ ] list item 2
`,
});
await document.migrateVersion();
expect(document.text).toBe(`- [ ] list item
- [ ] list item 2
`);
});
test('should update checked task list formatting', async () => {
const document = await buildDocument({
version: 1,
text: `[x] list item
`,
});
await document.migrateVersion();
expect(document.text).toBe(`- [x] list item
`);
});
test('should update nested task list formatting', async () => {
const document = await buildDocument({
version: 1,
text: `[x] list item
[ ] list item
[x] list item
`,
});
await document.migrateVersion();
expect(document.text).toBe(`- [x] list item
- [ ] list item
- [x] list item
`);
});
});
describe('#searchForTeam', () => { describe('#searchForTeam', () => {
test('should return search results from public collections', async () => { test('should return search results from public collections', async () => {
const team = await buildTeam(); const team = await buildTeam();

View File

@ -1,5 +1,8 @@
// @flow // @flow
import { DataTypes, sequelize } from '../sequelize'; import { DataTypes, sequelize } from '../sequelize';
import MarkdownSerializer from 'slate-md-serializer';
const serializer = new MarkdownSerializer();
const Revision = sequelize.define('revision', { const Revision = sequelize.define('revision', {
id: { id: {
@ -11,6 +14,11 @@ const Revision = sequelize.define('revision', {
editorVersion: DataTypes.STRING, editorVersion: DataTypes.STRING,
title: DataTypes.STRING, title: DataTypes.STRING,
text: DataTypes.TEXT, text: DataTypes.TEXT,
// backup contains a record of text at the moment it was converted to v2
// this is a safety measure during deployment of new editor and will be
// dropped in a future update
backup: DataTypes.TEXT,
}); });
Revision.associate = models => { Revision.associate = models => {
@ -33,11 +41,26 @@ Revision.associate = models => {
}; };
Revision.prototype.migrateVersion = function() { Revision.prototype.migrateVersion = function() {
// migrate from revision version 0 -> 1 means removing the title from the let migrated = false;
// revision text attribute.
// migrate from document version 0 -> 1
if (!this.version) { if (!this.version) {
// removing the title from the document text attribute
this.text = this.text.replace(/^#\s(.*)\n/, ''); this.text = this.text.replace(/^#\s(.*)\n/, '');
this.version = 1; this.version = 1;
migrated = true;
}
// migrate from document version 1 -> 2
if (this.version === 1) {
const nodes = serializer.deserialize(this.text);
this.backup = this.text;
this.text = serializer.serialize(nodes, { version: 2 });
this.version = 2;
migrated = true;
}
if (migrated) {
return this.save({ silent: true, hooks: false }); return this.save({ silent: true, hooks: false });
} }
}; };

View File

@ -153,6 +153,7 @@ Team.prototype.provisionFirstCollection = async function(userId) {
'utf8' 'utf8'
); );
const document = await Document.create({ const document = await Document.create({
version: 1,
isWelcome: true, isWelcome: true,
parentDocumentId: null, parentDocumentId: null,
collectionId: collection.id, collectionId: collection.id,

View File

@ -1,18 +1,20 @@
The heart of Outline is the document editor. We let you write in whichever way you prefer be it Markdown, WYSIWYG, or taking advantage of the many keyboard shortcuts. The heart of Outline is the document editor. We let you write in whichever way you prefer be it Markdown, WYSIWYG, or taking advantage of the many keyboard shortcuts.
![The formatting toolbar](https://s3.amazonaws.com/dev.beautifulatlas.com/uploads/e2b85962-ca66-4e4c-90d3-b32d30f0610c/754830c0-2aca-467c-82de-2fd6e990b696/Group.png)
## Markdown ## Markdown
If youre comfortable writing markdown then all of the usual shortcuts are supported, for example type \*\*bold\*\* and hit `space` to instantly create bold text. If you forget some syntax or are after a refresher, simply type `?` to access the keyboard shortcut help. If youre comfortable writing markdown then all of the shortcuts you are used to are supported, for example type \*\*bold\*\* to instantly create bold text. If you forget some syntax or are after a quick refresher hit the keyboard icon in the bottom right hand corner for our guide. Learning some of the key Markdown shortcuts will make using Outline faster and more enjoyable!
*Tip:* You can even paste markdown from elsewhere directly into a document. ![The formatting toolbar](/images/screenshots/formatting-toolbar.png)
## Blocks *Tip:* You can also paste markdown or html from elsewhere directly into a document.
The editor supports a variety of content blocks including images, tables, lists, quotes, and more. You can also drag and drop images to include them in your document or paste a link to embed content from one of the many supported [integrations](https://www.getoutline.com/integrations) ## Rich documents
*Tip:* Headings are collapsible, just click the arrow to the left of any heading to temporarily hide the content below. The editor supports a variety of content blocks including images, tables, lists, quotes, videos, and more. Type "/" on an empty line or click on the "+" icon to trigger the block insert menu, you can keep typing to filter it down.
You can also drag and drop images to include them in your document or paste a link to embed content from one of the many supported [integrations](https://www.getoutline.com/integrations)
![The block menu](/images/screenshots/block-menu.png)
## References ## References

View File

@ -1,141 +0,0 @@
/*
Based on Prism template by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/prism/)
Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)
*/
code[class*="language-"],
pre[class*="language-"] {
-webkit-font-smoothing: initial;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 13px;
line-height: 1.375;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
color: #24292e;
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #6a737d;
}
.token.punctuation {
color: #5e6687;
}
.token.namespace {
opacity: .7;
}
.token.operator,
.token.boolean,
.token.number {
color: #d73a49;
}
.token.property {
color: #c08b30;
}
.token.tag {
color: #3d8fd1;
}
.token.string {
color: #032f62;
}
.token.selector {
color: #6679cc;
}
.token.attr-name {
color: #c76b29;
}
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #22a2c9;
}
.token.attr-value,
.token.keyword,
.token.control,
.token.directive,
.token.unit {
color: #d73a49;
}
.token.function {
color: #6f42c1;
}
.token.statement,
.token.regex,
.token.atrule {
color: #22a2c9;
}
.token.placeholder,
.token.variable {
color: #3d8fd1;
}
.token.deleted {
text-decoration: line-through;
}
.token.inserted {
border-bottom: 1px dotted #202746;
text-decoration: none;
}
.token.italic {
font-style: italic;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.important {
color: #c94922;
}
.token.entity {
cursor: help;
}
pre > code.highlight {
outline: 0.4em solid #c94922;
outline-offset: .4em;
}

View File

@ -46,6 +46,8 @@ export const base = {
...spacing, ...spacing,
fontFamily: fontFamily:
"-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen, Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif", "-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen, Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif",
fontFamilyMono:
"'SFMono-Regular',Consolas,'Liberation Mono', Menlo, Courier,monospace",
fontWeight: 400, fontWeight: 400,
backgroundTransition: 'background 100ms ease-in-out', backgroundTransition: 'background 100ms ease-in-out',
zIndex: 100, zIndex: 100,
@ -53,6 +55,31 @@ export const base = {
selected: colors.primary, selected: colors.primary,
buttonBackground: colors.primary, buttonBackground: colors.primary,
buttonText: colors.white, buttonText: colors.white,
textHighlight: '#B3E7FF',
codeComment: '#6a737d',
codePunctuation: '#5e6687',
codeNumber: '#d73a49',
codeProperty: '#c08b30',
codeTag: '#3d8fd1',
codeString: '#032f62',
codeSelector: '#6679cc',
codeAttr: '#c76b29',
codeEntity: '#22a2c9',
codeKeyword: '#d73a49',
codeFunction: '#6f42c1',
codeStatement: '#22a2c9',
codePlaceholder: '#3d8fd1',
codeInserted: '#202746',
codeImportant: '#c94922',
blockToolbarBackground: colors.white,
blockToolbarTrigger: colors.slate,
blockToolbarTriggerIcon: colors.white,
blockToolbarItem: colors.almostBlack,
blockToolbarText: colors.almostBlack,
blockToolbarHoverBackground: colors.slateLight,
blockToolbarDivider: colors.slateLight,
breakpoints: { breakpoints: {
mobile: 0, // targeting all devices mobile: 0, // targeting all devices
@ -102,11 +129,6 @@ export const light = {
tooltipBackground: colors.almostBlack, tooltipBackground: colors.almostBlack,
tooltipText: colors.white, tooltipText: colors.white,
blockToolbarBackground: colors.smoke,
blockToolbarTrigger: colors.slate,
blockToolbarTriggerIcon: colors.white,
blockToolbarItem: colors.almostBlack,
quote: colors.slateLight, quote: colors.slateLight,
codeBackground: colors.smoke, codeBackground: colors.smoke,
codeBorder: colors.smokeDark, codeBorder: colors.smokeDark,
@ -121,7 +143,7 @@ export const dark = {
link: colors.almostWhite, link: colors.almostWhite,
text: colors.almostWhite, text: colors.almostWhite,
textSecondary: lighten(0.2, colors.slate), textSecondary: lighten(0.1, colors.slate),
textTertiary: colors.slate, textTertiary: colors.slate,
placeholder: darken(0.5, '#B1BECC'), placeholder: darken(0.5, '#B1BECC'),
@ -154,14 +176,10 @@ export const dark = {
tooltipBackground: colors.white, tooltipBackground: colors.white,
tooltipText: colors.lightBlack, tooltipText: colors.lightBlack,
blockToolbarBackground: colors.white,
blockToolbarTrigger: colors.almostWhite,
blockToolbarTriggerIcon: colors.almostBlack,
blockToolbarItem: colors.lightBlack,
quote: colors.almostWhite, quote: colors.almostWhite,
codeBackground: colors.almostBlack, codeBackground: colors.black,
codeBorder: colors.black50, codeBorder: colors.black50,
codeString: '#3d8fd1',
embedBorder: colors.black50, embedBorder: colors.black50,
horizontalRule: darken(0.2, colors.slate), horizontalRule: darken(0.2, colors.slate),
}; };

View File

@ -1,28 +0,0 @@
// @flow
import { filter } from 'lodash';
import slugify from 'shared/utils/slugify';
import unescape from 'shared/utils/unescape';
export default function getHeadingsForText(
text: string
): { level: number, title: string, slug: string }[] {
const regex = /^(#{1,6})\s(.*)$/gm;
let match;
let output = [];
while ((match = regex.exec(text)) !== null) {
if (!match) continue;
const level = match[1].length;
const title = unescape(match[2]);
let slug = slugify(title);
const existing = filter(output, { slug });
if (existing.length) {
slug = `${slug}-${existing.length}`;
}
output.push({ level, title, slug });
}
return output;
}

View File

@ -1,23 +0,0 @@
/* eslint-disable flowtype/require-valid-file-annotation */
import getHeadingsForText from './getHeadingsForText';
it('should return an array of document headings', () => {
const response = getHeadingsForText(`
# Header
## Subheading
`);
expect(response.length).toBe(2);
expect(response[0].level).toBe(1);
expect(response[0].title).toBe('Header');
expect(response[1].level).toBe(2);
expect(response[1].title).toBe('Subheading');
});
it('should unescape special characters', () => {
const response = getHeadingsForText(`# Header <\\>`);
expect(response.length).toBe(1);
expect(response[0].title).toBe('Header <>');
});

View File

@ -1,29 +1,39 @@
// @flow // @flow
import MarkdownSerializer from 'slate-md-serializer'; import { parser } from 'rich-markdown-editor';
const Markdown = new MarkdownSerializer();
export default function parseDocumentIds(text: string): string[] { export default function parseDocumentIds(text: string): string[] {
const value = Markdown.deserialize(text); const value = parser.parse(text);
let links = []; let links = [];
function findLinks(node) { function findLinks(node) {
if (node.type === 'link') { // get text nodes
const href = node.data.get('href'); if (node.type.name === 'text') {
// get marks for text nodes
node.marks.forEach(mark => {
// any of the marks links?
if (mark.type.name === 'link') {
const { href } = mark.attrs;
// any of the links to other docs?
if (href.startsWith('/doc')) {
const tokens = href.replace(/\/$/, '').split('/');
const lastToken = tokens[tokens.length - 1];
if (href.startsWith('/doc')) { // don't return the same link more than once
const tokens = href.replace(/\/$/, '').split('/'); if (!links.includes(lastToken)) {
const lastToken = tokens[tokens.length - 1]; links.push(lastToken);
links.push(lastToken); }
} }
}
});
} }
if (!node.nodes) { if (!node.content.size) {
return; return;
} }
node.nodes.forEach(findLinks); node.content.descendants(findLinks);
} }
findLinks(value.document); findLinks(value);
return links; return links;
} }

View File

@ -1,20 +1,38 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import parseDocumentIds from './parseDocumentIds'; import parseDocumentIds from './parseDocumentIds';
it('should return an array of document ids', () => { it('should not return non links', () => {
expect(parseDocumentIds(`# Header`).length).toBe(0); expect(parseDocumentIds(`# Header`).length).toBe(0);
expect( });
parseDocumentIds(`# Header
it('should return an array of document ids', () => {
const result = parseDocumentIds(`# Header
[title](/doc/test-456733) [internal](/doc/test-456733)
`)[0] `);
).toBe('test-456733');
expect(result.length).toBe(1);
expect(result[0]).toBe('test-456733');
});
it('should not return duplicate document ids', () => {
expect(parseDocumentIds(`# Header`).length).toBe(0);
const result = parseDocumentIds(`# Header
[internal](/doc/test-456733)
[another link to the same doc](/doc/test-456733)
`);
expect(result.length).toBe(1);
expect(result[0]).toBe('test-456733');
}); });
it('should not return non document links', () => { it('should not return non document links', () => {
expect(parseDocumentIds(`[title](http://www.google.com)`).length).toBe(0); expect(parseDocumentIds(`[google](http://www.google.com)`).length).toBe(0);
}); });
it('should not return non document relative links', () => { it('should not return non document relative links', () => {
expect(parseDocumentIds(`[title](/developers)`).length).toBe(0); expect(parseDocumentIds(`[relative](/developers)`).length).toBe(0);
}); });

777
yarn.lock

File diff suppressed because it is too large Load Diff