mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b3a1cdde00 |
@@ -3,7 +3,7 @@ Business Source License 1.1
|
||||
Parameters
|
||||
|
||||
Licensor: General Outline, Inc.
|
||||
Licensed Work: Outline 0.87.4
|
||||
Licensed Work: Outline 0.87.3
|
||||
The Licensed Work is (c) 2025 General Outline, Inc.
|
||||
Additional Use Grant: You may make use of the Licensed Work, provided that
|
||||
you may not use the Licensed Work for a Document
|
||||
@@ -15,7 +15,7 @@ Additional Use Grant: You may make use of the Licensed Work, provided that
|
||||
Licensed Work by creating teams and documents
|
||||
controlled by such third parties.
|
||||
|
||||
Change Date: 2029-09-18
|
||||
Change Date: 2029-09-01
|
||||
|
||||
Change License: Apache License, Version 2.0
|
||||
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
<img width="1640" alt="screenshot" src="https://user-images.githubusercontent.com/380914/110356468-26374600-7fef-11eb-9f6a-f2cc2c8c6590.png">
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://circleci.com/gh/outline/outline" rel="nofollow"><img src="https://circleci.com/gh/outline/outline.svg?style=shield"></a>
|
||||
<a href="http://www.typescriptlang.org" rel="nofollow"><img src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" alt="TypeScript"></a>
|
||||
<a href="https://github.com/prettier/prettier"><img src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat" alt="Prettier"></a>
|
||||
<a href="https://github.com/styled-components/styled-components"><img src="https://img.shields.io/badge/style-%F0%9F%92%85%20styled--components-orange.svg" alt="Styled Components"></a>
|
||||
<a href="https://translate.getoutline.com/project/outline" alt="Localized"><img src="https://badges.crowdin.net/outline/localized.svg"></a>
|
||||
</p>
|
||||
|
||||
This is the source code that runs [**Outline**](https://www.getoutline.com) and all the associated services. If you want to use Outline then you don't need to run this code, A hosted version of the app is offered at [getoutline.com](https://www.getoutline.com). You can also find documentation on using Outline in [our guide](https://docs.getoutline.com/s/guide).
|
||||
This is the source code that runs [**Outline**](https://www.getoutline.com) and all the associated services. If you want to use Outline then you don't need to run this code, we offer a hosted version of the app at [getoutline.com](https://www.getoutline.com). You can also find documentation on using Outline in [our guide](https://docs.getoutline.com/s/guide).
|
||||
|
||||
If you'd like to run your own copy of Outline or contribute to development then this is the place for you.
|
||||
|
||||
@@ -50,14 +51,13 @@ please refer to the [architecture document](docs/ARCHITECTURE.md) first for a hi
|
||||
|
||||
In development Outline outputs simple logging to the console, prefixed by categories. In production it outputs JSON logs, these can be easily parsed by your preferred log ingestion pipeline.
|
||||
|
||||
HTTP logging is disabled by default, but can be enabled by setting the `DEBUG=http` environment variable. logging
|
||||
can be enabled for all categories by setting `DEBUG=*` or for specific categories such as `DEBUG=database` and `LOG_LEVEL=debug`, or `LOG_LEVEL=silly` for very verbose logging.
|
||||
HTTP logging is disabled by default, but can be enabled by setting the `DEBUG=http` environment variable.
|
||||
|
||||
## Tests
|
||||
|
||||
We aim to have sufficient test coverage for critical parts of the application and aren't aiming for 100% unit test coverage. All API endpoints and anything authentication related should be thoroughly tested.
|
||||
|
||||
To add new tests, write your tests with [Jest](https://facebook.github.io/jest/) and add a file with `.test.ts` extension next to the tested code.
|
||||
To add new tests, write your tests with [Jest](https://facebook.github.io/jest/) and add a file with `.test.js` extension next to the tested code.
|
||||
|
||||
```shell
|
||||
# To run all tests
|
||||
@@ -68,14 +68,14 @@ make watch
|
||||
```
|
||||
|
||||
Once the test database is created with `make test` you may individually run
|
||||
frontend and backend tests directly with jest:
|
||||
frontend and backend tests directly.
|
||||
|
||||
```shell
|
||||
# To run backend tests
|
||||
yarn test:server
|
||||
|
||||
# To run a specific backend test in watch mode
|
||||
yarn test path/to/file.test.ts --watch
|
||||
# To run a specific backend test
|
||||
yarn test:server myTestFile
|
||||
|
||||
# To run frontend tests
|
||||
yarn test:app
|
||||
@@ -86,15 +86,14 @@ yarn test:app
|
||||
Sequelize is used to create and run migrations, for example:
|
||||
|
||||
```shell
|
||||
yarn db:create-migration --name my-migration
|
||||
yarn db:migrate
|
||||
yarn db:rollback
|
||||
yarn sequelize migration:generate --name my-migration
|
||||
yarn sequelize db:migrate
|
||||
```
|
||||
|
||||
Or, to run migrations on test database:
|
||||
Or to run migrations on test database:
|
||||
|
||||
```shell
|
||||
yarn db:migrate --env test
|
||||
yarn sequelize db:migrate --env test
|
||||
```
|
||||
|
||||
# Activity
|
||||
|
||||
@@ -13,6 +13,7 @@ import ErrorSuspended from "~/scenes/Errors/ErrorSuspended";
|
||||
import Layout from "~/components/Layout";
|
||||
import RegisterKeyDown from "~/components/RegisterKeyDown";
|
||||
import Sidebar from "~/components/Sidebar";
|
||||
import SidebarRight from "~/components/Sidebar/Right";
|
||||
import SettingsSidebar from "~/components/Sidebar/Settings";
|
||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||
import { usePostLoginPath } from "~/hooks/useLastVisitedPath";
|
||||
@@ -108,10 +109,12 @@ const AuthenticatedLayout: React.FC = ({ children }: Props) => {
|
||||
>
|
||||
{(showHistory || showComments) && (
|
||||
<Route path={`/doc/${slug}`}>
|
||||
<React.Suspense fallback={null}>
|
||||
{showHistory && <DocumentHistory />}
|
||||
{showComments && <DocumentComments />}
|
||||
</React.Suspense>
|
||||
<SidebarRight>
|
||||
<React.Suspense fallback={null}>
|
||||
{showHistory && <DocumentHistory />}
|
||||
{showComments && <DocumentComments />}
|
||||
</React.Suspense>
|
||||
</SidebarRight>
|
||||
</Route>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
@@ -155,16 +155,14 @@ const DocumentMeta: React.FC<Props> = ({
|
||||
}
|
||||
return (
|
||||
<Viewed>
|
||||
<Separator />
|
||||
<Modified highlight>{t("Never viewed")}</Modified>
|
||||
• <Modified highlight>{t("Never viewed")}</Modified>
|
||||
</Viewed>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Viewed>
|
||||
<Separator />
|
||||
{t("Viewed")} <Time dateTime={lastViewedAt} addSuffix shorten />
|
||||
• {t("Viewed")} <Time dateTime={lastViewedAt} addSuffix shorten />
|
||||
</Viewed>
|
||||
);
|
||||
};
|
||||
@@ -188,17 +186,16 @@ const DocumentMeta: React.FC<Props> = ({
|
||||
)}
|
||||
{showParentDocuments && nestedDocumentsCount > 0 && (
|
||||
<span>
|
||||
<Separator />
|
||||
{nestedDocumentsCount}{" "}
|
||||
• {nestedDocumentsCount}{" "}
|
||||
{t("nested document", {
|
||||
count: nestedDocumentsCount,
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
{timeSinceNow()}
|
||||
{timeSinceNow()}
|
||||
{canShowProgressBar && (
|
||||
<>
|
||||
<Separator />
|
||||
•
|
||||
<DocumentTasks document={document} />
|
||||
</>
|
||||
)}
|
||||
@@ -207,14 +204,6 @@ const DocumentMeta: React.FC<Props> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const Separator = styled.span`
|
||||
padding: 0 0.4em;
|
||||
|
||||
&::after {
|
||||
content: "•";
|
||||
}
|
||||
`;
|
||||
|
||||
const Strong = styled.strong`
|
||||
font-weight: 550;
|
||||
`;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { depths, s } from "@shared/styles";
|
||||
import ErrorBoundary from "~/components/ErrorBoundary";
|
||||
import Flex from "~/components/Flex";
|
||||
import ResizeBorder from "~/components/Sidebar/components/ResizeBorder";
|
||||
import useMobile from "~/hooks/useMobile";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import { sidebarAppearDuration } from "~/styles/animations";
|
||||
|
||||
@@ -19,6 +20,7 @@ function Right({ children, border, className }: Props) {
|
||||
const theme = useTheme();
|
||||
const { ui } = useStores();
|
||||
const [isResizing, setResizing] = React.useState(false);
|
||||
const isMobile = useMobile();
|
||||
const maxWidth = theme.sidebarMaxWidth;
|
||||
const minWidth = theme.sidebarMinWidth + 16; // padding
|
||||
|
||||
@@ -98,11 +100,13 @@ function Right({ children, border, className }: Props) {
|
||||
<Sidebar {...animationProps} $border={border} className={className}>
|
||||
<Position style={style} column>
|
||||
<ErrorBoundary>{children}</ErrorBoundary>
|
||||
<ResizeBorder
|
||||
onMouseDown={handleMouseDown}
|
||||
onDoubleClick={handleReset}
|
||||
dir="right"
|
||||
/>
|
||||
{!isMobile && (
|
||||
<ResizeBorder
|
||||
onMouseDown={handleMouseDown}
|
||||
onDoubleClick={handleReset}
|
||||
dir="right"
|
||||
/>
|
||||
)}
|
||||
</Position>
|
||||
</Sidebar>
|
||||
);
|
||||
|
||||
@@ -18,8 +18,6 @@ Drawer.displayName = "Drawer";
|
||||
/** Drawer's trigger. */
|
||||
const DrawerTrigger = DrawerPrimitive.Trigger;
|
||||
|
||||
const DrawerHandle = DrawerPrimitive.Handle;
|
||||
|
||||
/** Drawer's content - renders the overlay and the actual content. */
|
||||
const DrawerContent = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Content>,
|
||||
@@ -58,9 +56,11 @@ const DrawerTitle = React.forwardRef<
|
||||
const { hidden, children, ...rest } = props;
|
||||
|
||||
const title = (
|
||||
<Text size="medium" weight="bold" as={TitleWrapper} justify="center">
|
||||
{children}
|
||||
</Text>
|
||||
<TitleWrapper justify="center">
|
||||
<Text size="medium" weight="bold">
|
||||
{children}
|
||||
</Text>
|
||||
</TitleWrapper>
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -100,4 +100,4 @@ const TitleWrapper = styled(Flex)`
|
||||
padding: 8px 0;
|
||||
`;
|
||||
|
||||
export { Drawer, DrawerTrigger, DrawerHandle, DrawerContent, DrawerTitle };
|
||||
export { Drawer, DrawerTrigger, DrawerContent, DrawerTitle };
|
||||
|
||||
@@ -90,7 +90,7 @@ type StyledContentProps = {
|
||||
|
||||
const StyledContent = styled(PopoverPrimitive.Content)<StyledContentProps>`
|
||||
z-index: ${depths.modal};
|
||||
max-height: min(85vh, var(--radix-popover-content-available-height));
|
||||
max-height: var(--radix-popover-content-available-height);
|
||||
transform-origin: var(--radix-popover-content-transform-origin);
|
||||
|
||||
background: ${s("menuBackground")};
|
||||
|
||||
@@ -9,7 +9,6 @@ import { CustomTheme } from "@shared/types";
|
||||
import type { Theme } from "~/stores/UiStore";
|
||||
import useMediaQuery from "~/hooks/useMediaQuery";
|
||||
import useStores from "./useStores";
|
||||
import useQuery from "./useQuery";
|
||||
|
||||
/**
|
||||
* Builds a theme based on the current user's preferences, the current device
|
||||
@@ -24,11 +23,9 @@ export default function useBuildTheme(
|
||||
overrideTheme?: Theme
|
||||
) {
|
||||
const { ui } = useStores();
|
||||
const params = useQuery();
|
||||
const isMobile = useMediaQuery(`(max-width: ${breakpoints.tablet}px)`);
|
||||
const isPrinting = useMediaQuery("print");
|
||||
const queryTheme = (params.get("theme") as Theme) || undefined;
|
||||
const resolvedTheme = overrideTheme ?? queryTheme ?? ui.resolvedTheme;
|
||||
const resolvedTheme = overrideTheme ?? ui.resolvedTheme;
|
||||
|
||||
const theme = useMemo(
|
||||
() =>
|
||||
|
||||
+4
-4
@@ -53,8 +53,8 @@ if (element) {
|
||||
<HelmetProvider>
|
||||
<Provider {...stores}>
|
||||
<Analytics>
|
||||
<Router history={history}>
|
||||
<Theme>
|
||||
<Theme>
|
||||
<Router history={history}>
|
||||
<ErrorBoundary showTitle>
|
||||
<KBarProvider actions={[]} options={commandBarOptions}>
|
||||
<LazyPolyfill>
|
||||
@@ -74,8 +74,8 @@ if (element) {
|
||||
</LazyPolyfill>
|
||||
</KBarProvider>
|
||||
</ErrorBoundary>
|
||||
</Theme>
|
||||
</Router>
|
||||
</Router>
|
||||
</Theme>
|
||||
</Analytics>
|
||||
</Provider>
|
||||
</HelmetProvider>
|
||||
|
||||
@@ -48,7 +48,6 @@ import MembershipPreview from "./components/MembershipPreview";
|
||||
import Notices from "./components/Notices";
|
||||
import Overview from "./components/Overview";
|
||||
import ShareButton from "./components/ShareButton";
|
||||
import first from "lodash/first";
|
||||
|
||||
const IconPicker = lazy(() => import("~/components/IconPicker"));
|
||||
|
||||
@@ -207,7 +206,7 @@ const CollectionScene = observer(function _CollectionScene() {
|
||||
<Suspense fallback={fallbackIcon}>
|
||||
<IconPicker
|
||||
icon={collection.icon ?? "collection"}
|
||||
color={collection.color ?? (first(colorPalette) as string)}
|
||||
color={collection.color ?? colorPalette[0]}
|
||||
initial={collection.initial}
|
||||
size={40}
|
||||
popoverPosition="bottom-start"
|
||||
|
||||
@@ -23,7 +23,6 @@ import CommentForm from "./CommentForm";
|
||||
import CommentSortMenu from "./CommentSortMenu";
|
||||
import CommentThread from "./CommentThread";
|
||||
import Sidebar from "./SidebarLayout";
|
||||
import useMobile from "~/hooks/useMobile";
|
||||
import { ArrowDownIcon } from "~/components/Icons/ArrowIcon";
|
||||
|
||||
function Comments() {
|
||||
@@ -35,8 +34,6 @@ function Comments() {
|
||||
const document = documents.get(match.params.documentSlug);
|
||||
const focusedComment = useFocusedComment();
|
||||
const can = usePolicy(document);
|
||||
const isMobile = useMobile();
|
||||
|
||||
const query = useQuery();
|
||||
const [viewingResolved, setViewingResolved] = useState(
|
||||
query.get("resolved") !== null || focusedComment?.isResolved || false
|
||||
@@ -126,73 +123,15 @@ function Comments() {
|
||||
prevThreadCount.current = threads.length;
|
||||
}, [sortOption.type, threads.length, viewingResolved]);
|
||||
|
||||
const content =
|
||||
!document || !isEditorInitialized ? null : (
|
||||
<>
|
||||
<Scrollable
|
||||
id="comments"
|
||||
bottomShadow={!focusedComment}
|
||||
hiddenScrollbars
|
||||
topShadow
|
||||
ref={scrollableRef}
|
||||
onScroll={handleScroll}
|
||||
>
|
||||
<Wrapper $hasComments={hasComments}>
|
||||
{hasComments ? (
|
||||
threads.map((thread) => (
|
||||
<CommentThread
|
||||
key={thread.id}
|
||||
comment={thread}
|
||||
document={document}
|
||||
recessed={!!focusedComment && focusedComment.id !== thread.id}
|
||||
focused={focusedComment?.id === thread.id}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<NoComments align="center" justify="center" auto>
|
||||
<PositionedEmpty>
|
||||
{viewingResolved
|
||||
? t("No resolved comments")
|
||||
: t("No comments yet")}
|
||||
</PositionedEmpty>
|
||||
</NoComments>
|
||||
)}
|
||||
{showJumpToRecentBtn && (
|
||||
<Fade>
|
||||
<JumpToRecent onClick={scrollToBottom}>
|
||||
<Flex align="center">
|
||||
{t("New comments")}
|
||||
<ArrowDownIcon size={20} />
|
||||
</Flex>
|
||||
</JumpToRecent>
|
||||
</Fade>
|
||||
)}
|
||||
</Wrapper>
|
||||
</Scrollable>
|
||||
<AnimatePresence initial={false}>
|
||||
{(!focusedComment || isMobile) && can.comment && !viewingResolved && (
|
||||
<NewCommentForm
|
||||
draft={draft}
|
||||
onSaveDraft={onSaveDraft}
|
||||
documentId={document.id}
|
||||
placeholder={`${t("Add a comment")}…`}
|
||||
autoFocus={false}
|
||||
dir={document.dir}
|
||||
animatePresence
|
||||
standalone
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
);
|
||||
if (!document || !isEditorInitialized) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Sidebar
|
||||
title={
|
||||
<Flex align="center" justify="space-between" gap={8} auto>
|
||||
<div style={isMobile ? { padding: "0 8px" } : undefined}>
|
||||
{t("Comments")}
|
||||
</div>
|
||||
<Flex align="center" justify="space-between" auto>
|
||||
<span>{t("Comments")}</span>
|
||||
<CommentSortMenu
|
||||
viewingResolved={viewingResolved}
|
||||
onChange={(val) => {
|
||||
@@ -204,7 +143,60 @@ function Comments() {
|
||||
onClose={() => ui.set({ commentsExpanded: false })}
|
||||
scrollable={false}
|
||||
>
|
||||
{content}
|
||||
<Scrollable
|
||||
id="comments"
|
||||
bottomShadow={!focusedComment}
|
||||
hiddenScrollbars
|
||||
topShadow
|
||||
ref={scrollableRef}
|
||||
onScroll={handleScroll}
|
||||
>
|
||||
<Wrapper $hasComments={hasComments}>
|
||||
{hasComments ? (
|
||||
threads.map((thread) => (
|
||||
<CommentThread
|
||||
key={thread.id}
|
||||
comment={thread}
|
||||
document={document}
|
||||
recessed={!!focusedComment && focusedComment.id !== thread.id}
|
||||
focused={focusedComment?.id === thread.id}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<NoComments align="center" justify="center" auto>
|
||||
<PositionedEmpty>
|
||||
{viewingResolved
|
||||
? t("No resolved comments")
|
||||
: t("No comments yet")}
|
||||
</PositionedEmpty>
|
||||
</NoComments>
|
||||
)}
|
||||
{showJumpToRecentBtn && (
|
||||
<Fade>
|
||||
<JumpToRecent onClick={scrollToBottom}>
|
||||
<Flex align="center">
|
||||
{t("New comments")}
|
||||
<ArrowDownIcon size={20} />
|
||||
</Flex>
|
||||
</JumpToRecent>
|
||||
</Fade>
|
||||
)}
|
||||
</Wrapper>
|
||||
</Scrollable>
|
||||
<AnimatePresence initial={false}>
|
||||
{!focusedComment && can.comment && !viewingResolved && (
|
||||
<NewCommentForm
|
||||
draft={draft}
|
||||
onSaveDraft={onSaveDraft}
|
||||
documentId={document.id}
|
||||
placeholder={`${t("Add a comment")}…`}
|
||||
autoFocus={false}
|
||||
dir={document.dir}
|
||||
animatePresence
|
||||
standalone
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</Sidebar>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,13 +9,12 @@ import { TeamPreference } from "@shared/types";
|
||||
import Document from "~/models/Document";
|
||||
import Revision from "~/models/Revision";
|
||||
import { openDocumentInsights } from "~/actions/definitions/documents";
|
||||
import DocumentMeta, { Separator } from "~/components/DocumentMeta";
|
||||
import DocumentMeta from "~/components/DocumentMeta";
|
||||
import Fade from "~/components/Fade";
|
||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||
import { useLocationSidebarContext } from "~/hooks/useLocationSidebarContext";
|
||||
import usePolicy from "~/hooks/usePolicy";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import breakpoint from "styled-components-breakpoint";
|
||||
import { documentPath } from "~/utils/routeHelpers";
|
||||
import NudeButton from "~/components/NudeButton";
|
||||
|
||||
@@ -47,7 +46,7 @@ function TitleDocumentMeta({ to, document, revision, ...rest }: Props) {
|
||||
<Meta document={document} revision={revision} to={to} replace {...rest}>
|
||||
{commentingEnabled && can.comment && (
|
||||
<>
|
||||
<Separator />
|
||||
•
|
||||
<CommentLink
|
||||
to={{
|
||||
pathname: documentPath(document),
|
||||
@@ -67,7 +66,7 @@ function TitleDocumentMeta({ to, document, revision, ...rest }: Props) {
|
||||
!document.isDraft &&
|
||||
!document.isTemplate ? (
|
||||
<Wrapper>
|
||||
<Separator />
|
||||
•
|
||||
<InsightsButton action={openDocumentInsights}>
|
||||
{t("Viewed by")}{" "}
|
||||
{onlyYou
|
||||
@@ -109,16 +108,6 @@ export const Meta = styled(DocumentMeta)<{ rtl?: boolean }>`
|
||||
user-select: none;
|
||||
z-index: 1;
|
||||
|
||||
${breakpoint("mobile", "tablet")`
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
line-height: 1.6;
|
||||
|
||||
${Separator} {
|
||||
display: none;
|
||||
}
|
||||
`}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
cursor: var(--pointer);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import last from "lodash/last";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -32,7 +33,6 @@ import { decodeURIComponentSafe } from "~/utils/urls";
|
||||
import MultiplayerEditor from "./AsyncMultiplayerEditor";
|
||||
import DocumentMeta from "./DocumentMeta";
|
||||
import DocumentTitle from "./DocumentTitle";
|
||||
import first from "lodash/first";
|
||||
|
||||
const extensions = withUIExtensions(withComments(richExtensions));
|
||||
|
||||
@@ -80,7 +80,7 @@ function DocumentEditor(props: Props, ref: React.RefObject<any>) {
|
||||
const can = usePolicy(document);
|
||||
const commentingEnabled = !!team?.getPreference(TeamPreference.Commenting);
|
||||
|
||||
const iconColor = document.color ?? (first(colorPalette) as string);
|
||||
const iconColor = document.color ?? (last(colorPalette) as string);
|
||||
const childRef = React.useRef<HTMLDivElement>(null);
|
||||
const focusAtStart = React.useCallback(() => {
|
||||
if (ref.current) {
|
||||
|
||||
@@ -15,7 +15,6 @@ import { useLocationSidebarContext } from "~/hooks/useLocationSidebarContext";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import { documentPath } from "~/utils/routeHelpers";
|
||||
import Sidebar from "./SidebarLayout";
|
||||
import useMobile from "~/hooks/useMobile";
|
||||
|
||||
const DocumentEvents = [
|
||||
"documents.publish",
|
||||
@@ -38,7 +37,6 @@ function History() {
|
||||
const document = documents.get(match.params.documentSlug);
|
||||
const [revisionsOffset, setRevisionsOffset] = React.useState(0);
|
||||
const [eventsOffset, setEventsOffset] = React.useState(0);
|
||||
const isMobile = useMobile();
|
||||
|
||||
const fetchHistory = React.useCallback(async () => {
|
||||
if (!document) {
|
||||
@@ -127,10 +125,6 @@ function History() {
|
||||
}, [revisions, document, revisionEvents, nonRevisionEvents]);
|
||||
|
||||
const onCloseHistory = React.useCallback(() => {
|
||||
if (isMobile) {
|
||||
// Allow closing the history drawer on mobile to view revision content
|
||||
return;
|
||||
}
|
||||
if (document) {
|
||||
history.push({
|
||||
pathname: documentPath(document),
|
||||
|
||||
@@ -3,19 +3,15 @@ import { BackIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import { s, ellipsis } from "@shared/styles";
|
||||
import { depths, s, ellipsis } from "@shared/styles";
|
||||
import Button from "~/components/Button";
|
||||
import Flex from "~/components/Flex";
|
||||
import { Portal } from "~/components/Portal";
|
||||
import Scrollable from "~/components/Scrollable";
|
||||
import Tooltip from "~/components/Tooltip";
|
||||
import useMobile from "~/hooks/useMobile";
|
||||
import { draggableOnDesktop } from "~/styles";
|
||||
import RightSidebar from "~/components/Sidebar/Right";
|
||||
import {
|
||||
Drawer,
|
||||
DrawerContent,
|
||||
DrawerTitle,
|
||||
} from "~/components/primitives/Drawer";
|
||||
import { fadeIn } from "~/styles/animations";
|
||||
|
||||
type Props = Omit<React.HTMLAttributes<HTMLDivElement>, "title"> & {
|
||||
/* The title of the sidebar */
|
||||
@@ -23,7 +19,7 @@ type Props = Omit<React.HTMLAttributes<HTMLDivElement>, "title"> & {
|
||||
/* The content of the sidebar */
|
||||
children: React.ReactNode;
|
||||
/* Called when the sidebar is closed */
|
||||
onClose: () => void;
|
||||
onClose: React.MouseEventHandler;
|
||||
/* Whether the sidebar should be scrollable */
|
||||
scrollable?: boolean;
|
||||
};
|
||||
@@ -32,23 +28,8 @@ function SidebarLayout({ title, onClose, children, scrollable = true }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const isMobile = useMobile();
|
||||
|
||||
const content = scrollable ? (
|
||||
<Scrollable hiddenScrollbars topShadow>
|
||||
{children}
|
||||
</Scrollable>
|
||||
) : (
|
||||
children
|
||||
);
|
||||
|
||||
return isMobile ? (
|
||||
<Drawer onClose={onClose} defaultOpen>
|
||||
<DrawerContent>
|
||||
<DrawerTitle>{title}</DrawerTitle>
|
||||
{content}
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
) : (
|
||||
<RightSidebar>
|
||||
return (
|
||||
<>
|
||||
<Header>
|
||||
<Title>{title}</Title>
|
||||
<Tooltip content={t("Close")} shortcut="Esc">
|
||||
@@ -60,11 +41,35 @@ function SidebarLayout({ title, onClose, children, scrollable = true }: Props) {
|
||||
/>
|
||||
</Tooltip>
|
||||
</Header>
|
||||
{content}
|
||||
</RightSidebar>
|
||||
{scrollable ? (
|
||||
<Scrollable hiddenScrollbars topShadow>
|
||||
{children}
|
||||
</Scrollable>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
|
||||
{isMobile && (
|
||||
<Portal>
|
||||
<Backdrop onClick={onClose} />
|
||||
</Portal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const Backdrop = styled.a`
|
||||
animation: ${fadeIn} 250ms ease-in-out;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
cursor: default;
|
||||
z-index: ${depths.mobileSidebar - 1};
|
||||
background: ${s("backdrop")};
|
||||
`;
|
||||
|
||||
const ForwardIcon = styled(BackIcon)`
|
||||
transform: rotate(180deg);
|
||||
flex-shrink: 0;
|
||||
|
||||
+13
-13
@@ -51,16 +51,16 @@
|
||||
"> 0.25%, not dead"
|
||||
],
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.888.0",
|
||||
"@aws-sdk/lib-storage": "3.888.0",
|
||||
"@aws-sdk/s3-presigned-post": "3.888.0",
|
||||
"@aws-sdk/s3-request-presigner": "3.888.0",
|
||||
"@aws-sdk/signature-v4-crt": "^3.888.0",
|
||||
"@babel/core": "^7.28.4",
|
||||
"@aws-sdk/client-s3": "3.879.0",
|
||||
"@aws-sdk/lib-storage": "3.879.0",
|
||||
"@aws-sdk/s3-presigned-post": "3.879.0",
|
||||
"@aws-sdk/s3-request-presigner": "3.879.0",
|
||||
"@aws-sdk/signature-v4-crt": "^3.879.0",
|
||||
"@babel/core": "^7.28.3",
|
||||
"@babel/plugin-proposal-decorators": "^7.28.0",
|
||||
"@babel/plugin-transform-class-properties": "^7.27.1",
|
||||
"@babel/plugin-transform-destructuring": "^7.28.0",
|
||||
"@babel/plugin-transform-regenerator": "^7.28.4",
|
||||
"@babel/plugin-transform-regenerator": "^7.28.3",
|
||||
"@babel/preset-env": "^7.28.3",
|
||||
"@babel/preset-react": "^7.27.1",
|
||||
"@benrbray/prosemirror-math": "^0.2.2",
|
||||
@@ -73,9 +73,9 @@
|
||||
"@dotenvx/dotenvx": "^1.49.0",
|
||||
"@emoji-mart/data": "^1.2.1",
|
||||
"@fast-csv/parse": "^5.0.5",
|
||||
"@fortawesome/fontawesome-svg-core": "^7.0.1",
|
||||
"@fortawesome/free-brands-svg-icons": "^7.0.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^7.0.1",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||
"@fortawesome/react-fontawesome": "^0.2.6",
|
||||
"@getoutline/react-roving-tabindex": "^3.2.4",
|
||||
"@hocuspocus/extension-redis": "1.1.2",
|
||||
@@ -148,7 +148,7 @@
|
||||
"i18next-fs-backend": "^2.6.0",
|
||||
"i18next-http-backend": "^2.7.3",
|
||||
"invariant": "^2.2.4",
|
||||
"ioredis": "^5.7.0",
|
||||
"ioredis": "^5.6.0",
|
||||
"is-printable-key-event": "^1.0.0",
|
||||
"jsdom": "^22.1.0",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
@@ -315,7 +315,7 @@
|
||||
"@types/node": "20.17.30",
|
||||
"@types/node-fetch": "^2.6.9",
|
||||
"@types/nodemailer": "^6.4.17",
|
||||
"@types/passport-oauth2": "^1.8.0",
|
||||
"@types/passport-oauth2": "^1.4.17",
|
||||
"@types/pluralize": "^0.0.33",
|
||||
"@types/png-chunks-extract": "^1.0.2",
|
||||
"@types/proxy-from-env": "^1.0.4",
|
||||
@@ -381,6 +381,6 @@
|
||||
"qs": "6.9.7",
|
||||
"prismjs": "1.30.0"
|
||||
},
|
||||
"version": "0.87.4",
|
||||
"version": "0.87.3",
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
}
|
||||
|
||||
@@ -106,70 +106,4 @@ describe("documentDuplicator", () => {
|
||||
expect(response[0].color).toEqual(original.color);
|
||||
expect(response[0].publishedAt).toBeNull();
|
||||
});
|
||||
|
||||
it("should set originalDocumentId in sourceMetadata when duplicating", async () => {
|
||||
const user = await buildUser();
|
||||
const original = await buildDocument({
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
sourceMetadata: { fileName: "test.md", externalId: "ext123" },
|
||||
});
|
||||
|
||||
const response = await sequelize.transaction((transaction) =>
|
||||
documentDuplicator({
|
||||
document: original,
|
||||
collection: original.collection,
|
||||
user,
|
||||
ctx: createContext({ user, transaction }),
|
||||
})
|
||||
);
|
||||
|
||||
expect(response).toHaveLength(1);
|
||||
expect(response[0].sourceMetadata).toEqual({
|
||||
fileName: "test.md",
|
||||
externalId: "ext123",
|
||||
originalDocumentId: original.id,
|
||||
});
|
||||
});
|
||||
|
||||
it("should set originalDocumentId for child documents when duplicating recursively", async () => {
|
||||
const user = await buildUser();
|
||||
const original = await buildDocument({
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
|
||||
const childDocument = await buildDocument({
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
parentDocumentId: original.id,
|
||||
collection: original.collection,
|
||||
sourceMetadata: { fileName: "child.md" },
|
||||
});
|
||||
|
||||
const response = await sequelize.transaction((transaction) =>
|
||||
documentDuplicator({
|
||||
document: original,
|
||||
collection: original.collection,
|
||||
user,
|
||||
recursive: true,
|
||||
ctx: createContext({ user, transaction }),
|
||||
})
|
||||
);
|
||||
|
||||
expect(response).toHaveLength(2);
|
||||
|
||||
// Check parent document
|
||||
const duplicatedParent = response.find((doc) => !doc.parentDocumentId);
|
||||
expect(duplicatedParent?.sourceMetadata?.originalDocumentId).toEqual(
|
||||
original.id
|
||||
);
|
||||
|
||||
// Check child document
|
||||
const duplicatedChild = response.find((doc) => doc.parentDocumentId);
|
||||
expect(duplicatedChild?.sourceMetadata?.originalDocumentId).toEqual(
|
||||
childDocument.id
|
||||
);
|
||||
expect(duplicatedChild?.sourceMetadata?.fileName).toEqual("child.md");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -52,10 +52,6 @@ export default async function documentDuplicator({
|
||||
DocumentHelper.toProsemirror(document),
|
||||
["comment"]
|
||||
),
|
||||
sourceMetadata: {
|
||||
...document.sourceMetadata,
|
||||
originalDocumentId: document.id,
|
||||
},
|
||||
...sharedProperties,
|
||||
});
|
||||
|
||||
@@ -89,10 +85,6 @@ export default async function documentDuplicator({
|
||||
DocumentHelper.toProsemirror(childDocument),
|
||||
["comment"]
|
||||
),
|
||||
sourceMetadata: {
|
||||
...childDocument.sourceMetadata,
|
||||
originalDocumentId: childDocument.id,
|
||||
},
|
||||
...sharedProperties,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
// Drop the existing foreign key constraint
|
||||
await queryInterface.sequelize.query(
|
||||
`ALTER TABLE "shares" DROP CONSTRAINT "shares_collectionId_fkey"`
|
||||
);
|
||||
|
||||
// Add the foreign key constraint with CASCADE delete
|
||||
await queryInterface.sequelize.query(`
|
||||
ALTER TABLE "shares"
|
||||
ADD CONSTRAINT "shares_collectionId_fkey"
|
||||
FOREIGN KEY("collectionId")
|
||||
REFERENCES "collections" ("id")
|
||||
ON DELETE CASCADE
|
||||
`);
|
||||
},
|
||||
|
||||
async down(queryInterface, Sequelize) {
|
||||
// Drop the cascade constraint
|
||||
await queryInterface.sequelize.query(
|
||||
`ALTER TABLE "shares" DROP CONSTRAINT "shares_collectionId_fkey"`
|
||||
);
|
||||
|
||||
// Add back the original constraint without cascade
|
||||
await queryInterface.sequelize.query(`
|
||||
ALTER TABLE "shares"
|
||||
ADD CONSTRAINT "shares_collectionId_fkey"
|
||||
FOREIGN KEY("collectionId")
|
||||
REFERENCES "collections" ("id")
|
||||
ON DELETE NO ACTION
|
||||
`);
|
||||
},
|
||||
};
|
||||
@@ -99,7 +99,6 @@ async function presentDocument(
|
||||
importType: source?.format,
|
||||
createdByName: document.sourceMetadata.createdByName,
|
||||
fileName: document.sourceMetadata?.fileName,
|
||||
originalDocumentId: document.sourceMetadata?.originalDocumentId,
|
||||
}
|
||||
: undefined;
|
||||
}
|
||||
|
||||
@@ -308,15 +308,16 @@ export default abstract class ImportsProcessor<
|
||||
|
||||
for (const input of importTask.input) {
|
||||
const externalId = input.externalId;
|
||||
const internalId = await this.getInternalId(externalId, idMap);
|
||||
const internalId = this.getInternalId(externalId, idMap);
|
||||
|
||||
const parentExternalId = input.parentExternalId;
|
||||
const parentInternalId = parentExternalId
|
||||
? await this.getInternalId(parentExternalId, idMap)
|
||||
? this.getInternalId(parentExternalId, idMap)
|
||||
: undefined;
|
||||
|
||||
const collectionExternalId = input.collectionExternalId;
|
||||
const collectionInternalId = collectionExternalId
|
||||
? await this.getInternalId(collectionExternalId, idMap)
|
||||
? this.getInternalId(collectionExternalId, idMap)
|
||||
: undefined;
|
||||
|
||||
const output = outputMap[externalId];
|
||||
@@ -338,13 +339,12 @@ export default abstract class ImportsProcessor<
|
||||
transaction,
|
||||
});
|
||||
|
||||
const transformedContent = await this.updateMentionsAndAttachments({
|
||||
const transformedContent = this.updateMentionsAndAttachments({
|
||||
content: output.content,
|
||||
attachments,
|
||||
importInput,
|
||||
idMap,
|
||||
actorId: importModel.createdById,
|
||||
teamId: importModel.teamId,
|
||||
});
|
||||
|
||||
if (collectionItem) {
|
||||
@@ -408,7 +408,8 @@ export default abstract class ImportsProcessor<
|
||||
const isRootDocument =
|
||||
!parentExternalId || !!importInput[parentExternalId];
|
||||
|
||||
const defaults = {
|
||||
const document = Document.build({
|
||||
id: internalId,
|
||||
title: output.title,
|
||||
icon: output.emoji,
|
||||
content: transformedContent,
|
||||
@@ -429,39 +430,16 @@ export default abstract class ImportsProcessor<
|
||||
createdAt: output.createdAt ?? now,
|
||||
updatedAt: output.updatedAt ?? now,
|
||||
publishedAt: output.updatedAt ?? output.createdAt ?? now,
|
||||
};
|
||||
});
|
||||
|
||||
try {
|
||||
await Document.findOrCreateWithCtx(
|
||||
ctx,
|
||||
{
|
||||
where: {
|
||||
id: internalId,
|
||||
},
|
||||
defaults,
|
||||
silent: true,
|
||||
},
|
||||
{
|
||||
name: "create",
|
||||
data: { title: output.title, source: "import" },
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
if (err instanceof UniqueConstraintError) {
|
||||
Logger.error(
|
||||
`ImportsProcessor document creation failed due to unique constraint error (${internalId}: ${defaults.title})`,
|
||||
err,
|
||||
{
|
||||
fields: err.fields,
|
||||
documentId: internalId,
|
||||
title: defaults.title,
|
||||
collectionId: defaults.collectionId,
|
||||
parentDocumentId: defaults.parentDocumentId,
|
||||
}
|
||||
);
|
||||
await document.saveWithCtx(
|
||||
ctx,
|
||||
{ silent: true },
|
||||
{
|
||||
name: "create",
|
||||
data: { title: output.title, source: "import" },
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
);
|
||||
|
||||
// Update document id for attachments in document content.
|
||||
await Attachment.update(
|
||||
@@ -486,13 +464,12 @@ export default abstract class ImportsProcessor<
|
||||
* @param actorId ID of the user who created the import.
|
||||
* @returns Updated ProseMirrorDoc.
|
||||
*/
|
||||
private async updateMentionsAndAttachments({
|
||||
private updateMentionsAndAttachments({
|
||||
content,
|
||||
attachments,
|
||||
idMap,
|
||||
importInput,
|
||||
actorId,
|
||||
teamId,
|
||||
}: {
|
||||
content: ProsemirrorDoc;
|
||||
attachments: Attachment[];
|
||||
@@ -500,8 +477,7 @@ export default abstract class ImportsProcessor<
|
||||
// oxlint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
importInput: Record<string, ImportInput<any>[number]>;
|
||||
actorId: string;
|
||||
teamId: string;
|
||||
}): Promise<ProsemirrorDoc> {
|
||||
}): ProsemirrorDoc {
|
||||
// special case when the doc content is empty.
|
||||
if (!content.content.length) {
|
||||
return content;
|
||||
@@ -510,7 +486,7 @@ export default abstract class ImportsProcessor<
|
||||
const attachmentsMap = keyBy(attachments, "id");
|
||||
const doc = ProsemirrorHelper.toProsemirror(content);
|
||||
|
||||
const transformMentionNode = async (node: Node): Promise<Node> => {
|
||||
const transformMentionNode = (node: Node): Node => {
|
||||
const json = node.toJSON() as ProsemirrorData;
|
||||
const attrs = json.attrs ?? {};
|
||||
|
||||
@@ -518,7 +494,7 @@ export default abstract class ImportsProcessor<
|
||||
attrs.actorId = actorId;
|
||||
|
||||
const externalId = attrs.modelId as string;
|
||||
attrs.modelId = await this.getInternalId(externalId, idMap, teamId);
|
||||
attrs.modelId = this.getInternalId(externalId, idMap);
|
||||
|
||||
const isCollectionMention = !!importInput[externalId]; // the referenced externalId is a root page.
|
||||
attrs.type = isCollectionMention
|
||||
@@ -539,66 +515,37 @@ export default abstract class ImportsProcessor<
|
||||
return Node.fromJSON(schema, json);
|
||||
};
|
||||
|
||||
const transformFragment = async (fragment: Fragment): Promise<Fragment> => {
|
||||
const nodePromises: Promise<Node>[] = [];
|
||||
const transformFragment = (fragment: Fragment): Fragment => {
|
||||
const nodes: Node[] = [];
|
||||
|
||||
fragment.forEach((node) => {
|
||||
if (node.type.name === "mention") {
|
||||
nodePromises.push(transformMentionNode(node));
|
||||
} else if (node.type.name === "attachment") {
|
||||
nodePromises.push(Promise.resolve(transformAttachmentNode(node)));
|
||||
} else {
|
||||
nodePromises.push(
|
||||
transformFragment(node.content).then((transformedContent) =>
|
||||
node.copy(transformedContent)
|
||||
)
|
||||
);
|
||||
}
|
||||
nodes.push(
|
||||
node.type.name === "mention"
|
||||
? transformMentionNode(node)
|
||||
: node.type.name === "attachment"
|
||||
? transformAttachmentNode(node)
|
||||
: node.copy(transformFragment(node.content))
|
||||
);
|
||||
});
|
||||
|
||||
const nodes = await Promise.all(nodePromises);
|
||||
return Fragment.fromArray(nodes);
|
||||
};
|
||||
|
||||
return doc.copy(await transformFragment(doc.content)).toJSON();
|
||||
return doc.copy(transformFragment(doc.content)).toJSON();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get internalId for the given externalId.
|
||||
* Returned internalId will be used as "id" for collections and documents created in the import.
|
||||
*
|
||||
* @param teamId teamId associated with the import.
|
||||
* @param externalId externalId from a source.
|
||||
* @param idMap Map of internalId to externalId.
|
||||
* @returns Mapped internalId.
|
||||
*/
|
||||
private async getInternalId(
|
||||
externalId: string,
|
||||
idMap: Record<string, string>,
|
||||
teamId?: string
|
||||
) {
|
||||
let internalId = idMap[externalId];
|
||||
|
||||
if (!internalId && teamId) {
|
||||
const existingId = (
|
||||
await Document.findOne({
|
||||
attributes: ["id"],
|
||||
where: {
|
||||
teamId,
|
||||
sourceMetadata: {
|
||||
externalId,
|
||||
},
|
||||
},
|
||||
})
|
||||
)?.id;
|
||||
|
||||
if (existingId) {
|
||||
return existingId;
|
||||
}
|
||||
}
|
||||
|
||||
idMap[externalId] = internalId ?? uuidv4();
|
||||
return idMap[externalId];
|
||||
private getInternalId(externalId: string, idMap: Record<string, string>) {
|
||||
const internalId = idMap[externalId] ?? uuidv4();
|
||||
idMap[externalId] = internalId;
|
||||
return internalId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -58,8 +58,6 @@ export const TeamsUpdateSchema = BaseSchema.extend({
|
||||
.optional(),
|
||||
/** Side to display the document's table of contents in relation to the main content. */
|
||||
tocPosition: z.nativeEnum(TOCPosition).optional(),
|
||||
/** Whether to prevent shared documents from being embedded in iframes on external websites. */
|
||||
preventDocumentEmbedding: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
}),
|
||||
|
||||
@@ -194,11 +194,8 @@ export const renderShare = async (ctx: Context, next: Next) => {
|
||||
ctx.status = 404;
|
||||
}
|
||||
|
||||
// Allow shares to be embedded in iframes on other websites unless prevented by team preference
|
||||
const preventEmbedding = team?.getPreference(TeamPreference.PreventDocumentEmbedding) ?? false;
|
||||
if (!preventEmbedding) {
|
||||
ctx.remove("X-Frame-Options");
|
||||
}
|
||||
// Allow shares to be embedded in iframes on other websites
|
||||
ctx.remove("X-Frame-Options");
|
||||
|
||||
const publicBranding =
|
||||
team?.getPreference(TeamPreference.PublicBranding) ?? false;
|
||||
|
||||
@@ -32,7 +32,6 @@ export const TeamPreferenceDefaults: TeamPreferences = {
|
||||
[TeamPreference.Commenting]: true,
|
||||
[TeamPreference.CustomTheme]: undefined,
|
||||
[TeamPreference.TocPosition]: TOCPosition.Left,
|
||||
[TeamPreference.PreventDocumentEmbedding]: false,
|
||||
};
|
||||
|
||||
export const UserPreferenceDefaults: UserPreferences = {
|
||||
|
||||
@@ -23,9 +23,8 @@ export default function splitHeading(type: NodeType): Command {
|
||||
const previousBlockIsCollapsed = !!collapsedNodes.find(
|
||||
(a) => a.pos === previousBlock?.pos
|
||||
);
|
||||
const isEmpty = $from.parent.content.size === 0;
|
||||
|
||||
if (previousBlockIsCollapsed && !isEmpty) {
|
||||
if (previousBlockIsCollapsed) {
|
||||
// Insert a new heading directly before this one
|
||||
const transaction = state.tr.insert(
|
||||
$from.before(),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
mathBackspaceCmd,
|
||||
insertMathCmd,
|
||||
makeInlineMathInputRule,
|
||||
mathSchemaSpec,
|
||||
} from "@benrbray/prosemirror-math";
|
||||
import { PluginSimple } from "markdown-it";
|
||||
@@ -15,8 +16,6 @@ import MathPlugin from "../extensions/Math";
|
||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||
import mathRule, { REGEX_INLINE_MATH_DOLLARS } from "../rules/math";
|
||||
import Node from "./Node";
|
||||
import { InputRule } from "prosemirror-inputrules";
|
||||
import { isInCode } from "../queries/isInCode";
|
||||
|
||||
export default class Math extends Node {
|
||||
get name() {
|
||||
@@ -36,34 +35,10 @@ export default class Math extends Node {
|
||||
|
||||
inputRules({ schema }: { schema: Schema }) {
|
||||
return [
|
||||
new InputRule(REGEX_INLINE_MATH_DOLLARS, (state, match, start, end) => {
|
||||
if (isInCode(state)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let $start = state.doc.resolve(start);
|
||||
let index = $start.index();
|
||||
let $end = state.doc.resolve(end);
|
||||
// check if replacement valid
|
||||
if (
|
||||
!$start.parent.canReplaceWith(
|
||||
index,
|
||||
$end.index(),
|
||||
schema.nodes.math_inline
|
||||
)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
// perform replacement
|
||||
return state.tr.replaceRangeWith(
|
||||
start,
|
||||
end,
|
||||
schema.nodes.math_inline.create(
|
||||
undefined,
|
||||
schema.nodes.math_inline.schema.text(match[1])
|
||||
)
|
||||
);
|
||||
}),
|
||||
makeInlineMathInputRule(
|
||||
REGEX_INLINE_MATH_DOLLARS,
|
||||
schema.nodes.math_inline
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -17,19 +17,14 @@ export function getMarksBetween(
|
||||
let marks: { start: number; end: number; mark: Mark }[] = [];
|
||||
|
||||
state.doc.nodesBetween(start, end, (node, pos) => {
|
||||
if (node.isText) {
|
||||
const nodeStart = Math.max(start, pos);
|
||||
const nodeEnd = Math.min(end, pos + node.nodeSize);
|
||||
|
||||
marks = [
|
||||
...marks,
|
||||
...node.marks.map((mark) => ({
|
||||
start: nodeStart,
|
||||
end: nodeEnd,
|
||||
mark,
|
||||
})),
|
||||
];
|
||||
}
|
||||
marks = [
|
||||
...marks,
|
||||
...node.marks.map((mark) => ({
|
||||
start: pos,
|
||||
end: pos + node.nodeSize,
|
||||
mark,
|
||||
})),
|
||||
];
|
||||
});
|
||||
|
||||
return marks;
|
||||
|
||||
@@ -20,21 +20,21 @@ type Options = {
|
||||
*/
|
||||
export function isInCode(state: EditorState, options?: Options): boolean {
|
||||
const { nodes, marks } = state.schema;
|
||||
const opts =
|
||||
options?.inclusive !== undefined
|
||||
? { inclusive: options?.inclusive }
|
||||
: undefined;
|
||||
|
||||
if (!options?.onlyMark) {
|
||||
if (
|
||||
nodes.code_block &&
|
||||
isNodeActive(nodes.code_block, undefined, opts)(state)
|
||||
isNodeActive(nodes.code_block, undefined, {
|
||||
inclusive: options?.inclusive,
|
||||
})(state)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
nodes.code_fence &&
|
||||
isNodeActive(nodes.code_fence, undefined, opts)(state)
|
||||
isNodeActive(nodes.code_fence, undefined, {
|
||||
inclusive: options?.inclusive,
|
||||
})(state)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@@ -42,7 +42,9 @@ export function isInCode(state: EditorState, options?: Options): boolean {
|
||||
|
||||
if (!options?.onlyBlock) {
|
||||
if (marks.code_inline) {
|
||||
return isMarkActive(marks.code_inline, undefined, opts)(state);
|
||||
return isMarkActive(marks.code_inline, undefined, {
|
||||
inclusive: options?.inclusive,
|
||||
})(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -468,21 +468,19 @@
|
||||
"Replace": "Ersetzen",
|
||||
"Replace all": "Alle ersetzen",
|
||||
"Image width": "Bildbreite",
|
||||
"Width": "Breite",
|
||||
"Image height": "Bildhöhe",
|
||||
"Height": "Höhe",
|
||||
"Profile picture": "Profilbild",
|
||||
"Create a new doc": "Neues Dokument erstellen",
|
||||
"{{ userName }} won't be notified, as they do not have access to this document": "{{ userName }} wird nicht benachrichtigt, da sie keinen Zugriff auf dieses Dokument haben",
|
||||
"Keep as link": "Als Link beibehalten",
|
||||
"Mention": "Erwähnung",
|
||||
"Embed": "Einbetten",
|
||||
"Insert after": "Danach einfügen",
|
||||
"Insert before": "Davor einfügen",
|
||||
"Move up": "Nach oben",
|
||||
"Move down": "Nach unten",
|
||||
"Move left": "Nach links bewegen",
|
||||
"Move right": "Nach rechts bewegen",
|
||||
"Insert after": "Insert after",
|
||||
"Insert before": "Insert before",
|
||||
"Move up": "Move up",
|
||||
"Move down": "Move down",
|
||||
"Move left": "Move left",
|
||||
"Move right": "Move right",
|
||||
"Align center": "Zentrieren",
|
||||
"Align left": "Links ausrichten",
|
||||
"Align right": "Rechts ausrichten",
|
||||
@@ -499,6 +497,8 @@
|
||||
"Create a new child doc": "Neues untergeordnetes Dokument erstellen",
|
||||
"Delete table": "Tabelle löschen",
|
||||
"Delete file": "Datei löschen",
|
||||
"Width": "Breite",
|
||||
"Height": "Höhe",
|
||||
"Download file": "Datei herunterladen",
|
||||
"Replace file": "Datei ersetzen",
|
||||
"Delete image": "Bild löschen",
|
||||
@@ -584,7 +584,7 @@
|
||||
"Manual sort": "Manuelle Sortierung",
|
||||
"Collection menu": "Sammlungsmenü",
|
||||
"Comment options": "Kommentar Optionen",
|
||||
"Enable viewer insights": "Leserstatistiken aktivieren",
|
||||
"Enable viewer insights": "Leser Statistiken aktivieren",
|
||||
"Enable embeds": "Einbettungen aktivieren",
|
||||
"File": "Datei",
|
||||
"Group members": "Gruppenmitglieder",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"New API key": "Nuova chiave API",
|
||||
"Delete": "Cancella",
|
||||
"Revoke": "Revoca",
|
||||
"Revoke API key": "Revoca chiave API",
|
||||
"Revoke API key": "Revoke API key",
|
||||
"Revoke token": "Revoca token",
|
||||
"Open collection": "Apri la raccolta",
|
||||
"New collection": "Nuova raccolta",
|
||||
@@ -40,8 +40,8 @@
|
||||
"Copy ID": "Copia ID",
|
||||
"Clear IndexedDB cache": "Pulisci cache IndexedDB",
|
||||
"IndexedDB cache cleared": "Cache IndexedDB pulita",
|
||||
"Clear local storage": "Pulisci archivio locale",
|
||||
"Local storage cleared": "Archivio locale pulito",
|
||||
"Clear local storage": "Clear local storage",
|
||||
"Local storage cleared": "Local storage cleared",
|
||||
"Toggle debug logging": "Attiva/Disattiva il log di debug",
|
||||
"Debug logging enabled": "Log di debug attivato",
|
||||
"Debug logging disabled": "Log di debug disattivato",
|
||||
@@ -88,9 +88,9 @@
|
||||
"Create template": "Crea un modello",
|
||||
"Open random document": "Apri un documento casuale",
|
||||
"Search documents for \"{{searchQuery}}\"": "Cerca documenti per \"{{searchQuery}}\"",
|
||||
"Move to workspace": "Sposta nello spazio di lavoro",
|
||||
"Move to workspace": "Sposta allo spazio di lavoro",
|
||||
"Move": "Sposta",
|
||||
"Move to collection": "Sposta nella raccolta",
|
||||
"Move to collection": "Sposta alla raccolta",
|
||||
"Move {{ documentType }}": "Sposta {{ documentType }}",
|
||||
"Are you sure you want to archive this document?": "Sei sicuro di voler archiviare questo documento?",
|
||||
"Document archived": "Documento archiviato",
|
||||
@@ -142,7 +142,7 @@
|
||||
"Change theme": "Cambia tema",
|
||||
"Change theme to": "Cambia tema in",
|
||||
"Share link copied": "Link di condivisione copiato",
|
||||
"Go to collection": "Vai alla raccolta",
|
||||
"Go to collection": "Go to collection",
|
||||
"Go to document": "Vai al documento",
|
||||
"Revoke link": "Revoca il link",
|
||||
"Share link revoked": "Link condivisione revocato",
|
||||
@@ -175,7 +175,7 @@
|
||||
"currently viewing": "attualmente visualizzato",
|
||||
"previously edited": "precedentemente modificato",
|
||||
"You": "Tu",
|
||||
"Avatar of {{ name }}": "Avatar di {{ name }}",
|
||||
"Avatar of {{ name }}": "Avatar of {{ name }}",
|
||||
"Viewers": "Visitatori",
|
||||
"Collections are used to group documents and choose permissions": "Le raccolte sono usate per raggruppare i documenti e assegnare i permessi",
|
||||
"Name": "Nome",
|
||||
@@ -206,14 +206,14 @@
|
||||
"Move document": "Sposta il documento",
|
||||
"Moving": "Spostamento",
|
||||
"Moving the document <em>{{ title }}</em> to the {{ newCollectionName }} collection will change permission for all workspace members from <em>{{ prevPermission }}</em> to <em>{{ newPermission }}</em>.": "Spostare il documento <em>{{ title }}</em> alla raccolta {{ newCollectionName }} cambierà i permessi per tutti i membri dello spazio di lavoro da <em>{{ prevPermission }}</em> a <em>{{ newPermission }}</em>.",
|
||||
"More options": "Più opzioni",
|
||||
"More options": "More options",
|
||||
"Submenu": "Sottomenu",
|
||||
"Collections could not be loaded, please reload the app": "Impossibile caricare le raccolte, per favore ricarica l'app",
|
||||
"Start view": "Schermata iniziale",
|
||||
"Install now": "Installa ora",
|
||||
"Disconnect": "Disconnetti",
|
||||
"Disconnecting": "Disconnessione in corso",
|
||||
"Are you sure you want to disconnect the <em>{{ service }}</em> integration?": "Sei sicuro di voler disconnettere l'integrazione con <em>{{ service }}</em>?",
|
||||
"Disconnecting": "Disconnecting",
|
||||
"Are you sure you want to disconnect the <em>{{ service }}</em> integration?": "Are you sure you want to disconnect the <em>{{ service }}</em> integration?",
|
||||
"This will stop sending analytics events to the configured instance.": "This will stop sending analytics events to the configured instance.",
|
||||
"Deleted Collection": "Raccolte Eliminate",
|
||||
"Untitled": "Senza titolo",
|
||||
@@ -260,10 +260,10 @@
|
||||
"Sorry, part of the application failed to load. This may be because it was updated since you opened the tab or because of a failed network request. Please try reloading.": "Spiacenti, parte dell'applicazione non si è caricata correttamente. È possibile che sia stata aggiornata da quando hai aperto la scheda oppure è fallita una richiesta di rete. Si prega di ricaricare la pagina.",
|
||||
"Reload": "Ricaricare",
|
||||
"Something Unexpected Happened": "È successo qualcosa di imprevisto",
|
||||
"An error has occurred multiple times recently. If it continues please try clearing the cache or using a different browser.": "Di recente un errore è stato riscontrato più volte. Se persiste prova a pulire la cache o ad utilizzare un browser differente.",
|
||||
"An error has occurred multiple times recently. If it continues please try clearing the cache or using a different browser.": "An error has occurred multiple times recently. If it continues please try clearing the cache or using a different browser.",
|
||||
"Sorry, an unrecoverable error occurred{{notified}}. Please try reloading the page, it may have been a temporary glitch.": "Spiacenti, si è verificato un errore irreversibile{{notified}}. Prova a ricaricare la pagina, potrebbe essere un problema temporaneo.",
|
||||
"our engineers have been notified": "i nostri ingegneri sono stati informati",
|
||||
"Clear cache + reload": "Pulisci la cache e aggiorna",
|
||||
"Clear cache + reload": "Clear cache + reload",
|
||||
"Show detail": "Mostra dettagli",
|
||||
"{{userName}} archived": "{{userName}} archiviato",
|
||||
"{{userName}} restored": "{{userName}} recuperato",
|
||||
@@ -284,9 +284,9 @@
|
||||
"You will receive an email when it's complete.": "Riceverai un'email quando sarà completata.",
|
||||
"Include attachments": "Includi allegati",
|
||||
"Including uploaded images and files in the exported data": "Includere immagini e file caricati nei dati esportati",
|
||||
"{{count}} more user": "{{count}} utente ancora",
|
||||
"{{count}} more user_plural": "{{count}} utenti ancora",
|
||||
"Filter options": "Opzioni di filtro",
|
||||
"{{count}} more user": "{{count}} more user",
|
||||
"{{count}} more user_plural": "{{count}} more users",
|
||||
"Filter options": "Filter options",
|
||||
"Filter": "Filtro",
|
||||
"No results": "Nessun risultato",
|
||||
"{{authorName}} created <3></3>": "Creato\n\n\n\n\n\n\n\n",
|
||||
@@ -319,11 +319,11 @@
|
||||
"Change Language": "Cambia Lingua",
|
||||
"Dismiss": "Chiudi",
|
||||
"Lightbox": "Lightbox",
|
||||
"View, navigate, or download images in the document": "Visualizza, naviga, o scarica immagini nel documento",
|
||||
"View, navigate, or download images in the document": "View, navigate, or download images in the document",
|
||||
"Close": "Chiudi",
|
||||
"Previous": "Precedente",
|
||||
"Next": "Successivo",
|
||||
"Image failed to load": "Il caricamento dell'immagine è fallito",
|
||||
"Previous": "Previous",
|
||||
"Next": "Next",
|
||||
"Image failed to load": "Image failed to load",
|
||||
"You’re offline.": "Sei offline.",
|
||||
"Sorry, an error occurred.": "Spiacenti, si è verificato un errore.",
|
||||
"Click to retry": "Clicca per riprovare",
|
||||
@@ -332,7 +332,7 @@
|
||||
"Mark all as read": "Contrassegna tutto come letto",
|
||||
"You're all caught up": "Sei completamente aggiornato",
|
||||
"Icon": "Icona",
|
||||
"OAuth client icon": "Icona client OAuth",
|
||||
"OAuth client icon": "OAuth client icon",
|
||||
"My App": "La mia App",
|
||||
"Tagline": "Slogan",
|
||||
"A short description": "Una breve descrizione",
|
||||
@@ -367,7 +367,7 @@
|
||||
"Disable this setting to discourage search engines from indexing the page": "Disabilita questa impostazione per scoraggiare i motori di ricerca dall'indicizzare la pagina",
|
||||
"Show last modified": "Mostra l'ultima modifica",
|
||||
"Display the last modified timestamp on the shared page": "Mostra la data dell'ultima modifica nella pagina condivisa",
|
||||
"All documents in this collection will be shared on the web, including any new documents added later": "Tutti i documenti in questa raccolta saranno condivisi sul web, inclusi eventuali nuovi documenti aggiunti successivamente",
|
||||
"All documents in this collection will be shared on the web, including any new documents added later": "All documents in this collection will be shared on the web, including any new documents added later",
|
||||
"Invite": "Invita",
|
||||
"{{ userName }} was added to the collection": "{{ userName }} stato aggiunto alla raccolta",
|
||||
"{{ count }} people added to the collection": "Persone aggiunte alla collezione",
|
||||
@@ -409,15 +409,15 @@
|
||||
"{{ count }} groups added to the document": "Gruppi aggiunti al documento",
|
||||
"{{ count }} groups added to the document_plural": "Gruppi aggiunti al documento",
|
||||
"Logo": "Logo",
|
||||
"Expand sidebar": "Espandi barra laterale",
|
||||
"Collapse sidebar": "Nascondi barra laterale",
|
||||
"Expand sidebar": "Expand sidebar",
|
||||
"Collapse sidebar": "Collapse sidebar",
|
||||
"Archived collections": "Collezioni archiviate",
|
||||
"New doc": "Nuovo documento",
|
||||
"Empty": "Vuoto",
|
||||
"Collapse": "Raggruppa",
|
||||
"Expand": "Espandi",
|
||||
"Document not supported – try Markdown, Plain text, HTML, or Word": "Documento non supportato – prova Markdown, testo semplice, HTML o Word",
|
||||
"Import files": "Importa file",
|
||||
"Import files": "Import files",
|
||||
"Go back": "Torna indietro",
|
||||
"Go forward": "Vai avanti",
|
||||
"Could not load shared documents": "Impossibile caricare i documenti condivisi",
|
||||
@@ -456,7 +456,7 @@
|
||||
"New email": "Nuova email",
|
||||
"Email can't be empty": "L'email non può essere vuota",
|
||||
"Your import completed": "Importazione completata",
|
||||
"Sorry, invalid embed link": "Link d'incorporamento non valido",
|
||||
"Sorry, invalid embed link": "Sorry, invalid embed link",
|
||||
"Previous match": "Pagina precedente",
|
||||
"Next match": "Prossima Partita",
|
||||
"Find and replace": "Trova e sostituisci",
|
||||
@@ -467,22 +467,20 @@
|
||||
"Replacement": "Sostituire",
|
||||
"Replace": "Sostituisci",
|
||||
"Replace all": "Sostituisci tutti",
|
||||
"Image width": "Larghezza immagine",
|
||||
"Width": "Largezza",
|
||||
"Image height": "Altezza immagine",
|
||||
"Height": "Altezza",
|
||||
"Image width": "Image width",
|
||||
"Image height": "Image height",
|
||||
"Profile picture": "Immagine del profilo",
|
||||
"Create a new doc": "Crea un nuovo documento",
|
||||
"{{ userName }} won't be notified, as they do not have access to this document": "{{ userName }} Non verrà notificato, in quanto Non",
|
||||
"Keep as link": "Mantieni come link",
|
||||
"Mention": "Menzione",
|
||||
"Embed": "Incorpora",
|
||||
"Insert after": "Inserisci dopo",
|
||||
"Insert before": "Inserisci prima",
|
||||
"Move up": "Sposta su",
|
||||
"Move down": "Sposta giù",
|
||||
"Move left": "Sposta a sinistra",
|
||||
"Move right": "Sposta a destra",
|
||||
"Insert after": "Insert after",
|
||||
"Insert before": "Insert before",
|
||||
"Move up": "Move up",
|
||||
"Move down": "Move down",
|
||||
"Move left": "Move left",
|
||||
"Move right": "Move right",
|
||||
"Align center": "Allinea al centro",
|
||||
"Align left": "Allinea a sinistra",
|
||||
"Align right": "Allinea a destra",
|
||||
@@ -499,6 +497,8 @@
|
||||
"Create a new child doc": "Crea un nuovo documento",
|
||||
"Delete table": "Elimina tabella",
|
||||
"Delete file": "Cancella file",
|
||||
"Width": "Width",
|
||||
"Height": "Height",
|
||||
"Download file": "Scarica il file",
|
||||
"Replace file": "Sostituisci file",
|
||||
"Delete image": "Elimina immagine",
|
||||
@@ -559,7 +559,7 @@
|
||||
"Outdent": "Riduci rientro",
|
||||
"Video": "Video",
|
||||
"None": "Nessuno",
|
||||
"Delete embed": "Elimina incorporamento",
|
||||
"Delete embed": "Delete embed",
|
||||
"Rename": "Rinomina",
|
||||
"Could not import file": "Impossibile importare il file",
|
||||
"Unsubscribed from document": "Annullata l'iscrizione al documento",
|
||||
@@ -576,7 +576,7 @@
|
||||
"Import": "Importa",
|
||||
"Install": "Installa",
|
||||
"Integrations": "Integrazioni",
|
||||
"API key": "Chiave API",
|
||||
"API key": "API key",
|
||||
"Show path to document": "Mostra percorso del documento",
|
||||
"Sort in sidebar": "Ordina nella barra laterale",
|
||||
"A-Z sort": "Ordina da A-Z",
|
||||
@@ -622,7 +622,7 @@
|
||||
"mentioned you in": "Ti ha menzionato in",
|
||||
"left a comment on": "Lasciato un commento",
|
||||
"resolved a comment on": "Risolto un commento",
|
||||
"reacted {{ emoji }} to your comment on": "ha reagito con {{ emoji }} al tuo commento su",
|
||||
"reacted {{ emoji }} to your comment on": "reacted {{ emoji }} to your comment on",
|
||||
"shared": "condiviso",
|
||||
"invited you to": "Ti ha invitato a",
|
||||
"Choose a date": "Seleziona una data",
|
||||
@@ -635,7 +635,7 @@
|
||||
"30 days": "30 giorni",
|
||||
"60 days": "60 giorni",
|
||||
"90 days": "90 giorni",
|
||||
"Custom": "Personalizzato",
|
||||
"Custom": "Custom",
|
||||
"No expiration": "Nessuna scadenza",
|
||||
"The document archive is empty at the moment.": "L'archivio documenti è vuoto al momento.",
|
||||
"Drop documents to import": "Trascina qui i documenti oppure",
|
||||
@@ -664,14 +664,14 @@
|
||||
"Reply": "Rispondi",
|
||||
"Post": "Pubblica",
|
||||
"Upload image": "Carica immagine",
|
||||
"No resolved comments": "Nessun commento risolto",
|
||||
"No resolved comments": "No resolved comments",
|
||||
"No comments yet": "Nessun commento",
|
||||
"New comments": "Nuovi commenti",
|
||||
"Most recent": "Più recente",
|
||||
"Order in doc": "Ordine nel doc",
|
||||
"Order in doc": "Order in doc",
|
||||
"Resolved": "Risolto",
|
||||
"Sort comments": "Ordina commenti",
|
||||
"Show {{ count }} reply": "Mostra {{ count }} risposte",
|
||||
"Show {{ count }} reply": "Show {{ count }} reply",
|
||||
"Show {{ count }} reply_plural": "Mostra {{ count }} risposte",
|
||||
"Error updating comment": "Errore durante l'aggiornamento del commento",
|
||||
"Document is too large": "Il documento è troppo grande",
|
||||
@@ -692,7 +692,7 @@
|
||||
"only you": "solo tu",
|
||||
"person": "persona",
|
||||
"people": "persone",
|
||||
"Document title": "Titolo del documento",
|
||||
"Document title": "Document title",
|
||||
"Last updated": "Ultimo aggiornamento",
|
||||
"Type '/' to insert, or start writing…": "Digita '/' per inserire, o inizia a scrivere…",
|
||||
"Hide contents": "Nascondi contenuti",
|
||||
@@ -706,26 +706,26 @@
|
||||
"No history yet": "Nessuna cronologia",
|
||||
"Source": "Origine",
|
||||
"Created": "Creato",
|
||||
"Imported from {{ source }}": "Importato da {{ source }}",
|
||||
"Imported from {{ source }}": "Imported from {{ source }}",
|
||||
"Stats": "Statistiche",
|
||||
"{{ number }} minute read": "{{ number }} minuto di lettura",
|
||||
"{{ number }} words": "{{ number }} parola",
|
||||
"{{ number }} words_plural": "{{ number }} parole",
|
||||
"{{ number }} characters": "{{ number }} carattere",
|
||||
"{{ number }} characters_plural": "{{ number }} caratteri",
|
||||
"{{ number }} minute read": "{{ number }} minute read",
|
||||
"{{ number }} words": "{{ number }} word",
|
||||
"{{ number }} words_plural": "{{ number }} words",
|
||||
"{{ number }} characters": "{{ number }} character",
|
||||
"{{ number }} characters_plural": "{{ number }} characters",
|
||||
"{{ number }} emoji": "{{ number }} emoji",
|
||||
"No text selected": "Nessun testo selezionato",
|
||||
"{{ number }} words selected": "{{ number }} parola selezionata",
|
||||
"{{ number }} words selected_plural": "{{ number }} parole selezionate",
|
||||
"{{ number }} characters selected": "{{ number }} carattere selezionato",
|
||||
"{{ number }} characters selected_plural": "{{ number }} caratteri selezionati",
|
||||
"{{ number }} words selected": "{{ number }} word selected",
|
||||
"{{ number }} words selected_plural": "{{ number }} words selected",
|
||||
"{{ number }} characters selected": "{{ number }} character selected",
|
||||
"{{ number }} characters selected_plural": "{{ number }} characters selected",
|
||||
"Contributors": "Contributori",
|
||||
"Creator": "Autore",
|
||||
"Last edited": "Ultima modifica",
|
||||
"Previously edited": "Modificato precedentemente",
|
||||
"Sorry, the last change could not be persisted – please reload the page": "Spiacenti, non è stato possibile mantenere l'ultima modifica. Si prega di ricaricare la pagina",
|
||||
"{{ count }} days": "{{ count }} giorno",
|
||||
"{{ count }} days_plural": "{{ count }} giorni",
|
||||
"{{ count }} days": "{{ count }} day",
|
||||
"{{ count }} days_plural": "{{ count }} days",
|
||||
"This template will be permanently deleted in <2></2> unless restored.": "Questo modello verrà eliminato definitivamente in <2></2> salvo ripristino.",
|
||||
"This document will be permanently deleted in <2></2> unless restored.": "Questo documento verrà eliminato definitivamente tra <2></2> salvo ripristino.",
|
||||
"Highlight some text and use the <1></1> control to add placeholders that can be filled out when creating new documents": "Evidenzia del testo e usa il comando <1></1> per aggiungere dei segnaposto che possono essere compilati nella creazione di nuovi documenti",
|
||||
@@ -733,7 +733,7 @@
|
||||
"Deleted by {{userName}}": "Eliminato da {{userName}}",
|
||||
"Observing {{ userName }}": "Osservazione di {{ userName }}",
|
||||
"Backlinks": "Backlink",
|
||||
"This document is large which may affect performance": "Questo documento è grande, potrebbe influenzare le prestazioni",
|
||||
"This document is large which may affect performance": "This document is large which may affect performance",
|
||||
"Are you sure you want to delete the <em>{{ documentTitle }}</em> template?": "Sei sicuro di voler eliminare il template <em>{{ documentTitle }}</em>?",
|
||||
"Are you sure about that? Deleting the <em>{{ documentTitle }}</em> document will delete all of its history</em>.": "Sei sicuro? L'eliminazione del documento <em>{{ documentTitle }}</em> cancellerà tutta la sua cronologia</em>.",
|
||||
"Are you sure about that? Deleting the <em>{{ documentTitle }}</em> document will delete all of its history and <em>{{ any }} nested document</em>.": "Sei sicuro? L'eliminazione del documento <em>{{ documentTitle }}</em> cancellerà tutta la sua cronologia e <em>{{ any }} documenti annidati</em>.",
|
||||
@@ -753,19 +753,19 @@
|
||||
"Search documents": "Cerca documenti",
|
||||
"No documents found for your filters.": "Nessun documento trovato per i tuoi filtri.",
|
||||
"You’ve not got any drafts at the moment.": "Al momento non hai bozze.",
|
||||
"Payment Required": "Pagamento Richiesto",
|
||||
"No access to this doc": "Non hai accesso a questo documento",
|
||||
"It doesn’t look like you have permission to access this document.": "Sembra che tu non abbia i permessi per accedere a questo documento",
|
||||
"Please request access from the document owner.": "Richiedi l'accesso al proprietario del documento",
|
||||
"Not found": "Non trovato",
|
||||
"The page you’re looking for cannot be found. It might have been deleted or the link is incorrect.": "La pagina che stai cercando non è stata trovata. Potrebbe essere stata eliminata oppure il link non è corretto.",
|
||||
"Payment Required": "Payment Required",
|
||||
"No access to this doc": "No access to this doc",
|
||||
"It doesn’t look like you have permission to access this document.": "It doesn’t look like you have permission to access this document.",
|
||||
"Please request access from the document owner.": "Please request access from the document owner.",
|
||||
"Not found": "Not found",
|
||||
"The page you’re looking for cannot be found. It might have been deleted or the link is incorrect.": "The page you’re looking for cannot be found. It might have been deleted or the link is incorrect.",
|
||||
"Offline": "Non in linea",
|
||||
"We were unable to load the document while offline.": "Impossibile caricare il documento offline.",
|
||||
"Your account has been suspended": "Il tuo account è stato sospeso",
|
||||
"Warning Sign": "Simbolo di avvertenza",
|
||||
"A workspace admin (<em>{{ suspendedContactEmail }}</em>) has suspended your account. To re-activate your account, please reach out to them directly.": "Un amministratore dello spazio di lavoro (<em>{{ suspendedContactEmail }}</em>) ha sospeso il tuo account. Contattalo per farlo riattivare.",
|
||||
"Something went wrong": "Qualcosa è andato storto",
|
||||
"Sorry, an unknown error occurred loading the page. Please try again or contact support if the issue persists.": "Si è verificato un errore sconosciuto durante il caricamento della pagina. Riprova o contatta il supporto se il problema persiste.",
|
||||
"A workspace admin (<em>{{ suspendedContactEmail }}</em>) has suspended your account. To re-activate your account, please reach out to them directly.": "A workspace admin (<em>{{ suspendedContactEmail }}</em>) has suspended your account. To re-activate your account, please reach out to them directly.",
|
||||
"Something went wrong": "Something went wrong",
|
||||
"Sorry, an unknown error occurred loading the page. Please try again or contact support if the issue persists.": "Sorry, an unknown error occurred loading the page. Please try again or contact support if the issue persists.",
|
||||
"Created by me": "Creato da me",
|
||||
"Weird, this shouldn’t ever be empty": "Strano, questo non dovrebbe mai essere vuoto",
|
||||
"You haven’t created any documents yet": "Non hai ancora creato alcun documento",
|
||||
@@ -774,21 +774,21 @@
|
||||
"Those email addresses are already invited": "Agli indirizzi email è stato già inviato un invito",
|
||||
"Sorry, you can only send {{MAX_INVITES}} invites at a time": "Spiacenti, puoi inviare solo {{MAX_INVITES}} inviti alla volta",
|
||||
"Invited {{roleName}} will receive access to": "Invited {{roleName}} will receive access to",
|
||||
"{{collectionCount}} collections": "{{collectionCount}} raccolte",
|
||||
"{{collectionCount}} collections": "{{collectionCount}} collections",
|
||||
"Admin": "Amministratore",
|
||||
"Can manage all workspace settings": "Può gestire tutte le impostazioni dello spazio di lavoro",
|
||||
"Can create, edit, and delete documents": "Può creare, modificare ed eliminare documenti",
|
||||
"Can view and comment": "Può vedere e commentare",
|
||||
"Invite people to join your workspace. They can sign in with {{signinMethods}} or use their email address.": "Invita persone ad entrare nel tuo spazio di lavoro. Possono registrarsi con {{signinMethods}} oppure usando il proprio indirizzo email.",
|
||||
"Can manage all workspace settings": "Can manage all workspace settings",
|
||||
"Can create, edit, and delete documents": "Can create, edit, and delete documents",
|
||||
"Can view and comment": "Can view and comment",
|
||||
"Invite people to join your workspace. They can sign in with {{signinMethods}} or use their email address.": "Invite people to join your workspace. They can sign in with {{signinMethods}} or use their email address.",
|
||||
"Invite members to join your workspace. They will need to sign in with {{signinMethods}}.": "Invita membri a unirsi alla tua area di lavoro. Dovranno accedere con {{signinMethods}} o utilizzare il loro indirizzo email.",
|
||||
"As an admin you can also <2>enable email sign-in</2>.": "Come amministratore puoi anche <2>abilitare l'autenticazione via email</2>.",
|
||||
"Invite as": "Invita come",
|
||||
"Invite as": "Invite as",
|
||||
"Email": "Email",
|
||||
"Add another": "Aggiungi ancora",
|
||||
"Inviting": "Sto invitando",
|
||||
"Send Invites": "Spedisci gli inviti",
|
||||
"Open command menu": "Apri menu dei comandi",
|
||||
"Forward": "Inoltro",
|
||||
"Forward": "Forward",
|
||||
"Edit current document": "Modifica il documento corrente",
|
||||
"Move current document": "Sposta il documento corrente",
|
||||
"Open document history": "Apri cronologia del documento",
|
||||
@@ -796,11 +796,11 @@
|
||||
"Jump to home": "Vai alla home",
|
||||
"Focus search input": "Focus input di ricerca",
|
||||
"Open this guide": "Apri questa guida",
|
||||
"Enter": "Invio",
|
||||
"Enter": "Enter",
|
||||
"Publish document and exit": "Pubblica documento ed esci",
|
||||
"Save document": "Salva il documento",
|
||||
"Cancel editing": "Annulla modifica",
|
||||
"Collaboration": "Collaborazione",
|
||||
"Collaboration": "Collaboration",
|
||||
"Formatting": "Formattazione",
|
||||
"Paragraph": "Paragrafo",
|
||||
"Large header": "Intestazione grande",
|
||||
@@ -809,20 +809,20 @@
|
||||
"Underline": "Sottolineato",
|
||||
"Undo": "Annulla",
|
||||
"Redo": "Ripristina",
|
||||
"Move block up": "Sposta blocco in alto",
|
||||
"Move block down": "Sposta blocco in basso",
|
||||
"Move block up": "Move block up",
|
||||
"Move block down": "Move block down",
|
||||
"Lists": "Elenchi",
|
||||
"Toggle task list item": "Attiva/Disattiva elemento da un elenco di attività",
|
||||
"Tab": "Scheda",
|
||||
"Toggle task list item": "Toggle task list item",
|
||||
"Tab": "Tab",
|
||||
"Indent list item": "Aumenta rientro",
|
||||
"Outdent list item": "Riduci rientro",
|
||||
"Move list item up": "Sposta elemento su",
|
||||
"Move list item down": "Sposta elemento giù",
|
||||
"Tables": "Tabelle",
|
||||
"Insert row": "Inserisci riga",
|
||||
"Next cell": "Cella successiva",
|
||||
"Previous cell": "Cella precedente",
|
||||
"Space": "Spazio",
|
||||
"Tables": "Tables",
|
||||
"Insert row": "Insert row",
|
||||
"Next cell": "Next cell",
|
||||
"Previous cell": "Previous cell",
|
||||
"Space": "Space",
|
||||
"Numbered list": "Elenco numerato",
|
||||
"Blockquote": "Citazione",
|
||||
"Horizontal divider": "Separatore orizzontale",
|
||||
@@ -830,18 +830,18 @@
|
||||
"Inline code": "Codice inline",
|
||||
"Inline LaTeX": "LaTeX in linea",
|
||||
"Triggers": "Triggers",
|
||||
"Mention users and more": "Menziona utenti e altro ancora",
|
||||
"Mention users and more": "Mention users and more",
|
||||
"Emoji": "Emoji",
|
||||
"Insert block": "Inserisci blocco",
|
||||
"Insert block": "Insert block",
|
||||
"Sign In": "Accedi",
|
||||
"Continue with Email": "Continua con email",
|
||||
"Continue with {{ authProviderName }}": "Continua con {{ authProviderName }}",
|
||||
"Back to home": "Torna alla home",
|
||||
"The workspace could not be found": "Non è stato possibile trovare lo spazio di lavoro",
|
||||
"To continue, enter your workspace’s subdomain.": "Per continuare, inserisci il sottodominio del tuo spazio di lavoro.",
|
||||
"subdomain": "sottodominio",
|
||||
"The workspace could not be found": "The workspace could not be found",
|
||||
"To continue, enter your workspace’s subdomain.": "To continue, enter your workspace’s subdomain.",
|
||||
"subdomain": "subdomain",
|
||||
"Continue": "Continua",
|
||||
"Sorry, the code you entered is invalid or has expired.": "Il codice che hai inserito non è valido o è scaduto.",
|
||||
"Sorry, the code you entered is invalid or has expired.": "Sorry, the code you entered is invalid or has expired.",
|
||||
"The domain associated with your email address has not been allowed for this workspace.": "Il dominio associato al tuo indirizzo email non è permesso in questa area di lavoro.",
|
||||
"Unable to sign-in. Please navigate to your workspace's custom URL, then try to sign-in again.<1></1>If you were invited to a workspace, you will find a link to it in the invite email.": "Impossibile effettuare l'accesso. Per favore vai all'URL personalizzato della tua area di lavoro, quindi prova di nuovo ad accedere.<1></1>Se sei stato invitato in un'area di lavoro, troverai un link di accesso nella mail d'invito.",
|
||||
"Sorry, a new account cannot be created with a personal Gmail address.<1></1>Please use a Google Workspaces account instead.": "Spiacenti, non è possibile creare un nuovo account con un indirizzo Gmail personale.<1></1>Utilizza invece un account Google Workspaces.",
|
||||
@@ -906,7 +906,7 @@
|
||||
"reactions": "reactions",
|
||||
"pins": "pins",
|
||||
"shares": "shares",
|
||||
"users": "utenti",
|
||||
"users": "users",
|
||||
"teams": "teams",
|
||||
"workspace": "workspace",
|
||||
"Read all data": "Read all data",
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
"currently viewing": "현재 보는 중",
|
||||
"previously edited": "이전에 수정됨",
|
||||
"You": "본인",
|
||||
"Avatar of {{ name }}": "{{ name }}의 아바타",
|
||||
"Avatar of {{ name }}": "Avatar of {{ name }}",
|
||||
"Viewers": "열람자",
|
||||
"Collections are used to group documents and choose permissions": "컬렉션은 문서를 그룹화하고 권한을 지정하는 데 사용됩니다",
|
||||
"Name": "이름",
|
||||
@@ -206,7 +206,7 @@
|
||||
"Move document": "문서 이동",
|
||||
"Moving": "이동 중",
|
||||
"Moving the document <em>{{ title }}</em> to the {{ newCollectionName }} collection will change permission for all workspace members from <em>{{ prevPermission }}</em> to <em>{{ newPermission }}</em>.": "문서 <em>{{ title }}</em>을(를) {{ newCollectionName }} 컬렉션으로 이동하면 모든 작업 공간 멤버의 권한이 <em>{{ prevPermission }}</em>에서 <em>{{ newPermission }}</em>으로 변경됩니다.",
|
||||
"More options": "더 많은 옵션",
|
||||
"More options": "More options",
|
||||
"Submenu": "하위 메뉴",
|
||||
"Collections could not be loaded, please reload the app": "컬렉션을 불러올 수 없습니다. 앱을 새로고침하세요",
|
||||
"Start view": "시작 화면",
|
||||
@@ -286,7 +286,7 @@
|
||||
"Including uploaded images and files in the exported data": "내보낼 데이터에 업로드된 이미지 및 파일 포함",
|
||||
"{{count}} more user": "{{count}}명의 추가 사용자",
|
||||
"{{count}} more user_plural": "{{count}}명의 추가 사용자",
|
||||
"Filter options": "필터 옵션",
|
||||
"Filter options": "Filter options",
|
||||
"Filter": "필터",
|
||||
"No results": "결과 없음",
|
||||
"{{authorName}} created <3></3>": "{{authorName }} 이(가) <3></3> 생성",
|
||||
@@ -318,12 +318,12 @@
|
||||
"Permission": "권한",
|
||||
"Change Language": "언어 변경",
|
||||
"Dismiss": "닫기",
|
||||
"Lightbox": "라이트박스",
|
||||
"View, navigate, or download images in the document": "문서에서 이미지 보기, 탐색 또는 다운로드",
|
||||
"Lightbox": "Lightbox",
|
||||
"View, navigate, or download images in the document": "View, navigate, or download images in the document",
|
||||
"Close": "닫기",
|
||||
"Previous": "이전",
|
||||
"Next": "다음",
|
||||
"Image failed to load": "이미지를 로드하는 데 실패함",
|
||||
"Previous": "Previous",
|
||||
"Next": "Next",
|
||||
"Image failed to load": "Image failed to load",
|
||||
"You’re offline.": "오프라인 상태입니다.",
|
||||
"Sorry, an error occurred.": "죄송합니다. 오류가 발생했습니다.",
|
||||
"Click to retry": "클릭하여 재시도하기",
|
||||
@@ -332,7 +332,7 @@
|
||||
"Mark all as read": "모두 읽은 상태로 표시",
|
||||
"You're all caught up": "모두 확인함",
|
||||
"Icon": "아이콘",
|
||||
"OAuth client icon": "OAuth 클라이언트 아이콘",
|
||||
"OAuth client icon": "OAuth client icon",
|
||||
"My App": "내 앱",
|
||||
"Tagline": "태그라인",
|
||||
"A short description": "간단한 설명",
|
||||
@@ -409,15 +409,15 @@
|
||||
"{{ count }} groups added to the document": "{{ count }} 개의 그룹이 문서에 추가됨",
|
||||
"{{ count }} groups added to the document_plural": "{{ count }} 개의 그룹이 문서에 추가됨",
|
||||
"Logo": "로고",
|
||||
"Expand sidebar": "사이드바 펼치기",
|
||||
"Collapse sidebar": "사이드바 축소",
|
||||
"Expand sidebar": "Expand sidebar",
|
||||
"Collapse sidebar": "Collapse sidebar",
|
||||
"Archived collections": "보관된 컬렉션",
|
||||
"New doc": "새 문서",
|
||||
"Empty": "비어 있음",
|
||||
"Collapse": "감추기",
|
||||
"Expand": "펼치기",
|
||||
"Document not supported – try Markdown, Plain text, HTML, or Word": "이 문서는 지원되지 않습니다 – Markdown, Plain Text, HTML이나 Word를 이용해주세요",
|
||||
"Import files": "파일 가져오기",
|
||||
"Import files": "Import files",
|
||||
"Go back": "돌아가기",
|
||||
"Go forward": "앞으로 가기",
|
||||
"Could not load shared documents": "공유 문서를 불러올 수 없습니다.",
|
||||
@@ -467,22 +467,20 @@
|
||||
"Replacement": "대체",
|
||||
"Replace": "바꾸기",
|
||||
"Replace all": "모두 바꾸기",
|
||||
"Image width": "이미지 너비",
|
||||
"Width": "너비",
|
||||
"Image height": "이미지 높이",
|
||||
"Height": "높이",
|
||||
"Image width": "Image width",
|
||||
"Image height": "Image height",
|
||||
"Profile picture": "프로필 사진",
|
||||
"Create a new doc": "새 문서 만들기",
|
||||
"{{ userName }} won't be notified, as they do not have access to this document": "{{ userName }} 은(는) 이 문서에 대한 액세스 권한이 없으므로 알림을 받지 않습니다.",
|
||||
"Keep as link": "링크로 유지",
|
||||
"Mention": "언급됨",
|
||||
"Embed": "내장",
|
||||
"Insert after": "다음에 삽입",
|
||||
"Insert before": "이전에 삽입",
|
||||
"Move up": "위로 이동",
|
||||
"Move down": "아래로 이동",
|
||||
"Move left": "왼쪽으로 이동",
|
||||
"Move right": "오른쪽으로 이동",
|
||||
"Insert after": "Insert after",
|
||||
"Insert before": "Insert before",
|
||||
"Move up": "Move up",
|
||||
"Move down": "Move down",
|
||||
"Move left": "Move left",
|
||||
"Move right": "Move right",
|
||||
"Align center": "가운데 정렬",
|
||||
"Align left": "왼쪽 정렬",
|
||||
"Align right": "오른쪽 정렬",
|
||||
@@ -499,6 +497,8 @@
|
||||
"Create a new child doc": "새 하위 문서 생성",
|
||||
"Delete table": "테이블 삭제",
|
||||
"Delete file": "파일 삭제",
|
||||
"Width": "Width",
|
||||
"Height": "Height",
|
||||
"Download file": "파일 다운로드",
|
||||
"Replace file": "파일 바꾸기",
|
||||
"Delete image": "이미지 삭제",
|
||||
@@ -692,7 +692,7 @@
|
||||
"only you": "나만",
|
||||
"person": "개인",
|
||||
"people": "명",
|
||||
"Document title": "문서 제목",
|
||||
"Document title": "Document title",
|
||||
"Last updated": "마지막 업데이트",
|
||||
"Type '/' to insert, or start writing…": "'/'를 입력하여 삽입하거나 쓰기 시작...",
|
||||
"Hide contents": "내용 숨기기",
|
||||
@@ -937,7 +937,7 @@
|
||||
"Rotate secret": "시크릿 교체",
|
||||
"Rotating the client secret will invalidate the current secret. Make sure to update any applications using these credentials.": "클라이언트 시크릿을 교체하면 현재 시크릿이 무효화됩니다. 이 자격 증명을 사용하는 모든 애플리케이션을 업데이트해야 합니다.",
|
||||
"Displayed to users when authorizing": "사용자가 권한을 부여할 때 표시됨",
|
||||
"Application icon": "애플리케이션 아이콘",
|
||||
"Application icon": "Application icon",
|
||||
"Developer information shown to users when authorizing": "사용자가 권한을 부여할 때 표시되는 개발자 정보",
|
||||
"Developer name": "개발자 이름",
|
||||
"Developer URL": "개발자 URL",
|
||||
@@ -1001,7 +1001,7 @@
|
||||
"Search people": "사용자 검색",
|
||||
"No people matching your search": "찾으시는 사용자가 없습니다.",
|
||||
"No people left to add": "추가할 사용자가 없습니다",
|
||||
"Group admin": "그룹 관리",
|
||||
"Group admin": "Group admin",
|
||||
"Member": "멤버",
|
||||
"Admins": "관리자",
|
||||
"Date created": "생성 일자",
|
||||
@@ -1045,7 +1045,7 @@
|
||||
"These settings affect the way that your workspace appears to everyone on the team.": "이러한 설정은 팀의 모든 사람에게 워크스페이스가 표시되는 방식에 영향을 줍니다.",
|
||||
"Display": "표시",
|
||||
"The logo is displayed at the top left of the application.": "로고는 애플리케이션의 왼쪽 상단에 표시됩니다.",
|
||||
"Workspace logo": "워크스페이스 로고",
|
||||
"Workspace logo": "Workspace logo",
|
||||
"The workspace name, usually the same as your company name.": "워크스페이스 이름은 일반적으로 회사 이름과 동일합니다.",
|
||||
"Description": "설명",
|
||||
"A short description of your workspace.": "워크스페이스에 대한 간략한 설명입니다.",
|
||||
@@ -1276,7 +1276,7 @@
|
||||
"{{ user }} updated {{ timeAgo }}": "{{ user }} 업데이트 됨 {{ timeAgo }}",
|
||||
"You created {{ timeAgo }}": "{{ timeAgo }} 전에 내가 생성함",
|
||||
"{{ user }} created {{ timeAgo }}": "{{ user }} 이(가) {{ timeAgo }} 전에 생성",
|
||||
"Caption": "캡션",
|
||||
"Open": "열기",
|
||||
"Caption": "Caption",
|
||||
"Open": "Open",
|
||||
"Error loading data": "데이터 로딩 오류"
|
||||
}
|
||||
|
||||
@@ -468,21 +468,19 @@
|
||||
"Replace": "Vervang",
|
||||
"Replace all": "Vervang alle",
|
||||
"Image width": "Afbeelding breedte",
|
||||
"Width": "Breedte",
|
||||
"Image height": "Afbeelding hoogte",
|
||||
"Height": "Hoogte",
|
||||
"Profile picture": "Profielfoto",
|
||||
"Create a new doc": "Maak een nieuw document",
|
||||
"{{ userName }} won't be notified, as they do not have access to this document": "{{ userName }} wordt niet verwittigd omdat ze geen toegang hebben tot dit document",
|
||||
"Keep as link": "Behoud als link",
|
||||
"Mention": "Vermelding",
|
||||
"Embed": "Insluiten",
|
||||
"Insert after": "Invoegen achter",
|
||||
"Insert before": "Invoegen voor",
|
||||
"Move up": "Omhoog verplaatsen",
|
||||
"Move down": "Omlaag verplaatsen",
|
||||
"Move left": "Links verplaatsen",
|
||||
"Move right": "Rechts verplaatsen",
|
||||
"Insert after": "Insert after",
|
||||
"Insert before": "Insert before",
|
||||
"Move up": "Move up",
|
||||
"Move down": "Move down",
|
||||
"Move left": "Move left",
|
||||
"Move right": "Move right",
|
||||
"Align center": "Centreer",
|
||||
"Align left": "Links uitlijnen",
|
||||
"Align right": "Rechts uitlijnen",
|
||||
@@ -499,6 +497,8 @@
|
||||
"Create a new child doc": "Maak een nieuw subdocument",
|
||||
"Delete table": "Tabel verwijderen",
|
||||
"Delete file": "Verwijder bestand",
|
||||
"Width": "Breedte",
|
||||
"Height": "Hoogte",
|
||||
"Download file": "Download bestand",
|
||||
"Replace file": "Vervang bestand",
|
||||
"Delete image": "Afbeelding verwijderen",
|
||||
|
||||
@@ -468,21 +468,19 @@
|
||||
"Replace": "替换",
|
||||
"Replace all": "全部替换",
|
||||
"Image width": "图像宽度",
|
||||
"Width": "宽",
|
||||
"Image height": "图像高度",
|
||||
"Height": "高",
|
||||
"Profile picture": "个人头像",
|
||||
"Create a new doc": "新建文档",
|
||||
"{{ userName }} won't be notified, as they do not have access to this document": "{{ userName }} 不会被通知,因为他们没有访问此文档的权限",
|
||||
"Keep as link": "保留为链接",
|
||||
"Mention": "提及",
|
||||
"Embed": "嵌入",
|
||||
"Insert after": "在之后插入",
|
||||
"Insert before": "在之前插入",
|
||||
"Move up": "上移",
|
||||
"Move down": "下移",
|
||||
"Move left": "左移",
|
||||
"Move right": "右移",
|
||||
"Insert after": "Insert after",
|
||||
"Insert before": "Insert before",
|
||||
"Move up": "Move up",
|
||||
"Move down": "Move down",
|
||||
"Move left": "Move left",
|
||||
"Move right": "Move right",
|
||||
"Align center": "居中对齐",
|
||||
"Align left": "左对齐",
|
||||
"Align right": "右对齐",
|
||||
@@ -499,6 +497,8 @@
|
||||
"Create a new child doc": "创建一个新的子文档",
|
||||
"Delete table": "删除表格",
|
||||
"Delete file": "删除文件",
|
||||
"Width": "宽",
|
||||
"Height": "高",
|
||||
"Download file": "下载文件",
|
||||
"Replace file": "替换文件",
|
||||
"Delete image": "删除图片",
|
||||
|
||||
@@ -257,8 +257,6 @@ export type SourceMetadata = {
|
||||
externalName?: string;
|
||||
/** Whether the item was created through a trial license. */
|
||||
trial?: boolean;
|
||||
/** The ID of the original document when this document was duplicated. */
|
||||
originalDocumentId?: string;
|
||||
};
|
||||
|
||||
export type CustomTheme = {
|
||||
@@ -299,8 +297,6 @@ export enum TeamPreference {
|
||||
CustomTheme = "customTheme",
|
||||
/** Side to display the document's table of contents in relation to the main content. */
|
||||
TocPosition = "tocPosition",
|
||||
/** Whether to prevent shared documents from being embedded in iframes on external websites. */
|
||||
PreventDocumentEmbedding = "preventDocumentEmbedding",
|
||||
}
|
||||
|
||||
export type TeamPreferences = {
|
||||
@@ -314,7 +310,6 @@ export type TeamPreferences = {
|
||||
[TeamPreference.Commenting]?: boolean;
|
||||
[TeamPreference.CustomTheme]?: Partial<CustomTheme>;
|
||||
[TeamPreference.TocPosition]?: TOCPosition;
|
||||
[TeamPreference.PreventDocumentEmbedding]?: boolean;
|
||||
};
|
||||
|
||||
export enum NavigationNodeType {
|
||||
|
||||
@@ -109,12 +109,6 @@ import {
|
||||
faHandsClapping,
|
||||
faFolderClosed,
|
||||
faFlaskVial,
|
||||
faCircle,
|
||||
faSquare,
|
||||
faPentagon,
|
||||
faHexagon,
|
||||
faDiamond,
|
||||
faSpiral,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import intersection from "lodash/intersection";
|
||||
@@ -577,12 +571,6 @@ export class IconLibrary {
|
||||
faShopify,
|
||||
faSwift,
|
||||
faSlack,
|
||||
faCircle,
|
||||
faSquare,
|
||||
faPentagon,
|
||||
faHexagon,
|
||||
faDiamond,
|
||||
faSpiral,
|
||||
].map((icon) => [
|
||||
icon.iconName,
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user