Compare commits

..

15 Commits

Author SHA1 Message Date
Tom Moor d4c594423f More email styling 2021-07-08 22:38:42 -04:00
Tom Moor 2bf237d54b Merge branch 'main' into email-diff 2021-07-08 21:20:18 -04:00
Tom Moor 3565e68725 Merge branch 'main' into email-diff 2021-07-06 20:43:51 -04:00
Tom Moor 61039e9d0d Allow images in email diff 2021-06-25 09:41:34 -07:00
Tom Moor 6d09122d56 test: Deletion 2021-06-24 20:10:42 -07:00
Tom Moor 5fb6097153 Improved diff 2021-06-23 23:58:32 -07:00
Tom Moor ec17874568 Remove test harness 2021-06-22 07:35:38 -07:00
Tom Moor 40c3e9e85f test 2021-06-22 07:27:55 -07:00
Tom Moor 9f739f3788 Merge main 2021-06-22 07:26:45 -07:00
Tom Moor f6837b4742 wip 2021-06-20 23:15:04 -07:00
Tom Moor 1560e3c9f7 refactor 2021-06-20 12:49:15 -07:00
Tom Moor ca74908dc5 test 2021-06-20 00:20:37 -07:00
Tom Moor de7ec1119b Integrate into mailer, basic styling 2021-06-19 23:50:36 -07:00
Tom Moor 2093b4297f Merge main 2021-06-19 17:05:19 -07:00
Nan Yu 3df82c500b wip 2021-02-21 11:52:00 -08:00
60 changed files with 908 additions and 618 deletions
-2
View File
@@ -15,7 +15,6 @@ type Props = {|
target?: "_blank",
as?: string | React.ComponentType<*>,
hide?: () => void,
level?: number,
|};
const MenuItem = ({
@@ -89,7 +88,6 @@ export const MenuAnchor = styled.a`
margin: 0;
border: 0;
padding: 12px;
padding-left: ${(props) => 12 + props.level * 10}px;
width: 100%;
min-height: 32px;
background: none;
+41 -9
View File
@@ -9,11 +9,49 @@ import {
MenuItem as BaseMenuItem,
} from "reakit/Menu";
import styled from "styled-components";
import Header from "./Header";
import MenuItem, { MenuAnchor } from "./MenuItem";
import Separator from "./Separator";
import ContextMenu from ".";
import { type MenuItem as TMenuItem } from "types";
type TMenuItem =
| {|
title: React.Node,
to: string,
visible?: boolean,
selected?: boolean,
disabled?: boolean,
|}
| {|
title: React.Node,
onClick: (event: SyntheticEvent<>) => void | Promise<void>,
visible?: boolean,
selected?: boolean,
disabled?: boolean,
|}
| {|
title: React.Node,
href: string,
visible?: boolean,
selected?: boolean,
disabled?: boolean,
|}
| {|
title: React.Node,
visible?: boolean,
disabled?: boolean,
style?: Object,
hover?: boolean,
items: TMenuItem[],
|}
| {|
type: "separator",
visible?: boolean,
|}
| {|
type: "heading",
visible?: boolean,
title: React.Node,
|};
type Props = {|
items: TMenuItem[],
@@ -90,8 +128,7 @@ function Template({ items, ...menu }: Props): React.Node {
key={index}
disabled={item.disabled}
selected={item.selected}
level={item.level}
target={item.href.startsWith("#") ? undefined : "_blank"}
target="_blank"
{...menu}
>
{item.title}
@@ -130,11 +167,6 @@ function Template({ items, ...menu }: Props): React.Node {
return <Separator key={index} />;
}
if (item.type === "heading") {
return <Header>{item.title}</Header>;
}
console.warn("Unrecognized menu item", item);
return null;
});
}
+12 -27
View File
@@ -6,16 +6,14 @@ import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import {
fadeIn,
fadeAndSlideUp,
fadeAndSlideDown,
mobileContextMenu,
fadeAndScaleIn,
fadeAndSlideIn,
} from "shared/styles/animations";
import usePrevious from "hooks/usePrevious";
type Props = {|
"aria-label": string,
visible?: boolean,
placement?: string,
animating?: boolean,
children: React.Node,
onOpen?: () => void,
@@ -46,25 +44,13 @@ export default function ContextMenu({
return (
<>
<Menu hideOnClickOutside preventBodyScroll {...rest}>
{(props) => {
// kind of hacky, but this is an effective way of telling which way
// the menu will _actually_ be placed when taking into account screen
// positioning.
const topAnchor = props.style.top === "0";
const rightAnchor = props.placement === "bottom-end";
return (
<Position {...props}>
<Background
dir="auto"
topAnchor={topAnchor}
rightAnchor={rightAnchor}
>
{rest.visible || rest.animating ? children : null}
</Background>
</Position>
);
}}
{(props) => (
<Position {...props}>
<Background dir="auto">
{rest.visible || rest.animating ? children : null}
</Background>
</Position>
)}
</Menu>
{(rest.visible || rest.animating) && (
<Portal>
@@ -105,7 +91,7 @@ const Position = styled.div`
`;
const Background = styled.div`
animation: ${mobileContextMenu} 200ms ease;
animation: ${fadeAndSlideIn} 200ms ease;
transform-origin: 50% 100%;
max-width: 100%;
background: ${(props) => props.theme.menuBackground};
@@ -123,10 +109,9 @@ const Background = styled.div`
}
${breakpoint("tablet")`
animation: ${(props) =>
props.topAnchor ? fadeAndSlideDown : fadeAndSlideUp} 200ms ease;
animation: ${fadeAndScaleIn} 200ms ease;
transform-origin: ${(props) =>
props.rightAnchor === "bottom-end" ? "75%" : "25%"} 0;
props.left !== undefined ? "25%" : "75%"} 0;
max-width: 276px;
background: ${(props) => props.theme.menuBackground};
box-shadow: ${(props) => props.theme.menuShadow};
+2 -5
View File
@@ -66,9 +66,6 @@ function DocumentListItem(props: Props, ref) {
!document.isDraft && !document.isArchived && !document.isTemplate;
const can = policies.abilities(currentTeam.id);
const handleMenuOpen = React.useCallback(() => setMenuOpen(true), []);
const handleMenuClosed = React.useCallback(() => setMenuOpen(false), []);
return (
<DocumentLink
ref={ref}
@@ -146,8 +143,8 @@ function DocumentListItem(props: Props, ref) {
<DocumentMenu
document={document}
showPin={showPin}
onOpen={handleMenuOpen}
onClose={handleMenuClosed}
onOpen={() => setMenuOpen(true)}
onClose={() => setMenuOpen(false)}
modal={false}
/>
</Actions>
+1 -5
View File
@@ -72,10 +72,6 @@ const Actions = styled(Flex)`
flex-basis: 0;
min-width: auto;
padding-left: 8px;
${breakpoint("tablet")`
position: unset;
`};
`;
const Wrapper = styled(Flex)`
@@ -88,12 +84,12 @@ const Wrapper = styled(Flex)`
transform: translate3d(0, 0, 0);
backdrop-filter: blur(20px);
min-height: 56px;
justify-content: flex-start;
@media print {
display: none;
}
justify-content: flex-start;
${breakpoint("tablet")`
padding: ${(props) => (props.isCompact ? "12px" : `24px 24px 0`)};
justify-content: "center";
+2 -2
View File
@@ -4,7 +4,7 @@ import { transparentize } from "polished";
import * as React from "react";
import { Portal } from "react-portal";
import styled from "styled-components";
import { fadeAndSlideDown } from "shared/styles/animations";
import { fadeAndSlideIn } from "shared/styles/animations";
import parseDocumentSlug from "shared/utils/parseDocumentSlug";
import DocumentsStore from "stores/DocumentsStore";
import HoverPreviewDocument from "components/HoverPreviewDocument";
@@ -136,7 +136,7 @@ function HoverPreview({ node, ...rest }: Props) {
}
const Animate = styled.div`
animation: ${fadeAndSlideDown} 150ms ease;
animation: ${fadeAndSlideIn} 150ms ease;
@media print {
display: none;
-4
View File
@@ -29,10 +29,6 @@ const RealInput = styled.input`
background: none;
color: ${(props) => props.theme.text};
height: 30px;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&:disabled,
&::placeholder {
-2
View File
@@ -6,7 +6,6 @@ import {
fr,
es,
it,
ja,
ko,
ptBR,
pt,
@@ -24,7 +23,6 @@ const locales = {
es_ES: es,
fr_FR: fr,
it_IT: it,
ja_JP: ja,
ko_KR: ko,
pt_BR: ptBR,
pt_PT: pt,
+20 -21
View File
@@ -1,4 +1,5 @@
// @flow
import { observer } from "mobx-react";
import * as React from "react";
import Document from "models/Document";
import DocumentListItem from "components/DocumentListItem";
@@ -18,26 +19,24 @@ type Props = {|
showTemplate?: boolean,
|};
const PaginatedDocumentList = React.memo<Props>(function PaginatedDocumentList({
empty,
heading,
documents,
fetch,
options,
...rest
}: Props) {
return (
<PaginatedList
items={documents}
empty={empty}
heading={heading}
fetch={fetch}
options={options}
renderItem={(item) => (
<DocumentListItem key={item.id} document={item} {...rest} />
)}
/>
);
});
@observer
class PaginatedDocumentList extends React.Component<Props> {
render() {
const { empty, heading, documents, fetch, options, ...rest } = this.props;
return (
<PaginatedList
items={documents}
empty={empty}
heading={heading}
fetch={fetch}
options={options}
renderItem={(item) => (
<DocumentListItem key={item.id} document={item} {...rest} />
)}
/>
);
}
}
export default PaginatedDocumentList;
+8 -49
View File
@@ -1,26 +1,14 @@
// @flow
import { m } from "framer-motion";
import * as React from "react";
import { NavLink, Route } from "react-router-dom";
import { NavLink } from "react-router-dom";
import styled, { withTheme } from "styled-components";
import { type Theme } from "types";
type Props = {
theme: Theme,
children: React.Node,
};
const NavLinkWithChildrenFunc = ({ to, exact = false, children, ...rest }) => (
<Route path={to} exact={exact}>
{({ match }) => (
<NavLink to={to} {...rest}>
{children(match)}
</NavLink>
)}
</Route>
);
const TabLink = styled(NavLinkWithChildrenFunc)`
const TabLink = styled(NavLink)`
position: relative;
display: inline-flex;
align-items: center;
@@ -32,48 +20,19 @@ const TabLink = styled(NavLinkWithChildrenFunc)`
&:hover {
color: ${(props) => props.theme.textSecondary};
border-bottom: 3px solid ${(props) => props.theme.divider};
padding-bottom: 5px;
}
`;
const Active = styled(m.div)`
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 3px;
width: 100%;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
background: ${(props) => props.theme.textSecondary};
`;
const transition = {
type: "spring",
stiffness: 500,
damping: 30,
};
function Tab({ theme, children, ...rest }: Props) {
function Tab({ theme, ...rest }: Props) {
const activeStyle = {
paddingBottom: "5px",
borderBottom: `3px solid ${theme.textSecondary}`,
color: theme.textSecondary,
};
return (
<TabLink {...rest} activeStyle={activeStyle}>
{(match) => (
<>
{children}
{match && (
<Active
layoutId="underline"
initial={false}
transition={transition}
/>
)}
</>
)}
</TabLink>
);
return <TabLink {...rest} activeStyle={activeStyle} />;
}
export default withTheme(Tab);
+5 -57
View File
@@ -1,40 +1,13 @@
// @flow
import { AnimateSharedLayout } from "framer-motion";
import { transparentize } from "polished";
import * as React from "react";
import styled from "styled-components";
import useWindowSize from "hooks/useWindowSize";
const Nav = styled.nav`
border-bottom: 1px solid ${(props) => props.theme.divider};
margin: 12px 0;
overflow-y: auto;
white-space: nowrap;
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
&:after {
content: "";
position: absolute;
top: 0;
right: 0;
width: 50px;
height: 100%;
pointer-events: none;
background: ${(props) =>
props.$shadowVisible
? `linear-gradient(
90deg,
${transparentize(1, props.theme.background)} 0%,
${props.theme.background} 100%
)`
: `transparent`};
}
transition: opacity 250ms ease-out;
`;
// When sticky we need extra background coverage around the sides otherwise
@@ -57,36 +30,11 @@ export const Separator = styled.span`
margin-top: 6px;
`;
const Tabs = ({ children }: {| children: React.Node |}) => {
const ref = React.useRef<?HTMLDivElement>();
const [shadowVisible, setShadow] = React.useState(false);
const { width } = useWindowSize();
const updateShadows = React.useCallback(() => {
const c = ref.current;
if (!c) return;
const scrollLeft = c.scrollLeft;
const wrapperWidth = c.scrollWidth - c.clientWidth;
const fade = !!(wrapperWidth - scrollLeft !== 0);
if (fade !== shadowVisible) {
setShadow(fade);
}
}, [shadowVisible]);
React.useEffect(() => {
updateShadows();
}, [width, updateShadows]);
const Tabs = (props: {}) => {
return (
<AnimateSharedLayout>
<Sticky>
<Nav ref={ref} onScroll={updateShadows} $shadowVisible={shadowVisible}>
{children}
</Nav>
</Sticky>
</AnimateSharedLayout>
<Sticky>
<Nav {...props}></Nav>
</Sticky>
);
};
+9 -16
View File
@@ -1,6 +1,5 @@
// @flow
import "focus-visible";
import { LazyMotion } from "framer-motion";
import { createBrowserHistory } from "history";
import { Provider } from "mobx-react";
import * as React from "react";
@@ -50,10 +49,6 @@ if ("serviceWorker" in window.navigator) {
});
}
// Make sure to return the specific export containing the feature bundle.
const loadFeatures = () =>
import("./utils/motion.js").then((res) => res.default);
if (element) {
const App = () => (
<React.StrictMode>
@@ -61,17 +56,15 @@ if (element) {
<Analytics>
<Theme>
<ErrorBoundary>
<LazyMotion features={loadFeatures}>
<Router history={history}>
<>
<PageTheme />
<ScrollToTop>
<Routes />
</ScrollToTop>
<Toasts />
</>
</Router>
</LazyMotion>
<Router history={history}>
<>
<PageTheme />
<ScrollToTop>
<Routes />
</ScrollToTop>
<Toasts />
</>
</Router>
</ErrorBoundary>
</Theme>
</Analytics>
-76
View File
@@ -1,76 +0,0 @@
// @flow
import { observer } from "mobx-react";
import { TableOfContentsIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { MenuButton, useMenuState } from "reakit/Menu";
import Button from "components/Button";
import ContextMenu from "components/ContextMenu";
import Template from "components/ContextMenu/Template";
import { type MenuItem } from "types";
type Props = {|
headings: { title: string, level: number, id: string }[],
|};
function TableOfContentsMenu({ headings }: Props) {
const menu = useMenuState({
modal: true,
unstable_preventOverflow: true,
unstable_fixed: true,
unstable_flip: true,
});
const { t } = useTranslation();
const minHeading = headings.reduce(
(memo, heading) => (heading.level < memo ? heading.level : memo),
Infinity
);
const items: MenuItem[] = React.useMemo(() => {
let i = [
{
type: "heading",
visible: true,
title: t("Contents"),
},
...headings.map((heading) => ({
href: `#${heading.id}`,
title: t(heading.title),
level: heading.level - minHeading,
})),
];
if (i.length === 1) {
i.push({
href: "#",
title: t("Headings you add to the document will appear here"),
disabled: true,
});
}
return i;
}, [t, headings, minHeading]);
return (
<>
<MenuButton {...menu}>
{(props) => (
<Button
{...props}
icon={<TableOfContentsIcon />}
iconColor="currentColor"
borderOnHover
neutral
/>
)}
</MenuButton>
<ContextMenu {...menu} aria-label={t("Table of contents")}>
<Template {...menu} items={items} />
</ContextMenu>
</>
);
}
export default observer(TableOfContentsMenu);
@@ -70,13 +70,11 @@ const Wrapper = styled("div")`
display: none;
position: sticky;
top: 80px;
max-height: calc(100vh - 80px);
box-shadow: 1px 0 0 ${(props) => props.theme.divider};
margin-top: 40px;
margin-right: 2em;
min-height: 40px;
overflow-y: auto;
${breakpoint("desktopLarge")`
margin-left: -16em;
@@ -374,7 +374,6 @@ class DocumentScene extends React.Component<Props> {
sharedTree={this.props.sharedTree}
goBack={this.goBack}
onSave={this.onSave}
headings={headings}
/>
<MaxWidth
archived={document.isArchived}
-13
View File
@@ -24,7 +24,6 @@ import useMobile from "hooks/useMobile";
import useStores from "hooks/useStores";
import DocumentMenu from "menus/DocumentMenu";
import NewChildDocumentMenu from "menus/NewChildDocumentMenu";
import TableOfContentsMenu from "menus/TableOfContentsMenu";
import TemplatesMenu from "menus/TemplatesMenu";
import { type NavigationNode } from "types";
import { metaDisplay } from "utils/keyboard";
@@ -47,7 +46,6 @@ type Props = {|
publish?: boolean,
autosave?: boolean,
}) => void,
headings: { title: string, level: number, id: string }[],
|};
function DocumentHeader({
@@ -62,7 +60,6 @@ function DocumentHeader({
publishingIsDisabled,
sharedTree,
onSave,
headings,
}: Props) {
const { t } = useTranslation();
const { auth, ui, policies } = useStores();
@@ -156,11 +153,6 @@ function DocumentHeader({
}
actions={
<>
{isMobile && (
<TocWrapper>
<TableOfContentsMenu headings={headings} />
</TocWrapper>
)}
{!isPublishing && isSaving && <Status>{t("Saving")}</Status>}
<Collaborators
document={document}
@@ -282,9 +274,4 @@ const Status = styled(Action)`
color: ${(props) => props.theme.slate};
`;
const TocWrapper = styled(Action)`
position: absolute;
left: 42px;
`;
export default observer(DocumentHeader);
-2
View File
@@ -7,5 +7,3 @@ import Adapter from "enzyme-adapter-react-16";
Enzyme.configure({ adapter: new Adapter() });
global.localStorage = localStorage;
require("jest-fetch-mock").enableMocks();
-41
View File
@@ -58,44 +58,3 @@ export type SearchResult = {
context: string,
document: Document,
};
export type MenuItem =
| {|
title: React.Node,
to: string,
visible?: boolean,
selected?: boolean,
disabled?: boolean,
|}
| {|
title: React.Node,
onClick: (event: SyntheticEvent<>) => void | Promise<void>,
visible?: boolean,
selected?: boolean,
disabled?: boolean,
|}
| {|
title: React.Node,
href: string,
visible?: boolean,
selected?: boolean,
disabled?: boolean,
level?: number,
|}
| {|
title: React.Node,
visible?: boolean,
disabled?: boolean,
style?: Object,
hover?: boolean,
items: MenuItem[],
|}
| {|
type: "separator",
visible?: boolean,
|}
| {|
type: "heading",
visible?: boolean,
title: React.Node,
|};
+1 -4
View File
@@ -1,5 +1,4 @@
// @flow
import retry from "fetch-retry";
import invariant from "invariant";
import { map, trim } from "lodash";
import { getCookie } from "tiny-cookie";
@@ -25,8 +24,6 @@ const CF_AUTHORIZATION = getCookie("CF_Authorization");
// if the cookie is set, we must pass it with all ApiClient requests
const CREDENTIALS = CF_AUTHORIZATION ? "same-origin" : "omit";
const fetchWithRetry = retry(fetch);
class ApiClient {
baseUrl: string;
userAgent: string;
@@ -95,7 +92,7 @@ class ApiClient {
let response;
try {
response = await fetchWithRetry(urlToFetch, {
response = await fetch(urlToFetch, {
method,
body,
headers,
-3
View File
@@ -1,3 +0,0 @@
// @flow
import { domMax } from "framer-motion";
export default domMax;
+4 -5
View File
@@ -73,13 +73,11 @@
"emoji-regex": "^6.5.1",
"es6-error": "^4.1.1",
"exports-loader": "^0.6.4",
"fetch-retry": "^4.1.1",
"fetch-with-proxy": "^3.0.1",
"file-loader": "^1.1.6",
"flow-typed": "^3.3.1",
"focus-visible": "^5.1.0",
"fractional-index": "^1.0.0",
"framer-motion": "^4.1.17",
"fs-extra": "^4.0.2",
"http-errors": "1.4.0",
"i18next": "^19.8.3",
@@ -88,6 +86,7 @@
"imports-loader": "0.6.5",
"invariant": "^2.2.2",
"ioredis": "^4.24.3",
"isomorphic-fetch": "2.2.1",
"joplin-turndown-plugin-gfm": "^1.0.12",
"js-search": "^1.4.2",
"json-loader": "0.5.4",
@@ -111,6 +110,7 @@
"mobx": "^4.15.4",
"mobx-react": "^6.3.1",
"natural-sort": "^1.0.0",
"node-htmldiff": "^0.9.3",
"nodemailer": "^6.4.16",
"outline-icons": "^1.27.0",
"oy-vey": "^0.10.0",
@@ -162,8 +162,8 @@
"styled-normalize": "^8.0.4",
"tiny-cookie": "^2.3.1",
"tmp": "^0.2.1",
"turndown": "^7.1.1",
"utf8": "^3.0.0",
"turndown": "^6.0.0",
"utf8": "^2.1.0",
"uuid": "^8.3.2",
"validator": "5.2.0"
},
@@ -190,7 +190,6 @@
"html-webpack-plugin": "3.2.0",
"i18next-parser": "^3.3.0",
"jest-cli": "^26.0.0",
"jest-fetch-mock": "^3.0.3",
"koa-webpack-dev-middleware": "^1.4.5",
"koa-webpack-hot-middleware": "^1.0.3",
"nodemon": "^1.19.4",
+4 -2
View File
@@ -41,11 +41,13 @@ export const CollectionNotificationEmail = ({
<Body>
<Heading>{collection.name}</Heading>
<p>
{actor.name} {eventName} the collection "{collection.name}".
{actor.name} {eventName} the collection {collection.name}.
</p>
<EmptySpace height={10} />
<p>
<Button href={`${process.env.URL}${collection.url}`}>
<Button
href={`${process.env.URL}${collection.url}?ref=notification-email`}
>
Open Collection
</Button>
</p>
+225 -6
View File
@@ -1,8 +1,10 @@
// @flow
import * as React from "react";
import theme from "../../shared/styles/theme";
import { User, Document, Team, Collection } from "../models";
import Body from "./components/Body";
import Button from "./components/Button";
import Diff from "./components/Diff";
import EmailTemplate from "./components/EmailLayout";
import EmptySpace from "./components/EmptySpace";
import Footer from "./components/Footer";
@@ -15,6 +17,7 @@ export type Props = {
document: Document,
collection: Collection,
eventName: string,
summary: string,
unsubscribeUrl: string,
};
@@ -38,26 +41,34 @@ export const DocumentNotificationEmail = ({
document,
collection,
eventName = "published",
summary,
unsubscribeUrl,
}: Props) => {
const link = `${team.url}${document.url}?ref=notification-email`;
return (
<EmailTemplate>
<Header />
<Body>
<Heading>
"{document.title}" {eventName}
{document.title} {eventName}
</Heading>
<p>
{actor.name} {eventName} the document "{document.title}", in the{" "}
{collection.name} collection.
</p>
<hr />
<EmptySpace height={10} />
<p>{document.getSummary()}</p>
<EmptySpace height={10} />
{summary && (
<>
<EmptySpace height={20} />
<Diff href={link}>
<div dangerouslySetInnerHTML={{ __html: summary }} />
</Diff>
<EmptySpace height={20} />
</>
)}
<p>
<Button href={`${team.url}${document.url}`}>Open Document</Button>
<Button href={link}>Open Document</Button>
</p>
</Body>
@@ -65,3 +76,211 @@ export const DocumentNotificationEmail = ({
</EmailTemplate>
);
};
export const css = `
font-family: ${theme.fontFamily};
font-weight: ${theme.fontWeight};
font-size: 1em;
line-height: 1.7em;
pre {
white-space: pre-wrap;
}
img {
text-align: center;
max-width: 100%;
max-height: 75vh;
clear: both;
}
img.image-right-50 {
float: right;
width: 50%;
margin-left: 2em;
margin-bottom: 1em;
clear: initial;
}
img.image-left-50 {
float: left;
width: 50%;
margin-right: 2em;
margin-bottom: 1em;
clear: initial;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 1em 0 0.5em;
font-weight: 500;
}
.notice {
display: flex;
align-items: center;
background: ${theme.noticeInfoBackground};
color: ${theme.noticeInfoText};
border-radius: 4px;
padding: 8px 16px;
margin: 8px 0;
}
.notice-tip {
background: ${theme.noticeTipBackground};
color: ${theme.noticeTipText};
}
.notice-warning {
background: ${theme.noticeWarningBackground};
color: ${theme.noticeWarningText};
}
b,
strong {
font-weight: 600;
}
p {
margin: 0;
}
a {
color: ${theme.link};
}
ins {
background-color: #128a2929;
text-decoration: none;
}
del {
background-color: ${theme.slateLight};
color: ${theme.slate};
text-decoration: strikethrough;
}
hr {
position: relative;
height: 1em;
border: 0;
}
hr:before {
content: "";
display: block;
position: absolute;
border-top: 1px solid ${theme.horizontalRule};
top: 0.5em;
left: 0;
right: 0;
}
hr.page-break {
page-break-after: always;
}
hr.page-break:before {
border-top: 1px dashed ${theme.horizontalRule};
}
code {
border-radius: 4px;
border: 1px solid ${theme.codeBorder};
padding: 3px 4px;
font-family: ${theme.fontFamilyMono};
font-size: 85%;
}
mark {
border-radius: 1px;
color: ${theme.textHighlightForeground};
background: ${theme.textHighlight};
a {
color: ${theme.textHighlightForeground};
}
}
ul {
padding-left: 0;
}
.checkbox-list-item {
list-style: none;
padding: 4px 0;
margin: 0;
}
.checkbox {
font-size: 0;
display: block;
float: left;
white-space: nowrap;
width: 12px;
height: 12px;
margin-top: 2px;
margin-right: 8px;
border: 1px solid ${theme.textSecondary};
border-radius: 3px;
}
pre {
display: block;
overflow-x: auto;
padding: 0.75em 1em;
line-height: 1.4em;
position: relative;
background: ${theme.codeBackground};
border-radius: 4px;
border: 1px solid ${theme.codeBorder};
-webkit-font-smoothing: initial;
font-family: ${theme.fontFamilyMono};
font-size: 13px;
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;
margin: 0;
code {
font-size: 13px;
background: none;
padding: 0;
border: 0;
}
}
table {
width: 100%;
border-collapse: collapse;
border-radius: 4px;
margin-top: 1em;
box-sizing: border-box;
* {
box-sizing: border-box;
}
tr {
position: relative;
border-bottom: 1px solid ${theme.tableDivider};
}
td,
th {
position: relative;
vertical-align: top;
border: 1px solid ${theme.tableDivider};
position: relative;
padding: 4px 8px;
min-width: 100px;
}
}
`;
+1 -1
View File
@@ -47,7 +47,7 @@ export const InviteEmail = ({
</p>
<EmptySpace height={10} />
<p>
<Button href={teamUrl}>Join now</Button>
<Button href={`${teamUrl}?ref=invite-email`}>Join now</Button>
</p>
</Body>
+3 -1
View File
@@ -43,7 +43,9 @@ export const WelcomeEmail = ({ teamUrl }: Props) => {
</p>
<EmptySpace height={10} />
<p>
<Button href={`${teamUrl}/home`}>View my dashboard</Button>
<Button href={`${teamUrl}/home?ref=welcome-email`}>
View my dashboard
</Button>
</p>
</Body>
+25
View File
@@ -0,0 +1,25 @@
// @flow
import * as React from "react";
import theme from "../../../shared/styles/theme";
type Props = {|
children: React.Node,
href?: string,
|};
export default ({ children, ...rest }: Props) => {
const style = {
borderRadius: "4px",
background: theme.secondaryBackground,
padding: ".5em 1em",
color: theme.text,
display: "block",
textDecoration: "none",
};
return (
<a width="100%" style={style} {...rest}>
{children}
</a>
);
};
+2 -2
View File
@@ -3,9 +3,9 @@ import { Table, TBody, TR, TD } from "oy-vey";
import * as React from "react";
import theme from "../../../shared/styles/theme";
type Props = {
type Props = {|
children: React.Node,
};
|};
export default (props: Props) => (
<Table width="550" padding="40">
+2
View File
@@ -100,6 +100,8 @@ export type RevisionEvent = {
documentId: string,
collectionId: string,
teamId: string,
actorId: string,
modelId: string,
};
export type CollectionImportEvent = {
+3 -1
View File
@@ -13,6 +13,7 @@ import {
type Props as DocumentNotificationEmailT,
DocumentNotificationEmail,
documentNotificationEmailText,
css as documentNotificationEmailCSS,
} from "./emails/DocumentNotificationEmail";
import { ExportEmail, exportEmailText } from "./emails/ExportEmail";
import {
@@ -146,8 +147,9 @@ export class Mailer {
this.sendMail({
to: opts.to,
title: `${opts.document.title}${opts.eventName}`,
previewText: `${opts.actor.name} ${opts.eventName} a new document`,
previewText: `${opts.actor.name} ${opts.eventName} a document`,
html: <DocumentNotificationEmail {...opts} />,
headCSS: documentNotificationEmailCSS,
text: documentNotificationEmailText(opts),
});
};
+143 -22
View File
@@ -1,32 +1,60 @@
// @flow
import debug from "debug";
import type { DocumentEvent, CollectionEvent, Event } from "../events";
import type {
DocumentEvent,
RevisionEvent,
CollectionEvent,
Event,
} from "../events";
import mailer from "../mailer";
import {
View,
Document,
Team,
Collection,
Revision,
User,
NotificationSetting,
Attachment,
} from "../models";
import { Op } from "../sequelize";
import markdownDiff from "../utils/markdownDiff";
import parseAttachmentIds from "../utils/parseAttachmentIds";
import { getSignedImageUrl } from "../utils/s3";
const log = debug("services");
async function replaceImageAttachments(text: string) {
const attachmentIds = parseAttachmentIds(text);
await Promise.all(
attachmentIds.map(async (id) => {
const attachment = await Attachment.findByPk(id);
if (attachment) {
const accessUrl = await getSignedImageUrl(attachment.key, 86400 * 4);
text = text.replace(attachment.redirectUrl, accessUrl);
}
})
);
return text;
}
export default class Notifications {
async on(event: Event) {
switch (event.name) {
case "documents.publish":
case "documents.update.debounced":
return this.documentUpdated(event);
return this.documentPublished(event);
case "revisions.create":
return this.revisionCreated(event);
case "collections.create":
return this.collectionCreated(event);
default:
}
}
async documentUpdated(event: DocumentEvent) {
async documentPublished(event: DocumentEvent) {
// never send notifications when batch importing documents
if (event.data && event.data.source === "import") return;
@@ -45,10 +73,7 @@ export default class Notifications {
[Op.ne]: document.lastModifiedById,
},
teamId: document.teamId,
event:
event.name === "documents.publish"
? "documents.publish"
: "documents.update",
event: "documents.publish",
},
include: [
{
@@ -59,25 +84,14 @@ export default class Notifications {
],
});
const eventName =
event.name === "documents.publish" ? "published" : "updated";
const eventName = "published";
for (const setting of notificationSettings) {
// For document updates we only want to send notifications if
// the document has been edited by the user with this notification setting
// This could be replaced with ability to "follow" in the future
if (
eventName === "updated" &&
!document.collaboratorIds.includes(setting.userId)
) {
return;
}
// Check the user has access to the collection this document is in. Just
// because they were a collaborator once doesn't mean they still are.
const collectionIds = await setting.user.collectionIds();
if (!collectionIds.includes(document.collectionId)) {
return;
continue;
}
// If this user has viewed the document since the last update was made
@@ -96,7 +110,7 @@ export default class Notifications {
log(
`suppressing notification to ${setting.userId} because update viewed`
);
return;
continue;
}
mailer.documentNotification({
@@ -105,12 +119,119 @@ export default class Notifications {
document,
team,
collection,
summary: document.getSummary(),
actor: document.updatedBy,
unsubscribeUrl: setting.unsubscribeUrl,
});
}
}
async revisionCreated(event: RevisionEvent) {
const revision = await Revision.findByPk(event.modelId, {
include: [
{
model: Document,
as: "document",
include: [
{
model: Collection,
as: "collection",
},
],
},
],
});
if (!revision) return;
const { document } = revision;
const { collection } = document;
if (!collection || !document) return;
const team = await Team.findByPk(document.teamId);
if (!team) return;
const notificationSettings = await NotificationSetting.findAll({
where: {
userId: {
[Op.ne]: revision.userId,
},
teamId: document.teamId,
event: "documents.update",
},
include: [
{
model: User,
required: true,
as: "user",
},
],
});
const eventName = "updated";
for (const setting of notificationSettings) {
// For document updates we only want to send notifications if
// the document has been edited by the user with this notification setting
// This could be replaced with ability to "follow" in the future
if (!document.collaboratorIds.includes(setting.userId)) {
continue;
}
// Check the user has access to the collection this document is in. Just
// because they were a collaborator once doesn't mean they still are.
const collectionIds = await setting.user.collectionIds();
if (!collectionIds.includes(document.collectionId)) {
continue;
}
// If this user has viewed the document since the last update was made
// then we can avoid sending them a useless notification, yay.
const view = await View.findOne({
where: {
userId: setting.userId,
documentId: event.documentId,
updatedAt: {
[Op.gt]: document.updatedAt,
},
},
});
if (view) {
log(
`suppressing notification to ${setting.userId} because update viewed`
);
continue;
}
const previous = await Revision.findOne({
where: {
documentId: document.id,
createdAt: {
[Op.lt]: revision.createdAt,
},
},
order: [["createdAt", "DESC"]],
});
let summary = markdownDiff(previous ? previous.text : "", revision.text);
console.log(summary);
summary = await replaceImageAttachments(summary);
console.log(summary);
mailer.documentNotification({
to: setting.user.email,
eventName,
document,
team,
collection,
summary,
actor: revision.user,
unsubscribeUrl: setting.unsubscribeUrl,
});
}
}
async collectionCreated(event: CollectionEvent) {
const collection = await Collection.findByPk(event.collectionId, {
include: [
+12 -6
View File
@@ -1,6 +1,6 @@
/* eslint-disable flowtype/require-valid-file-annotation */
import mailer from "../mailer";
import { View, NotificationSetting } from "../models";
import { View, NotificationSetting, Revision } from "../models";
import { buildDocument, buildCollection, buildUser } from "../test/factories";
import { flushdb } from "../test/support";
import NotificationsService from "./notifications";
@@ -89,9 +89,10 @@ describe("documents.publish", () => {
});
});
describe("documents.update.debounced", () => {
describe("revisions.create", () => {
test("should send a notification to other collaborator", async () => {
const document = await buildDocument();
const revision = await Revision.createFromDocument(document);
const collaborator = await buildUser({ teamId: document.teamId });
document.collaboratorIds = [collaborator.id];
await document.save();
@@ -103,8 +104,9 @@ describe("documents.update.debounced", () => {
});
await Notifications.on({
name: "documents.update.debounced",
name: "revisions.create",
documentId: document.id,
modelId: revision.id,
collectionId: document.collectionId,
teamId: document.teamId,
actorId: document.createdById,
@@ -115,6 +117,7 @@ describe("documents.update.debounced", () => {
test("should not send a notification if viewed since update", async () => {
const document = await buildDocument();
const revision = await Revision.createFromDocument(document);
const collaborator = await buildUser({ teamId: document.teamId });
document.collaboratorIds = [collaborator.id];
await document.save();
@@ -128,9 +131,10 @@ describe("documents.update.debounced", () => {
await View.touch(document.id, collaborator.id, true);
await Notifications.on({
name: "documents.update.debounced",
name: "revisions.create",
documentId: document.id,
collectionId: document.collectionId,
modelId: revision.id,
teamId: document.teamId,
actorId: document.createdById,
});
@@ -138,12 +142,13 @@ describe("documents.update.debounced", () => {
expect(mailer.documentNotification).not.toHaveBeenCalled();
});
test("should not send a notification to last editor", async () => {
test("should not send a notification to the last user that modified", async () => {
const user = await buildUser();
const document = await buildDocument({
teamId: user.teamId,
lastModifiedById: user.id,
});
const revision = await Revision.createFromDocument(document);
await NotificationSetting.create({
userId: user.id,
@@ -152,8 +157,9 @@ describe("documents.update.debounced", () => {
});
await Notifications.on({
name: "documents.update.debounced",
name: "revisions.create",
documentId: document.id,
modelId: revision.id,
collectionId: document.collectionId,
teamId: document.teamId,
actorId: document.createdById,
+10 -2
View File
@@ -1,6 +1,6 @@
// @flow
import type { DocumentEvent, RevisionEvent } from "../events";
import { Revision, Document } from "../models";
import { Revision, Document, Event } from "../models";
export default class Revisions {
async on(event: DocumentEvent | RevisionEvent) {
@@ -22,7 +22,15 @@ export default class Revisions {
return;
}
await Revision.createFromDocument(document);
const revision = await Revision.createFromDocument(document);
Event.add({
name: "revisions.create",
documentId: document.id,
collectionId: document.collectionId,
modelId: revision.id,
teamId: document.teamId,
actorId: revision.userId,
});
break;
}
+34
View File
@@ -0,0 +1,34 @@
# Heading 1
## Heading 2
This is a test paragraph
This is a second test paragraph. This is a second sentence.
This is a another test paragraph. This is a another sentence.
- list item 1
- list item 2
```
this is a codeblock
```
:::info
This is an info block
:::
!!This is a placeholder!!
==this is a highlight==
- [ ] checklist item 1
- [ ] checklist item 2
- [x] checklist item 3
same on both sides
same on both sides
same on both sides
+37
View File
@@ -0,0 +1,37 @@
# Heading 1
## Heading 2
This is a test paragraph
This is a second test paragraph. This is a second sentence.
This is a another test paragraph. This is a another sentence.
- list item 1
```
this is a codeblock
```
This is a new paragraph.
:::info
This is an info block
:::
!!This is a placeholder!!
==this is a highlight==
- [x] checklist item 1
- [x] checklist item 2
- [ ] checklist item 3
- [ ] checklist item 4
- [x] checklist item 5
same on both sides
same on both sides
same on both sides
+2
View File
@@ -2,9 +2,11 @@
require("dotenv").config({ silent: true });
// test environment variables
process.env.URL = "http://localhost:3000";
process.env.DATABASE_URL = process.env.DATABASE_URL_TEST;
process.env.NODE_ENV = "test";
process.env.GOOGLE_CLIENT_ID = "123";
process.env.AZURE_CLIENT_ID = "";
process.env.SLACK_KEY = "123";
process.env.DEPLOYMENT = "";
process.env.ALLOWED_DOMAINS = "allowed-domain.com";
@@ -0,0 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should diff a complex document 1`] = `
"<p>This is a second test paragraph. This is a second sentence.</p>
<p>This is a another test paragraph. This is a another sentence.</p>
<ul>
<li>list item 1</li>
<li data-diff-node=\\"del\\" data-operation-index=\\"1\\"><del data-operation-index=\\"1\\">list item 2</del></li></ul>
<pre><code>this is a codeblock
</code></pre><p data-diff-node=\\"ins\\" data-operation-index=\\"3\\"><ins data-operation-index=\\"3\\">This is a new paragraph.</ins></p>
<div class=\\"notice notice-info\\">
<p>This is an info block</p>
</div>
<p><span class=\\"placeholder\\">This is a placeholder</span></p>
<p><span class=\\"highlight\\">this is a highlight</span></p>
<ul>
<li class=\\"checkbox-list-item\\"><span class=\\"checkbox checked\\">[<del data-operation-index=\\"5\\"> ]</del><ins data-operation-index=\\"5\\">x]</ins></span>checklist item 1</li><li class=\\"checkbox-list-item\\"><span class=\\"checkbox checked\\" data-diff-node=\\"ins\\" data-operation-index=\\"7\\"><ins data-operation-index=\\"7\\">[x]</ins></span><ins data-operation-index=\\"7\\">checklist item 2</ins></li>
<li class=\\"checkbox-list-item\\"><span class=\\"checkbox \\">[ ]</span>checklist item <del data-operation-index=\\"9\\">2</del><ins data-operation-index=\\"9\\">3</ins></li><li class=\\"checkbox-list-item\\"><span class=\\"checkbox \\" data-diff-node=\\"ins\\" data-operation-index=\\"9\\"><ins data-operation-index=\\"9\\">[ ]</ins></span><ins data-operation-index=\\"9\\">checklist item 4</ins></li>
<li class=\\"checkbox-list-item\\"><span class=\\"checkbox checked\\">[x]</span>checklist item <del data-operation-index=\\"11\\">3</del><ins data-operation-index=\\"11\\">5</ins></li>
</ul>"
`;
exports[`should return everything inserted when previously empty 1`] = `
"<h1 data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">Heading 1</ins></h1><h2 data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">Heading 2</ins></h2><p data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">This is a test paragraph</ins></p><p data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">This is a second test paragraph. This is a second sentence.</ins></p><p data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">This is a another test paragraph. This is a another sentence.</ins></p><ul data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><li data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">list item 1</ins></li><li data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">list item 2</ins></li></ul><pre data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><code data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">this is a codeblock
</ins></code></pre><div class=\\"notice notice-info\\" data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><p data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">This is an info block</ins></p></div><p data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><span class=\\"placeholder\\" data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">This is a placeholder</ins></span></p><p data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><span class=\\"highlight\\" data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">this is a highlight</ins></span></p><ul data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><li class=\\"checkbox-list-item\\" data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><span class=\\"checkbox \\" data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">[ ]</ins></span><ins data-operation-index=\\"0\\">checklist item 1</ins></li><li class=\\"checkbox-list-item\\" data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><span class=\\"checkbox \\" data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">[ ]</ins></span><ins data-operation-index=\\"0\\">checklist item 2</ins></li><li class=\\"checkbox-list-item\\" data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><span class=\\"checkbox checked\\" data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">[x]</ins></span><ins data-operation-index=\\"0\\">checklist item 3</ins></li></ul><p data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">same on both sides</ins></p><p data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">same on both sides</ins></p><p data-diff-node=\\"ins\\" data-operation-index=\\"0\\"><ins data-operation-index=\\"0\\">same on both sides</ins></p>"
`;
+57
View File
@@ -0,0 +1,57 @@
// @flow
import { findIndex, findLastIndex } from "lodash";
import diff from "node-htmldiff";
import { renderToHtml } from "rich-markdown-editor";
export default function markdownDiff(
before: string,
after: string,
fullDiff: boolean = false,
buffer: number = 1
) {
// The basic idea here is to first render the Markdown to HTML, then diff the
// HTML - both sides will have valid HTML so we should have a valid diff as well
const beforeHtml = renderToHtml(before);
const afterHtml = renderToHtml(after);
const diffHtml = diff(beforeHtml, afterHtml);
if (fullDiff) {
return diffHtml;
}
if (before === after) {
return "";
}
// Split diff at paragraphs and find the first and last changed tags
// so we can chop around paragraphs rather than return the entire document.
//
// In an ideal world we'd use an AST here and parse that rather than be doing
// operations on strings. I hope this can be revisted in the future with an
// improved diffing library.
const newParagraph = /(?:^|\n)<p>/;
let lines = diffHtml.split(newParagraph);
const firstChangedLineIndex = findIndex(
lines,
(value) => value.includes("<ins ") || value.includes("<del ")
);
const lastChangedLineIndex = findLastIndex(
lines,
(value) => value.includes("</ins>") || value.includes("</del>")
);
const start = Math.max(0, firstChangedLineIndex - buffer);
const end = Math.min(lines.length, lastChangedLineIndex + buffer);
lines = lines.slice(start, end);
if (!lines.length) {
return "";
}
return [start > 0 ? "" : undefined, ...lines]
.filter((x) => x !== undefined)
.join("\n<p>")
.trim();
}
+55
View File
@@ -0,0 +1,55 @@
// @flow
import fs from "fs";
import path from "path";
import markdownDiff from "./markdownDiff";
it("should diff a complex document", async () => {
const before = await fs.promises.readFile(
path.resolve(process.cwd(), "server", "test", "fixtures", "complex.md"),
"utf8"
);
const after = await fs.promises.readFile(
path.resolve(
process.cwd(),
"server",
"test",
"fixtures",
"complexModified.md"
),
"utf8"
);
const diff = markdownDiff(before, after);
expect(diff).toMatchSnapshot();
});
it("should return empty string when both sides are empty", () => {
const diff = markdownDiff("", "");
expect(diff).toEqual("");
});
it("should return everything inserted when previously empty", async () => {
const content = await fs.promises.readFile(
path.resolve(process.cwd(), "server", "test", "fixtures", "complex.md"),
"utf8"
);
const diff = markdownDiff("", content);
expect(diff).toMatchSnapshot();
});
it("should return empty for changed nodes", async () => {
// Note: This isn't ideal behavior, but it is current behavior. If the diffing
// library is improved then we could potentially render the old + new heading
// with ins/del tags as appropriate.
const diff = markdownDiff("# Heading", "## Heading");
expect(diff).toEqual("");
});
it("should return deleted nodes", async () => {
const diff = markdownDiff("![caption](/image.png)", "");
expect(diff).toEqual(
'<p><del data-operation-index="0"><img src="/image.png" alt="caption"></del></p>'
);
});
+2 -2
View File
@@ -163,13 +163,13 @@ export const deleteFromS3 = (key: string) => {
.promise();
};
export const getSignedImageUrl = async (key: string) => {
export const getSignedImageUrl = async (key: string, expires: number = 60) => {
const isDocker = process.env.AWS_S3_UPLOAD_BUCKET_URL.match(/http:\/\/s3:/);
const params = {
Bucket: AWS_S3_UPLOAD_BUCKET_NAME,
Key: key,
Expires: 60,
Expires: expires,
};
return isDocker
-1
View File
@@ -13,7 +13,6 @@ export const languageOptions = [
{ label: "Español (España)", value: "es_ES" },
{ label: "Français (France)", value: "fr_FR" },
{ label: "Italiano (Italia)", value: "it_IT" },
{ label: "日本語 (Japanese)", value: "ja_JP" },
{ label: "한국어 (Korean)", value: "ko_KR" },
{ label: "Português (Brazil)", value: "pt_BR" },
{ label: "Português (Portugal)", value: "pt_PT" },
+2 -2
View File
@@ -152,12 +152,12 @@
"Path to document": "Pfad zum Dokument",
"Group member options": "Optionen für Gruppenmitglieder",
"Remove": "Entfernen",
"Collection": "Sammlung",
"New document": "Neues Dokument",
"Import document": "Dokument importieren",
"Edit": "Bearbeiten",
"Permissions": "Berechtigungen",
"Delete": "Löschen",
"Collection": "Sammlung",
"Collection permissions": "Berechtigungen für Sammlungen",
"Edit collection": "Sammlung bearbeiten",
"Delete collection": "Sammlung löschen",
@@ -206,7 +206,6 @@
"Share options": "Teilen-Einstellungen",
"Go to document": "Zum Dokument gehen",
"Revoke link": "Link widerrufen",
"Table of contents": "Inhaltsverzeichnis",
"By {{ author }}": "Von {{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Sind Sie sicher, dass Sie {{ userName }} zu einem Administrator machen möchten? Administratoren können Team- und Rechnungsinformationen ändern.",
"Are you sure you want to make {{ userName }} a member?": "Sind Sie sicher, dass Sie {{ userName }} zu einem Mitglied machen möchten?",
@@ -338,6 +337,7 @@
"Move current document": "Aktuelles Dokument verschieben",
"Jump to search": "Zur Suche springen",
"Jump to home": "Zurück zum Dashboard",
"Table of contents": "Inhaltsverzeichnis",
"Toggle navigation": "Navigation ausblenden",
"Focus search input": "Fokussiere Suchergebnis",
"Open this guide": "Diese Anleitung öffnen",
+1 -3
View File
@@ -206,9 +206,6 @@
"Share options": "Share options",
"Go to document": "Go to document",
"Revoke link": "Revoke link",
"Contents": "Contents",
"Headings you add to the document will appear here": "Headings you add to the document will appear here",
"Table of contents": "Table of contents",
"By {{ author }}": "By {{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.",
"Are you sure you want to make {{ userName }} a member?": "Are you sure you want to make {{ userName }} a member?",
@@ -340,6 +337,7 @@
"Move current document": "Move current document",
"Jump to search": "Jump to search",
"Jump to home": "Jump to home",
"Table of contents": "Table of contents",
"Toggle navigation": "Toggle navigation",
"Focus search input": "Focus search input",
"Open this guide": "Open this guide",
+24 -24
View File
@@ -1,7 +1,7 @@
{
"currently editing": "actualmente editando",
"currently viewing": "viendo actualmente",
"previously edited": "editado previamente",
"previously edited": "previously edited",
"You": "Usted",
"Viewers": "Viewers",
"Sorry, an error occurred saving the collection": "Lo sentimos, se produjo un error al guardar la colección",
@@ -30,9 +30,9 @@
"in": "en",
"nested document": "documento anidado",
"nested document_plural": "documentos anidados",
"Viewed by": "Visto por",
"only you": "sólo tú",
"person": "persona",
"Viewed by": "Viewed by",
"only you": "only you",
"person": "person",
"people": "people",
"Currently editing": "Currently editing",
"Currently viewing": "Currently viewing",
@@ -55,8 +55,8 @@
"Delete column": "Eliminar columna",
"Delete row": "Borrar fila",
"Delete table": "Eliminar tabla",
"Delete image": "Eliminar imagen",
"Download image": "Descargar imagen",
"Delete image": "Delete image",
"Download image": "Download image",
"Float left": "Float left",
"Float right": "Float right",
"Center large": "Center large",
@@ -109,7 +109,7 @@
"Dismiss": "Descartar",
"Keyboard shortcuts": "Atajos del teclado",
"Back": "Atras",
"Collections could not be loaded, please reload the app": "No se pudieron cargar las colecciones, por favor recarga la aplicación",
"Collections could not be loaded, please reload the app": "Collections could not be loaded, please reload the app",
"New collection": "Nueva colección",
"Collections": "Colecciones",
"Untitled": "Sin título",
@@ -136,8 +136,8 @@
"Installation": "Instalación",
"Unstar": "Eliminar de favoritos",
"Star": "Favorito",
"Previous page": "Página anterior",
"Next page": "Página siguiente",
"Previous page": "Previous page",
"Next page": "Next page",
"Could not import file": "No se pudo importar el archivo",
"Appearance": "Apariencia",
"System": "Sistema",
@@ -152,12 +152,12 @@
"Path to document": "Ruta al documento",
"Group member options": "Opciones de miembros del grupo",
"Remove": "Eliminar",
"Collection": "Colección",
"New document": "Nuevo documento",
"Import document": "Importar documento",
"Edit": "Editar",
"Permissions": "Permisos",
"Delete": "Borrar",
"Collection": "Colección",
"Collection permissions": "Permisos de colección",
"Edit collection": "Editar colección",
"Delete collection": "Eliminar colección",
@@ -181,7 +181,7 @@
"Create template": "Crear plantilla",
"Duplicate": "Duplicar",
"Unpublish": "Cancelar publicación",
"Permanently delete": "Eliminar permanentemente",
"Permanently delete": "Permanently delete",
"Move": "Mover",
"History": "Historial",
"Download": "Descargar",
@@ -206,20 +206,19 @@
"Share options": "Share options",
"Go to document": "Ir al documento",
"Revoke link": "Revocar enlace",
"Table of contents": "Tabla de contenido",
"By {{ author }}": "Por {{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "¿Estás seguro de que quieres convertir a {{ userName }} en administrador? Los administradores pueden modificar el equipo e información de facturación.",
"Are you sure you want to make {{ userName }} a member?": "¿Estás seguro de que quieres convertir a {{ userName }} en miembro?",
"Are you sure you want to make {{ userName }} a read-only viewer? They will not be able to edit any content": "Are you sure you want to make {{ userName }} a read-only viewer? They will not be able to edit any content",
"Are you sure you want to suspend this account? Suspended users will be prevented from logging in.": "¿Está seguro que desea suspender esta cuenta? Los usuarios suspendidos no podrán iniciar sesión.",
"User options": "Opciones de usuario",
"User options": "User options",
"Make {{ userName }} a member": "Make {{ userName }} a member",
"Make {{ userName }} a viewer": "Make {{ userName }} a viewer",
"Make {{ userName }} an admin…": "Hacer a {{ userName }} un administrador…",
"Revoke invite": "Revocar Invitación",
"Activate account": "Activar cuenta",
"Suspend account": "Suspender cuenta",
"API token created": "Token de API creado",
"API token created": "API token created",
"Name your token something that will help you to remember it's use in the future, for example \"local development\", \"production\", or \"continuous integration\".": "Name your token something that will help you to remember it's use in the future, for example \"local development\", \"production\", or \"continuous integration\".",
"Documents": "Documentos",
"The document archive is empty at the moment.": "El archivo de documento está vacío en este momento.",
@@ -227,9 +226,9 @@
"<em>{{ collectionName }}</em> doesnt contain any\n documents yet.": "<em>{{ collectionName }}</em> aún no contiene\n documentos.",
"Get started by creating a new one!": "¡Empiece creando uno nuevo!",
"Create a document": "Crear documento",
"Manage permissions": "Administrar permisos",
"This collection is only visible to those given access": "Esta colección sólo es visible a aquellos con acceso",
"Private": "Privado",
"Manage permissions": "Manage permissions",
"This collection is only visible to those given access": "This collection is only visible to those given access",
"Private": "Private",
"Pinned": "Fijado",
"Recently updated": "Recientemente actualizado",
"Recently published": "Recientemente publicado",
@@ -242,7 +241,7 @@
"Alphabetical": "Alfabético",
"Public document sharing": "Public document sharing",
"When enabled, documents can be shared publicly on the internet.": "When enabled, documents can be shared publicly on the internet.",
"Public sharing is currently disabled in the team security settings.": "Compartir públicamente está desactivado en las configuraciones de seguridad del equipo.",
"Public sharing is currently disabled in the team security settings.": "Public sharing is currently disabled in the team security settings.",
"Saving": "Guardando",
"Save": "Guardar",
"Collections are for grouping your documents. They work best when organized around a topic or internal team — Product or Engineering for example.": "Collections are for grouping your documents. They work best when organized around a topic or internal team — Product or Engineering for example.",
@@ -269,15 +268,15 @@
"Never signed in": "No ha iniciado sesión nunca",
"Invited": "Invitado",
"Admin": "Admin",
"{{ userName }} was removed from the collection": "{{ userName }} fue quitado de la colección",
"{{ userName }} was removed from the collection": "{{ userName }} was removed from the collection",
"Could not remove user": "No se pudo remover al usuario",
"{{ userName }} permissions were updated": "Se actualizaron los permisos de {{ userName }}",
"Could not update user": "No se pudo actualizar el usuario",
"The {{ groupName }} group was removed from the collection": "El grupo {{ groupName }} fue quitado de la colección",
"Could not remove group": "No se pudo eliminar el grupo",
"{{ userName }} permissions were updated": "{{ userName }} permissions were updated",
"Could not update user": "Could not update user",
"The {{ groupName }} group was removed from the collection": "The {{ groupName }} group was removed from the collection",
"Could not remove group": "Could not remove group",
"{{ groupName }} permissions were updated": "{{ groupName }} permissions were updated",
"Default access permissions were updated": "Default access permissions were updated",
"Could not update permissions": "No se pudo actualizar los permisos",
"Could not update permissions": "Could not update permissions",
"The <em>{{ collectionName }}</em> collection is private. Team members have no access to it by default.": "The <em>{{ collectionName }}</em> collection is private. Team members have no access to it by default.",
"Team members can view documents in the <em>{{ collectionName }}</em> collection by default.": "Team members can view documents in the <em>{{ collectionName }}</em> collection by default.",
"Team members can view and edit documents in the <em>{{ collectionName }}</em> collection by\n default.": "Team members can view and edit documents in the <em>{{ collectionName }}</em> collection by\n default.",
@@ -338,6 +337,7 @@
"Move current document": "Mover el documento actual",
"Jump to search": "Ir a la búsqueda",
"Jump to home": "Jump to home",
"Table of contents": "Tabla de contenido",
"Toggle navigation": "Toggle navigation",
"Focus search input": "Focus search input",
"Open this guide": "Abra esta guía",
+2 -2
View File
@@ -152,12 +152,12 @@
"Path to document": "Path to document",
"Group member options": "Group member options",
"Remove": "حذف",
"Collection": "مجموعه",
"New document": "سند جدید",
"Import document": "وارد کردن سند",
"Edit": "ویرایش",
"Permissions": "دسترسی‌ها",
"Delete": "حذف",
"Collection": "مجموعه",
"Collection permissions": "دسترسی‌های مجموعه",
"Edit collection": "ویرایش مجموعه",
"Delete collection": "حذف مجموعه",
@@ -206,7 +206,6 @@
"Share options": "Share options",
"Go to document": "Go to document",
"Revoke link": "Revoke link",
"Table of contents": "Table of contents",
"By {{ author }}": "By {{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.",
"Are you sure you want to make {{ userName }} a member?": "Are you sure you want to make {{ userName }} a member?",
@@ -338,6 +337,7 @@
"Move current document": "Move current document",
"Jump to search": "Jump to search",
"Jump to home": "Jump to home",
"Table of contents": "Table of contents",
"Toggle navigation": "Toggle navigation",
"Focus search input": "Focus search input",
"Open this guide": "Open this guide",
+2 -2
View File
@@ -152,12 +152,12 @@
"Path to document": "Chemin d'accès au document",
"Group member options": "Options des membres du groupe",
"Remove": "Supprimer",
"Collection": "Collection",
"New document": "Nouveau document",
"Import document": "Importer un document",
"Edit": "Modifier",
"Permissions": "Permissions",
"Delete": "Supprimer",
"Collection": "Collection",
"Collection permissions": "Permissions sur les collections",
"Edit collection": "Modifier la collection",
"Delete collection": "Supprimer la collection",
@@ -206,7 +206,6 @@
"Share options": "Options de partage",
"Go to document": "Accéder au document",
"Revoke link": "Révoquer le lien",
"Table of contents": "Table des matières",
"By {{ author }}": "Par {{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Voulez-vous vraiment faire de {{ userName }} un administrateur ? Les administrateurs peuvent modifier les informations relatives à l'équipe et à la facturation.",
"Are you sure you want to make {{ userName }} a member?": "Êtes-vous sûr de vouloir faire de {{ userName }} un membre ?",
@@ -338,6 +337,7 @@
"Move current document": "Déplacer le document courant",
"Jump to search": "Aller à la recherche",
"Jump to home": "Jump to home",
"Table of contents": "Table des matières",
"Toggle navigation": "Toggle navigation",
"Focus search input": "Focus search input",
"Open this guide": "Ouvrir ce guide",
+2 -2
View File
@@ -152,12 +152,12 @@
"Path to document": "Percorso del documento",
"Group member options": "Opzioni membri del gruppo",
"Remove": "Elimina",
"Collection": "Raccolta",
"New document": "Nuovo documento",
"Import document": "Importa documento",
"Edit": "Modifica",
"Permissions": "Permessi",
"Delete": "Cancella",
"Collection": "Raccolta",
"Collection permissions": "Collection permissions",
"Edit collection": "Modifica raccolta",
"Delete collection": "Elimina raccolta",
@@ -206,7 +206,6 @@
"Share options": "Opzioni di condivisione",
"Go to document": "Vai al documento",
"Revoke link": "Revoca il link",
"Table of contents": "Indice contenuti",
"By {{ author }}": "Di {{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Sei sicuro di voler rendere {{ userName }} un amministratore? Gli amministratori possono modificare le informazioni sul team e sulla fatturazione.",
"Are you sure you want to make {{ userName }} a member?": "Sei sicuro di voler rendere {{ userName }} un membro?",
@@ -338,6 +337,7 @@
"Move current document": "Sposta il documento corrente",
"Jump to search": "Vai alla ricerca",
"Jump to home": "Jump to home",
"Table of contents": "Indice contenuti",
"Toggle navigation": "Toggle navigation",
"Focus search input": "Focus search input",
"Open this guide": "Apri questa guida",
+16 -16
View File
@@ -30,7 +30,7 @@
"in": "に",
"nested document": "ネストされたドキュメント",
"nested document_plural": "ネストされたドキュメント",
"Viewed by": "閲覧者",
"Viewed by": "閲覧者:",
"only you": "自分のみ",
"person": "個人",
"people": "メンバー",
@@ -98,7 +98,7 @@
"Icon": "アイコン",
"Show menu": "メニューを表示",
"Choose icon": "アイコンの選択",
"Loading": "ローディング",
"Loading": "ローディング...",
"Search": "検索",
"Default access": "デフォルトアクセス",
"View and edit": "表示・編集",
@@ -109,7 +109,7 @@
"Dismiss": "終了",
"Keyboard shortcuts": "キーボードショートカット",
"Back": "戻る",
"Collections could not be loaded, please reload the app": "コレクションを読み込めませんでした。アプリを再読み込みしてください。",
"Collections could not be loaded, please reload the app": "Collections could not be loaded, please reload the app",
"New collection": "コレクションを追加",
"Collections": "コレクション",
"Untitled": "文書",
@@ -152,12 +152,12 @@
"Path to document": "ドキュメントのパス",
"Group member options": "グループメンバーのオプション",
"Remove": "削除",
"Collection": "コレクション",
"New document": "新しいドキュメント",
"Import document": "ドキュメントのインポート",
"Edit": "編集",
"Permissions": "権限",
"Delete": "削除",
"Collection": "コレクション",
"Collection permissions": "コレクションの権限",
"Edit collection": "編集コレクション",
"Delete collection": "コレクションの削除",
@@ -181,14 +181,14 @@
"Create template": "テンプレートの作成",
"Duplicate": "文書の複製",
"Unpublish": "未発表",
"Permanently delete": "完全に削除します。",
"Permanently delete": "Permanently delete",
"Move": "移動",
"History": "変更履歴",
"Download": "ダウンロード",
"Print": "プリント",
"Move {{ documentName }}": "{{ documentName }} を移動",
"Delete {{ documentName }}": "{{ documentName }}を削除します。",
"Permanently delete {{ documentName }}": "{{ documentName }} を完全に削除します。",
"Permanently delete {{ documentName }}": "Permanently delete {{ documentName }}",
"Edit group": "グループの編集",
"Delete group": "グループを削除",
"Group options": "グループのオプション",
@@ -206,7 +206,6 @@
"Share options": "共有オプション",
"Go to document": "ドキュメントに移動",
"Revoke link": "リンクを取り消す",
"Table of contents": "目次",
"By {{ author }}": "作ったのは{{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "本当に{{ userName }}を管理者にしていいのか?管理者は課金情報を修正することができます。また、チームを変更することもできます。",
"Are you sure you want to make {{ userName }} a member?": "本当に{{ userName }}をメンバーにしていいのか?",
@@ -220,7 +219,7 @@
"Activate account": "アカウントの有効化",
"Suspend account": "アカウントの一時停止",
"API token created": "APIトークンが作成されました",
"Name your token something that will help you to remember it's use in the future, for example \"local development\", \"production\", or \"continuous integration\".": "トークンには、「ローカル開発」、「本番」、「継続的インテグレーション」など、使用目的を思い出しやすい名前を付けてください。",
"Name your token something that will help you to remember it's use in the future, for example \"local development\", \"production\", or \"continuous integration\".": "Name your token something that will help you to remember it's use in the future, for example \"local development\", \"production\", or \"continuous integration\".",
"Documents": "ドキュメント",
"The document archive is empty at the moment.": "ドキュメントアーカイブは現在空です。",
"Search in collection": "コレクション内を検索",
@@ -243,7 +242,7 @@
"Public document sharing": "公開ドキュメントを共有",
"When enabled, documents can be shared publicly on the internet.": "有効にすると、ドキュメントをインターネット上で公開して共有できます。",
"Public sharing is currently disabled in the team security settings.": "チームのセキュリティ設定で公開共有が無効になっています。",
"Saving": "保存",
"Saving": "保存文書...",
"Save": "セーブ",
"Collections are for grouping your documents. They work best when organized around a topic or internal team — Product or Engineering for example.": "コレクションは、ドキュメントをグループ化するためのものです。製品やエンジニアリングなど、トピックや社内のチームを基準に作成することで、最も効果的に機能します。",
"This is the default level of access given to team members, you can give specific users or groups more access once the collection is created.": "これはチームメンバーに付与されるデフォルトのアクセスレベルです。コレクションが作成されると、さらに多くのアクセスを特定のユーザーまたはグループに付与できます。",
@@ -273,9 +272,9 @@
"Could not remove user": "このユーザーを削除することはできません。",
"{{ userName }} permissions were updated": "{{ userName }} の権限が更新されました",
"Could not update user": "ユーザーを更新できませんでした",
"The {{ groupName }} group was removed from the collection": "グループ {{ groupName }} がコレクションから削除されました",
"The {{ groupName }} group was removed from the collection": "グループ:{{ groupName }} がコレクションから削除されました",
"Could not remove group": "グループを削除できませんでした",
"{{ groupName }} permissions were updated": "グループ {{ groupName }} の権限が更新されました",
"{{ groupName }} permissions were updated": "グループ:{{ groupName }} の権限が更新されました",
"Default access permissions were updated": "デフォルトのアクセス権限が更新されました",
"Could not update permissions": "権限を更新できませんでした",
"The <em>{{ collectionName }}</em> collection is private. Team members have no access to it by default.": "<em>{{ collectionName }}</em> コレクションは非公開です。チームメンバーはデフォルトではアクセスできません。",
@@ -308,15 +307,15 @@
"Share nested documents": "ネストされたドキュメントを共有する",
"Nested documents are publicly available": "ネストされたドキュメントも公開されます",
"Nested documents are not shared": "ネストされたドキュメントは共有されません",
"Are you sure you want to delete the <em>{{ documentTitle }}</em> template?": "テンプレート <em>{{ documentTitle }}</em> を削除してもよろしいですか?",
"Are you sure you want to delete the <em>{{ documentTitle }}</em> template?": "テンプレート:<em>{{ documentTitle }}</em> を削除してもよろしいですか?",
"Are you sure about that? Deleting the <em>{{ documentTitle }}</em> document will delete all of its history and any nested documents.": "<em>{{ documentTitle }}</em> ドキュメントを削除すると、その履歴とネストされたドキュメントがすべて削除されます。よろしいですか?",
"If youd like the option of referencing or restoring the {{noun}} in the future, consider archiving it instead.": "将来的に {{noun}} の参照または復元が必要になる可能性がある場合は、代わりにアーカイブすることを検討してください。",
"Deleting": "削除中",
"Im sure  Delete": "間違いありません – 削除",
"Archiving": "アーカイブ中",
"Couldnt create the document, try again?": "ドキュメントを作成できませんでした。もう一度やり直してください。",
"Document permanently deleted": "ドキュメントが完全に削除されました。",
"Are you sure you want to permanently delete the <em>{{ documentTitle }}</em> document? This action is immediate and cannot be undone.": "<em>{{ documentTitle }}</em> ドキュメントを完全に削除してもよろしいですか?この操作は即時に反映され、元に戻すことはできません。",
"Couldnt create the document, try again?": "Couldnt create the document, try again?",
"Document permanently deleted": "Document permanently deleted",
"Are you sure you want to permanently delete the <em>{{ documentTitle }}</em> document? This action is immediate and cannot be undone.": "Are you sure you want to permanently delete the <em>{{ documentTitle }}</em> document? This action is immediate and cannot be undone.",
"Search documents": "ドキュメントの検索",
"No documents found for your filters.": "検索結果はありませんでした。",
"Youve not got any drafts at the moment.": "この時は下書きがない。",
@@ -338,6 +337,7 @@
"Move current document": "現在のドキュメントを移動する",
"Jump to search": "検索へ",
"Jump to home": "ホームへ移動",
"Table of contents": "目次",
"Toggle navigation": "ナビゲーションの切り替え",
"Focus search input": "検索窓にフォーカスする",
"Open this guide": "ガイドを開く",
@@ -397,7 +397,7 @@
"All groups": "すべてのグループ",
"No groups have been created yet": "グループはまだ作成されていません",
"Import started": "インポートを開始しました",
"Export in progress…": "エクスポートが進行中",
"Export in progress…": "エクスポートが進行中...",
"It is possible to import a zip file of folders and Markdown files previously exported from an Outline instance. Support will soon be added for importing from other services.": "OutlineからエクスポートしたフォルダーとMarkdownファイルを含むzipファイルをインポートすることができます。他のサービスからのインポートについては、間もなくサポートが追加される予定です。",
"Your file has been uploaded and the import is currently being processed, you can safely leave this page while it completes.": "ファイルのアップロードが成功し、現在インポート処理を行っています。完了後、このページを離れることが出来ます。",
"Sorry, the file <em>{{ fileName }}</em> is missing valid collections or documents.": "ファイル <em>{{ fileName }}</em> は有効なコレクションまたはドキュメントがありません。",
+2 -2
View File
@@ -152,12 +152,12 @@
"Path to document": "문서 경로",
"Group member options": "그룹 멤버 옵션",
"Remove": "제거",
"Collection": "컬렉션",
"New document": "새 문서",
"Import document": "문서 가져 오기",
"Edit": "편집",
"Permissions": "권한",
"Delete": "삭제",
"Collection": "컬렉션",
"Collection permissions": "Collection permissions",
"Edit collection": "컬렉션 편집",
"Delete collection": "컬렉션 삭제",
@@ -206,7 +206,6 @@
"Share options": "공유 옵션",
"Go to document": "문서로 이동",
"Revoke link": "링크 삭제",
"Table of contents": "목차",
"By {{ author }}": "{{ author }} 작성",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "정말로 {{ userName }} 에게 관리자 권한을 부여 하시겠습니까? 관리자는 팀과 결제 정보를 수정할 수 있습니다.",
"Are you sure you want to make {{ userName }} a member?": "정말로 {{ userName }} 에게 멤버 권한을 부여 하시겠습니까?",
@@ -338,6 +337,7 @@
"Move current document": "현재 문서로 이동",
"Jump to search": "검색으로 이동",
"Jump to home": "홈으로 이동",
"Table of contents": "목차",
"Toggle navigation": "Toggle navigation",
"Focus search input": "Focus search input",
"Open this guide": "이 가이드 열기",
+2 -2
View File
@@ -152,12 +152,12 @@
"Path to document": "Path to document",
"Group member options": "Group member options",
"Remove": "Verwijder",
"Collection": "Collectie",
"New document": "Nieuw document",
"Import document": "Importeer document",
"Edit": "Wijzig",
"Permissions": "Rechten",
"Delete": "Verwijder",
"Collection": "Collectie",
"Collection permissions": "Collection permissions",
"Edit collection": "Wijzig collectie",
"Delete collection": "Verwijder collectie",
@@ -206,7 +206,6 @@
"Share options": "Deel opties",
"Go to document": "Ga naar document",
"Revoke link": "Trek link in",
"Table of contents": "Table of contents",
"By {{ author }}": "Door {{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.",
"Are you sure you want to make {{ userName }} a member?": "Are you sure you want to make {{ userName }} a member?",
@@ -338,6 +337,7 @@
"Move current document": "Move current document",
"Jump to search": "Jump to search",
"Jump to home": "Jump to home",
"Table of contents": "Table of contents",
"Toggle navigation": "Toggle navigation",
"Focus search input": "Focus search input",
"Open this guide": "Open this guide",
+2 -2
View File
@@ -152,12 +152,12 @@
"Path to document": "Ścieżka do dokumentu",
"Group member options": "Group member options",
"Remove": "Usuń",
"Collection": "Kolekcja",
"New document": "Nowy dokument",
"Import document": "Zaimportuj dokument",
"Edit": "Edytuj",
"Permissions": "Uprawnienia",
"Delete": "Usuń",
"Collection": "Kolekcja",
"Collection permissions": "Uprawnienia kolekcji",
"Edit collection": "Edit collection",
"Delete collection": "Delete collection",
@@ -206,7 +206,6 @@
"Share options": "Opcje udostępniania",
"Go to document": "Przejdź do dokumentu",
"Revoke link": "Unieważnij link",
"Table of contents": "Table of contents",
"By {{ author }}": "By {{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Czy na pewno chcesz uczynić {{ userName }} administratorem? Administratorzy mogą modyfikować informacje o zespole i rozliczeniach.",
"Are you sure you want to make {{ userName }} a member?": "Are you sure you want to make {{ userName }} a member?",
@@ -338,6 +337,7 @@
"Move current document": "Przenieś bieżący dokument",
"Jump to search": "Jump to search",
"Jump to home": "Jump to home",
"Table of contents": "Table of contents",
"Toggle navigation": "Toggle navigation",
"Focus search input": "Focus search input",
"Open this guide": "Open this guide",
+2 -2
View File
@@ -152,12 +152,12 @@
"Path to document": "Caminho para o documento",
"Group member options": "Opções de membros do grupo",
"Remove": "Remover",
"Collection": "Coleção",
"New document": "Novo documento",
"Import document": "Importar documento",
"Edit": "Editar",
"Permissions": "Permissões",
"Delete": "Excluir",
"Collection": "Coleção",
"Collection permissions": "Permissões da coleção",
"Edit collection": "Editar coleção",
"Delete collection": "Excluir coleção",
@@ -206,7 +206,6 @@
"Share options": "Opções de compartilhamento",
"Go to document": "Ir para o documento",
"Revoke link": "Revogar link",
"Table of contents": "Índice",
"By {{ author }}": "Por {{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Tem certeza que deseja tornar {{ userName }} um administrador? Os administradores podem modificar as informações da equipe e do faturamento.",
"Are you sure you want to make {{ userName }} a member?": "Tem certeza de que deseja tornar {{ userName }} um membro?",
@@ -338,6 +337,7 @@
"Move current document": "Mover documento atual",
"Jump to search": "Ir para pesquisa",
"Jump to home": "Ir para a tela inicial",
"Table of contents": "Índice",
"Toggle navigation": "Alternar de navegação",
"Focus search input": "Focar pesquisa",
"Open this guide": "Abrir este guia",
+2 -2
View File
@@ -152,12 +152,12 @@
"Path to document": "Path to document",
"Group member options": "Group member options",
"Remove": "Remover",
"Collection": "Coleção",
"New document": "Novo documento",
"Import document": "Importar documento",
"Edit": "Editar",
"Permissions": "Permissões",
"Delete": "Apagar",
"Collection": "Coleção",
"Collection permissions": "Collection permissions",
"Edit collection": "Editar coleção",
"Delete collection": "Apagar coleção",
@@ -206,7 +206,6 @@
"Share options": "Share options",
"Go to document": "Ir para documento",
"Revoke link": "Revogar link",
"Table of contents": "Índice de conteúdos",
"By {{ author }}": "De {{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Tem a certeza que deseja tornar {{ userName }} um administrador? Os administradores podem modificar as informações de faturação e da equipa.",
"Are you sure you want to make {{ userName }} a member?": "Tem a certeza de que deseja tornar {{ userName }} um membro?",
@@ -338,6 +337,7 @@
"Move current document": "Mover documento atual",
"Jump to search": "Ir para a pesquisa",
"Jump to home": "Jump to home",
"Table of contents": "Índice de conteúdos",
"Toggle navigation": "Toggle navigation",
"Focus search input": "Focus search input",
"Open this guide": "Abrir este guia",
+2 -2
View File
@@ -152,12 +152,12 @@
"Path to document": "Путь к документу",
"Group member options": "Параметры участников группы",
"Remove": "Удалить",
"Collection": "Коллекция",
"New document": "Новый документ",
"Import document": "Импорт документа",
"Edit": "Редактировать",
"Permissions": "Права доступа",
"Delete": "Удалить",
"Collection": "Коллекция",
"Collection permissions": "Права доступа к коллекции",
"Edit collection": "Изменить подборку",
"Delete collection": "Удалить коллекцию",
@@ -206,7 +206,6 @@
"Share options": "Настройка доступа",
"Go to document": "Перейти к документу",
"Revoke link": "Отозвать ссылку",
"Table of contents": "Содержание",
"By {{ author }}": "По {{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Удаляем админа {{ userName }}? Админы могут настраивать команду и платежную информацию.",
"Are you sure you want to make {{ userName }} a member?": "Делаем {{ userName }} участником?",
@@ -338,6 +337,7 @@
"Move current document": "Переместить текущий документ",
"Jump to search": "Перейти к поиску",
"Jump to home": "Перейти на главную",
"Table of contents": "Содержание",
"Toggle navigation": "Toggle navigation",
"Focus search input": "Focus search input",
"Open this guide": "Открыть эту инструкцию",
+34 -34
View File
@@ -3,37 +3,37 @@
"currently viewing": "currently viewing",
"previously edited": "previously edited",
"You": "Du",
"Viewers": "Tittare",
"Viewers": "Viewers",
"Sorry, an error occurred saving the collection": "Sorry, an error occurred saving the collection",
"Add a description": "Lägg till en beskrivning",
"Collapse": "Collapse",
"Expand": "Expandera",
"Expand": "Expand",
"Submenu": "Undermeny",
"Trash": "Papperskorg",
"Archive": "Arkiv",
"Drafts": "Utkast",
"Templates": "Mallar",
"Deleted Collection": "Deleted Collection",
"New": "Ny",
"New": "New",
"Only visible to you": "Only visible to you",
"Draft": "Utkast",
"Template": "Mall",
"New doc": "Nytt dokument",
"deleted": "raderad",
"archived": "arkiverad",
"New doc": "New doc",
"deleted": "deleted",
"archived": "archived",
"created": "skapad",
"published": "publicerad",
"saved": "sparad",
"updated": "uppdaterad",
"Never viewed": "Never viewed",
"Viewed": "Sett",
"Viewed": "Viewed",
"in": "i",
"nested document": "nested document",
"nested document_plural": "nested documents",
"Viewed by": "Viewed by",
"only you": "endast dig",
"only you": "only you",
"person": "person",
"people": "människor",
"people": "people",
"Currently editing": "Currently editing",
"Currently viewing": "Currently viewing",
"Viewed {{ timeAgo }} ago": "Viewed {{ timeAgo }} ago",
@@ -48,7 +48,7 @@
"Todo list": "Att göra-lista",
"Code block": "Kodblock",
"Copied to clipboard": "Copied to clipboard",
"Code": "Kod",
"Code": "Code",
"Create link": "Skapa länk",
"Sorry, an error occurred creating the link": "Sorry, an error occurred creating the link",
"Create a new doc": "Create a new doc",
@@ -124,7 +124,7 @@
"Profile": "Profil",
"Notifications": "Notifications",
"API Tokens": "API Tokens",
"Team": "Lag",
"Team": "Team",
"Details": "Detaljer",
"Security": "Säkerhet",
"Members": "Members",
@@ -134,15 +134,15 @@
"Export": "Exportera",
"Integrations": "Integrations",
"Installation": "Installation",
"Unstar": "Ta bort stjärnmärkning",
"Star": "Stjärna",
"Unstar": "Unstar",
"Star": "Star",
"Previous page": "Previous page",
"Next page": "Next page",
"Could not import file": "Could not import file",
"Appearance": "Utseende",
"System": "System",
"Light": "Ljus",
"Dark": "rk",
"Light": "Light",
"Dark": "Dark",
"API documentation": "API documentation",
"Changelog": "Changelog",
"Send us feedback": "Send us feedback",
@@ -151,13 +151,13 @@
"Show path to document": "Show path to document",
"Path to document": "Path to document",
"Group member options": "Group member options",
"Remove": "Ta bort",
"Remove": "Remove",
"Collection": "Collection",
"New document": "New document",
"Import document": "Import document",
"Edit": "Redigera",
"Edit": "Edit",
"Permissions": "Permissions",
"Delete": "Radera",
"Collection": "Collection",
"Delete": "Delete",
"Collection permissions": "Collection permissions",
"Edit collection": "Edit collection",
"Delete collection": "Delete collection",
@@ -173,7 +173,7 @@
"Document options": "Document options",
"Restore": "Återställ",
"Choose a collection": "Choose a collection",
"Unpin": "Lossa",
"Unpin": "Unpin",
"Pin to collection": "Pin to collection",
"Enable embeds": "Enable embeds",
"Disable embeds": "Disable embeds",
@@ -206,7 +206,6 @@
"Share options": "Share options",
"Go to document": "Go to document",
"Revoke link": "Revoke link",
"Table of contents": "Table of contents",
"By {{ author }}": "By {{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.",
"Are you sure you want to make {{ userName }} a member?": "Are you sure you want to make {{ userName }} a member?",
@@ -230,7 +229,7 @@
"Manage permissions": "Manage permissions",
"This collection is only visible to those given access": "This collection is only visible to those given access",
"Private": "Private",
"Pinned": "Fastsatt",
"Pinned": "Pinned",
"Recently updated": "Recently updated",
"Recently published": "Recently published",
"Least recently updated": "Least recently updated",
@@ -243,12 +242,12 @@
"Public document sharing": "Public document sharing",
"When enabled, documents can be shared publicly on the internet.": "When enabled, documents can be shared publicly on the internet.",
"Public sharing is currently disabled in the team security settings.": "Public sharing is currently disabled in the team security settings.",
"Saving": "Sparar",
"Saving": "Saving",
"Save": "Spara",
"Collections are for grouping your documents. They work best when organized around a topic or internal team — Product or Engineering for example.": "Collections are for grouping your documents. They work best when organized around a topic or internal team — Product or Engineering for example.",
"This is the default level of access given to team members, you can give specific users or groups more access once the collection is created.": "This is the default level of access given to team members, you can give specific users or groups more access once the collection is created.",
"Creating": "Creating",
"Create": "Skapa",
"Create": "Create",
"{{ groupName }} was added to the collection": "{{ groupName }} was added to the collection",
"Could not add user": "Could not add user",
"Cant find the group youre looking for?": "Cant find the group youre looking for?",
@@ -298,7 +297,7 @@
"Publishing": "Publishing",
"Nested documents": "Nested documents",
"Anyone with the link <1></1>can view this document": "Anyone with the link <1></1>can view this document",
"Share": "Dela",
"Share": "Share",
"Share this document": "Share this document",
"This document is shared because the parent <em>{{ documentTitle }}</em> is publicly shared": "This document is shared because the parent <em>{{ documentTitle }}</em> is publicly shared",
"Publish to internet": "Publish to internet",
@@ -338,6 +337,7 @@
"Move current document": "Move current document",
"Jump to search": "Jump to search",
"Jump to home": "Jump to home",
"Table of contents": "Table of contents",
"Toggle navigation": "Toggle navigation",
"Focus search input": "Focus search input",
"Open this guide": "Open this guide",
@@ -352,8 +352,8 @@
"Small header": "Small header",
"Underline": "Underline",
"Undo": "Ångra",
"Redo": "Upprepa",
"Lists": "Listor",
"Redo": "Redo",
"Lists": "Lists",
"Indent list item": "Indent list item",
"Outdent list item": "Outdent list item",
"Move list item up": "Move list item up",
@@ -373,25 +373,25 @@
"All documents": "All documents",
"Include documents that are in the archive": "Include documents that are in the archive",
"Any author": "Any author",
"Author": "Författare",
"Author": "Author",
"Not Found": "Not Found",
"We were unable to find the page youre looking for.": "We were unable to find the page youre looking for.",
"Use the <em>{{ meta }}+K</em> shortcut to search from anywhere in your knowledge base": "Use the <em>{{ meta }}+K</em> shortcut to search from anywhere in your knowledge base",
"No documents found for your search filters. <1></1>": "No documents found for your search filters. <1></1>",
"Create a new document?": "Create a new document?",
"Clear filters": "Clear filters",
"Email": "E-post",
"Email": "Email",
"Last active": "Last active",
"Role": "Roll",
"Viewer": "Läs",
"Role": "Role",
"Viewer": "Viewer",
"Suspended": "Suspended",
"Shared": "Delad",
"Shared": "Shared",
"by {{ name }}": "by {{ name }}",
"Last accessed": "Last accessed",
"Add to Slack": "Add to Slack",
"Active": "Aktiv",
"Active": "Active",
"Everyone": "Everyone",
"Admins": "Administratörer",
"Admins": "Admins",
"New group": "New group",
"Groups can be used to organize and manage the people on your team.": "Groups can be used to organize and manage the people on your team.",
"All groups": "All groups",
+2 -2
View File
@@ -152,12 +152,12 @@
"Path to document": "Path to document",
"Group member options": "Group member options",
"Remove": "Remove",
"Collection": "Collection",
"New document": "New document",
"Import document": "Import document",
"Edit": "Edit",
"Permissions": "Permissions",
"Delete": "Delete",
"Collection": "Collection",
"Collection permissions": "Collection permissions",
"Edit collection": "Edit collection",
"Delete collection": "Delete collection",
@@ -206,7 +206,6 @@
"Share options": "Share options",
"Go to document": "Go to document",
"Revoke link": "Revoke link",
"Table of contents": "Table of contents",
"By {{ author }}": "By {{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.",
"Are you sure you want to make {{ userName }} a member?": "Are you sure you want to make {{ userName }} a member?",
@@ -338,6 +337,7 @@
"Move current document": "Move current document",
"Jump to search": "Jump to search",
"Jump to home": "Jump to home",
"Table of contents": "Table of contents",
"Toggle navigation": "Toggle navigation",
"Focus search input": "Focus search input",
"Open this guide": "Open this guide",
+2 -2
View File
@@ -152,12 +152,12 @@
"Path to document": "文件路径",
"Group member options": "小组成员选项",
"Remove": "移除",
"Collection": "文档集",
"New document": "新建文档",
"Import document": "导入文档",
"Edit": "编辑",
"Permissions": "权限",
"Delete": "删除",
"Collection": "文档集",
"Collection permissions": "文档集权限",
"Edit collection": "编辑文档集",
"Delete collection": "删除文档集",
@@ -206,7 +206,6 @@
"Share options": "分享选项",
"Go to document": "转到文档",
"Revoke link": "撤消链接",
"Table of contents": "目录",
"By {{ author }}": "创建人 {{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "您确定要设置 {{ userName }} 为管理员吗?管理员可以修改团队和帐单信息。",
"Are you sure you want to make {{ userName }} a member?": "您确定要设置 {{ userName }} 为成员吗?",
@@ -338,6 +337,7 @@
"Move current document": "移动当前文档",
"Jump to search": "跳转到搜索",
"Jump to home": "跳到页",
"Table of contents": "目录",
"Toggle navigation": "切换导航",
"Focus search input": "聚焦搜索结果",
"Open this guide": "打开指南",
+11 -11
View File
@@ -32,8 +32,8 @@
"nested document_plural": "子文件",
"Viewed by": "已瀏覽過",
"only you": "僅限自己",
"person": "",
"people": "",
"person": "person",
"people": "people",
"Currently editing": "正在編輯",
"Currently viewing": "正在瀏覽",
"Viewed {{ timeAgo }} ago": "{{ timeAgo }} 前瀏覽過",
@@ -57,8 +57,8 @@
"Delete table": "刪除表格",
"Delete image": "刪除圖片",
"Download image": "下載圖片",
"Float left": "靠左",
"Float right": "靠右",
"Float left": "Float left",
"Float right": "Float right",
"Center large": "Center large",
"Italic": "斜體",
"Sorry, that link wont work for this embed type": "抱歉,不支援將這個連結嵌入到文件",
@@ -152,12 +152,12 @@
"Path to document": "文件路徑",
"Group member options": "群組成員選項",
"Remove": "移除",
"Collection": "文件集",
"New document": "建立新文件",
"Import document": "匯入文件",
"Edit": "編輯",
"Permissions": "權限設定",
"Delete": "刪除",
"Collection": "文件集",
"Collection permissions": "文件集權限",
"Edit collection": "編輯文件集",
"Delete collection": "刪除文件集",
@@ -206,10 +206,9 @@
"Share options": "分享設定",
"Go to document": "跳轉到文件",
"Revoke link": "註銷連結",
"Table of contents": "目錄",
"By {{ author }}": "由 {{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "你確定要將 {{ userName }} 設為管理員嗎?管理員可以修改團隊及帳單資訊。",
"Are you sure you want to make {{ userName }} a member?": "您確定要將 {{ userName }} 設定設為成員之一嗎?",
"Are you sure you want to make {{ userName }} a member?": "您卻將要將 {{ userName }} 設定設為成員之一嗎?",
"Are you sure you want to make {{ userName }} a read-only viewer? They will not be able to edit any content": "你確定要將 {{ userName }} 設定為唯讀檢視者嗎?他們將無法編輯任何內容。",
"Are you sure you want to suspend this account? Suspended users will be prevented from logging in.": "你確定要停用這個使用者帳號嗎?遭到停用的使用者將無法登入。",
"User options": "使用者選項",
@@ -256,15 +255,15 @@
"Search by group name": "依照群組名稱搜尋",
"Search groups": "搜尋群組",
"No groups matching your search": "找不到符合搜尋條件的群組",
"No groups left to add": "所有群組都已經被新增",
"No groups left to add": "No groups left to add",
"Add": "新增",
"{{ userName }} was added to the collection": "{{ userName }} 已經被新增到文件集",
"Need to add someone whos not yet on the team yet?": "需要新增尚未加入團隊的人嗎?",
"Need to add someone whos not yet on the team yet?": "Need to add someone whos not yet on the team yet?",
"Invite people to {{ teamName }}": "邀請他人加入 {{ teamName }}",
"Search by name": "依名稱搜尋",
"Search people": "搜尋使用者",
"No people matching your search": "找不到符合搜尋條件的使用者",
"No people left to add": "所有人員都已經被新增",
"No people left to add": "No people left to add",
"Active <1></1> ago": "最後活動於 <1></1> 前",
"Never signed in": "從未登入",
"Invited": "已邀請",
@@ -286,7 +285,7 @@
"Add people": "新增人員",
"Add specific access for individual groups and team members": "為個別群組和團隊成員增加特定的存取權限",
"Add groups to {{ collectionName }}": "將群組加入到 {{ collectionName }}",
"Add people to {{ collectionName }}": "將人員加入到 {{ collectionName }}",
"Add people to {{ collectionName }}": "將使用者加入到 {{ collectionName }}",
"Hide contents": "隱藏內容",
"Show contents": "顯示內容",
"Edit {{noun}}": "編輯 {{noun}}",
@@ -338,6 +337,7 @@
"Move current document": "移動目前文件",
"Jump to search": "跳轉到搜尋",
"Jump to home": "跳轉到首頁",
"Table of contents": "目錄",
"Toggle navigation": "切換導覽",
"Focus search input": "Focus search input",
"Open this guide": "Open this guide",
+1 -25
View File
@@ -18,19 +18,7 @@ export const fadeAndScaleIn = keyframes`
}
`;
export const fadeAndSlideDown = keyframes`
from {
opacity: 0;
transform: scale(.98) translateY(-10px);
}
to {
opacity: 1;
transform: scale(1) translateY(0px);
}
`;
export const fadeAndSlideUp = keyframes`
export const fadeAndSlideIn = keyframes`
from {
opacity: 0;
transform: scale(.98) translateY(10px);
@@ -42,18 +30,6 @@ export const fadeAndSlideUp = keyframes`
}
`;
export const mobileContextMenu = keyframes`
from {
opacity: 0;
transform: scale(.98) translateY(10vh);
}
to {
opacity: 1;
transform: scale(1) translateY(0px);
}
`;
export const bounceIn = keyframes`
from,
20%,
+3
View File
@@ -57,6 +57,9 @@ module.exports = {
new webpack.DefinePlugin({
EDITOR_VERSION: JSON.stringify(pkg.version)
}),
new webpack.ProvidePlugin({
fetch: 'imports-loader?this=>global!exports-loader?global.fetch!isomorphic-fetch',
}),
new webpack.IgnorePlugin(/unicode\/category\/So/),
new HtmlWebpackPlugin({
template: 'server/static/index.html',
+46 -89
View File
@@ -1040,7 +1040,7 @@
exec-sh "^0.3.2"
minimist "^1.2.0"
"@emotion/is-prop-valid@^0.8.2", "@emotion/is-prop-valid@^0.8.8":
"@emotion/is-prop-valid@^0.8.8":
version "0.8.8"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a"
integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
@@ -4323,13 +4323,6 @@ cron-parser@^2.13.0:
is-nan "^1.3.0"
moment-timezone "^0.5.31"
cross-fetch@^3.0.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39"
integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==
dependencies:
node-fetch "2.6.1"
cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
@@ -4868,11 +4861,6 @@ domhandler@^4.0.0, domhandler@^4.2.0:
dependencies:
domelementtype "^2.2.0"
domino@^2.1.6:
version "2.1.6"
resolved "https://registry.yarnpkg.com/domino/-/domino-2.1.6.tgz#fe4ace4310526e5e7b9d12c7de01b7f485a57ffe"
integrity sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ==
domutils@1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
@@ -5052,6 +5040,13 @@ encodeurl@^1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
encoding@^0.1.11:
version "0.1.13"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
dependencies:
iconv-lite "^0.6.2"
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@@ -5769,11 +5764,6 @@ feature-policy@0.3.0:
resolved "https://registry.yarnpkg.com/feature-policy/-/feature-policy-0.3.0.tgz#7430e8e54a40da01156ca30aaec1a381ce536069"
integrity sha512-ZtijOTFN7TzCujt1fnNhfWPFPSHeZkesff9AXZj+UEjYBynWNUIYpC87Ve4wHzyexQsImicLu7WsC2LHq7/xrQ==
fetch-retry@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/fetch-retry/-/fetch-retry-4.1.1.tgz#fafe0bb22b54f4d0a9c788dff6dd7f8673ca63f3"
integrity sha512-e6eB7zN6UBSwGVwrbWVH+gdLnkW9WwHhmq2YDK1Sh30pzx1onRVGBvogTlUeWxwTa+L86NYdo4hFkh7O8ZjSnA==
fetch-test-server@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/fetch-test-server/-/fetch-test-server-1.2.0.tgz#65f23af1d030c293249a49bbd1b51e45fc68eb69"
@@ -6014,26 +6004,6 @@ fragment-cache@^0.2.1:
dependencies:
map-cache "^0.2.2"
framer-motion@^4.1.17:
version "4.1.17"
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-4.1.17.tgz#4029469252a62ea599902e5a92b537120cc89721"
integrity sha512-thx1wvKzblzbs0XaK2X0G1JuwIdARcoNOW7VVwjO8BUltzXPyONGAElLu6CiCScsOQRI7FIk/45YTFtJw5Yozw==
dependencies:
framesync "5.3.0"
hey-listen "^1.0.8"
popmotion "9.3.6"
style-value-types "4.1.4"
tslib "^2.1.0"
optionalDependencies:
"@emotion/is-prop-valid" "^0.8.2"
framesync@5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/framesync/-/framesync-5.3.0.tgz#0ecfc955e8f5a6ddc8fdb0cc024070947e1a0d9b"
integrity sha512-oc5m68HDO/tuK2blj7ZcdEBRx3p1PjrgHazL8GYEpvULhrtGIFbQArN6cQS2QhW8mitffaB+VYzMjDqBxxQeoA==
dependencies:
tslib "^2.1.0"
fresh@~0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
@@ -6631,11 +6601,6 @@ helmet@^3.21.1:
referrer-policy "1.2.0"
x-xss-protection "1.3.0"
hey-listen@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68"
integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==
hide-powered-by@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/hide-powered-by/-/hide-powered-by-1.1.0.tgz#be3ea9cab4bdb16f8744be873755ca663383fa7a"
@@ -6901,6 +6866,13 @@ iconv-lite@0.4.24:
dependencies:
safer-buffer ">= 2.1.2 < 3"
iconv-lite@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01"
integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"
ieee754@1.1.13:
version "1.1.13"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
@@ -7419,7 +7391,7 @@ is-retry-allowed@^1.0.0:
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4"
integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==
is-stream@^1.0.0, is-stream@^1.1.0:
is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
@@ -7517,6 +7489,14 @@ isobject@^3.0.0, isobject@^3.0.1:
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
isomorphic-fetch@2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=
dependencies:
node-fetch "^1.0.1"
whatwg-fetch ">=0.10.0"
isomorphic-fetch@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4"
@@ -7681,14 +7661,6 @@ jest-environment-node@^26.6.2:
jest-mock "^26.6.2"
jest-util "^26.6.2"
jest-fetch-mock@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz#31749c456ae27b8919d69824f1c2bd85fe0a1f3b"
integrity sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==
dependencies:
cross-fetch "^3.0.4"
promise-polyfill "^8.1.3"
jest-get-type@^26.3.0:
version "26.3.0"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0"
@@ -8013,7 +7985,7 @@ jsbn@~0.1.0:
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
jsdom@^16.4.0:
jsdom@^16.2.0, jsdom@^16.4.0:
version "16.4.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.4.0.tgz#36005bde2d136f73eee1a830c6d45e55408edddb"
integrity sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==
@@ -9452,11 +9424,24 @@ node-fetch@2.6.1, node-fetch@^2.1.2, node-fetch@^2.6.1:
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
node-fetch@^1.0.1:
version "1.7.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==
dependencies:
encoding "^0.1.11"
is-stream "^1.0.1"
node-gyp-build@^3.8.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.9.0.tgz#53a350187dd4d5276750da21605d1cb681d09e25"
integrity sha512-zLcTg6P4AbcHPq465ZMFNXx7XpKKJh+7kkN699NiQWisR2uWYOWNWqRHAmbnmKiL4e9aLSlmy5U7rEMUXV59+A==
node-htmldiff@^0.9.3:
version "0.9.3"
resolved "https://registry.yarnpkg.com/node-htmldiff/-/node-htmldiff-0.9.3.tgz#020704e381597e5e449a4708996edf23eebb7fcc"
integrity sha1-AgcE44FZfl5EmkcImW7fI+67f8w=
node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
@@ -10377,16 +10362,6 @@ polished@3.6.5:
dependencies:
"@babel/runtime" "^7.9.2"
popmotion@9.3.6:
version "9.3.6"
resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-9.3.6.tgz#b5236fa28f242aff3871b9e23721f093133248d1"
integrity sha512-ZTbXiu6zIggXzIliMi8LGxXBF5ST+wkpXGEjeTUDUOCdSQ356hij/xjeUdv0F8zCQNeqB1+PR5/BB+gC+QLAPw==
dependencies:
framesync "5.3.0"
hey-listen "^1.0.8"
style-value-types "4.1.4"
tslib "^2.1.0"
popper.js@^1.14.7:
version "1.16.1"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
@@ -10513,11 +10488,6 @@ promise-map-series@^0.2.1:
dependencies:
rsvp "^3.0.14"
promise-polyfill@^8.1.3:
version "8.2.0"
resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.2.0.tgz#367394726da7561457aba2133c9ceefbd6267da0"
integrity sha512-k/TC0mIcPVF6yHhUvwAp7cvL6I2fFV7TzF1DuGPI8mBh4QQazf36xCKEHKTZKRysEoTQoQdKyP25J8MPJp7j5g==
promise.prototype.finally@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/promise.prototype.finally/-/promise.prototype.finally-3.1.2.tgz#b8af89160c9c673cefe3b4c4435b53cfd0287067"
@@ -11736,7 +11706,7 @@ safe-regex@^1.1.0:
dependencies:
ret "~0.1.10"
"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@@ -12662,14 +12632,6 @@ strip-json-comments@~2.0.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
style-value-types@4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-4.1.4.tgz#80f37cb4fb024d6394087403dfb275e8bb627e75"
integrity sha512-LCJL6tB+vPSUoxgUBt9juXIlNJHtBMy8jkXzUJSBzeHWdBu6lhzHqCvLVkXFGsFIlNa2ln1sQHya/gzaFmB2Lg==
dependencies:
hey-listen "^1.0.8"
tslib "^2.1.0"
styled-components-breakpoint@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/styled-components-breakpoint/-/styled-components-breakpoint-2.1.1.tgz#37c1b92b0e96c1bbc5d293724d7a114daaa15fca"
@@ -13146,7 +13108,7 @@ tslib@^1.9.0, tslib@^1.9.3:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0:
tslib@^2.0.3, tslib@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
@@ -13168,12 +13130,12 @@ tunnel-agent@^0.6.0:
dependencies:
safe-buffer "^5.0.1"
turndown@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/turndown/-/turndown-7.1.1.tgz#96992f2d9b40a1a03d3ea61ad31b5a5c751ef77f"
integrity sha512-BEkXaWH7Wh7e9bd2QumhfAXk5g34+6QUmmWx+0q6ThaVOLuLUqsnkq35HQ5SBHSaxjSfSM7US5o4lhJNH7B9MA==
turndown@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/turndown/-/turndown-6.0.0.tgz#c083d6109a9366be1b84b86b20af09140ea4b413"
integrity sha512-UVJBhSyRHCpNKtQ00mNWlYUM/i+tcipkb++F0PrOpt0L7EhNd0AX9mWEpL2dRFBu7LWXMp4HgAMA4OeKKnN7og==
dependencies:
domino "^2.1.6"
jsdom "^16.2.0"
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
@@ -13562,11 +13524,6 @@ utf8@^2.1.0:
resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.2.tgz#1fa0d9270e9be850d9b05027f63519bf46457d96"
integrity sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY=
utf8@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1"
integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==
utif@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/utif/-/utif-2.0.1.tgz#9e1582d9bbd20011a6588548ed3266298e711759"
@@ -13945,7 +13902,7 @@ whatwg-encoding@^1.0.5:
dependencies:
iconv-lite "0.4.24"
whatwg-fetch@^3.4.1:
whatwg-fetch@>=0.10.0, whatwg-fetch@^3.4.1:
version "3.5.0"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz#605a2cd0a7146e5db141e29d1c62ab84c0c4c868"
integrity sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A==