mirror of
https://github.com/outline/outline.git
synced 2026-06-13 19:35:02 +03:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 087f5bd1d3 | |||
| 96c41ce823 | |||
| 0702570b0d | |||
| 4b209a7913 | |||
| 6393bd02f4 | |||
| 1776aad833 | |||
| 0c6b37cb60 | |||
| d664044579 | |||
| b3ca434c51 | |||
| 631b75def4 | |||
| d183dab063 | |||
| f082da6456 | |||
| ad72210714 | |||
| 9c85b26d43 | |||
| bf6a56849e | |||
| 68e8b2791a |
@@ -145,7 +145,7 @@ jobs:
|
||||
|
||||
bundle-size:
|
||||
needs: [build, types, changes]
|
||||
if: ${{ needs.changes.outputs.app == 'true' }}
|
||||
if: ${{ needs.changes.outputs.app == 'true' && github.repository == 'outline/outline' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as React from "react";
|
||||
import { Trans } from "react-i18next";
|
||||
import { IssueStatusIcon } from "@shared/components/IssueStatusIcon";
|
||||
import { UnfurlResourceType, UnfurlResponse } from "@shared/types";
|
||||
import { Avatar } from "~/components/Avatar";
|
||||
import Flex from "~/components/Flex";
|
||||
import { IssueStatusIcon } from "../Icons/IssueStatusIcon";
|
||||
import Text from "../Text";
|
||||
import Time from "../Time";
|
||||
import {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as React from "react";
|
||||
import { Trans } from "react-i18next";
|
||||
import { PullRequestIcon } from "@shared/components/PullRequestIcon";
|
||||
import { UnfurlResourceType, UnfurlResponse } from "@shared/types";
|
||||
import { Avatar } from "~/components/Avatar";
|
||||
import Flex from "~/components/Flex";
|
||||
import { PullRequestIcon } from "../Icons/PullRequestIcon";
|
||||
import Text from "../Text";
|
||||
import Time from "../Time";
|
||||
import {
|
||||
|
||||
@@ -114,6 +114,8 @@ const Modal: React.FC<Props> = ({
|
||||
<Small {...props}>
|
||||
<Centered
|
||||
onClick={(ev) => ev.stopPropagation()}
|
||||
// maxHeight needed for proper overflow behavior in Safari
|
||||
style={{ maxHeight: "65vh" }}
|
||||
column
|
||||
reverse
|
||||
>
|
||||
@@ -259,6 +261,7 @@ const Small = styled.div`
|
||||
width: 75vw;
|
||||
min-width: 350px;
|
||||
max-width: 450px;
|
||||
max-height: 65vh;
|
||||
z-index: ${depths.modal};
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
+1
-71
@@ -1,73 +1,3 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import { ellipsis } from "@shared/styles";
|
||||
|
||||
type Props = {
|
||||
/** The type of text to render */
|
||||
type?: "secondary" | "tertiary" | "danger";
|
||||
/** The size of the text */
|
||||
size?: "xlarge" | "large" | "medium" | "small" | "xsmall";
|
||||
/** The direction of the text (defaults to ltr) */
|
||||
dir?: "ltr" | "rtl" | "auto";
|
||||
/** Whether the text should be selectable (defaults to false) */
|
||||
selectable?: boolean;
|
||||
/** The font weight of the text */
|
||||
weight?: "xbold" | "bold" | "normal";
|
||||
/** Whether the text should be italic */
|
||||
italic?: boolean;
|
||||
/** Whether the text should be truncated with an ellipsis */
|
||||
ellipsis?: boolean;
|
||||
/** Whether the text should be monospaced */
|
||||
monospace?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Use this component for all interface text that should not be selectable
|
||||
* by the user, this is the majority of UI text explainers, notes, headings.
|
||||
*/
|
||||
const Text = styled.span<Props>`
|
||||
margin-top: 0;
|
||||
text-align: ${(props) => (props.dir ? props.dir : "inherit")};
|
||||
color: ${(props) =>
|
||||
props.type === "secondary"
|
||||
? props.theme.textSecondary
|
||||
: props.type === "tertiary"
|
||||
? props.theme.textTertiary
|
||||
: props.type === "danger"
|
||||
? props.theme.brand.red
|
||||
: props.theme.text};
|
||||
font-size: ${(props) =>
|
||||
props.size === "xlarge"
|
||||
? "26px"
|
||||
: props.size === "large"
|
||||
? "18px"
|
||||
: props.size === "medium"
|
||||
? "16px"
|
||||
: props.size === "small"
|
||||
? "14px"
|
||||
: props.size === "xsmall"
|
||||
? "13px"
|
||||
: "inherit"};
|
||||
|
||||
${(props) =>
|
||||
props.weight &&
|
||||
css`
|
||||
font-weight: ${props.weight === "xbold"
|
||||
? 600
|
||||
: props.weight === "bold"
|
||||
? 500
|
||||
: props.weight === "normal"
|
||||
? 400
|
||||
: "inherit"};
|
||||
`}
|
||||
|
||||
font-style: ${(props) => (props.italic ? "italic" : "normal")};
|
||||
font-family: ${(props) =>
|
||||
props.monospace ? props.theme.fontFamilyMono : "inherit"};
|
||||
|
||||
white-space: normal;
|
||||
user-select: ${(props) => (props.selectable ? "text" : "none")};
|
||||
|
||||
${(props) => props.ellipsis && ellipsis()}
|
||||
`;
|
||||
import Text from "@shared/components/Text";
|
||||
|
||||
export default Text;
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import { LinkIcon } from "outline-icons";
|
||||
import { observer } from "mobx-react";
|
||||
import { EmailIcon, LinkIcon } from "outline-icons";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { v4 } from "uuid";
|
||||
import { EmbedDescriptor } from "@shared/editor/embeds";
|
||||
import { MenuItem } from "@shared/editor/types";
|
||||
import { MentionType } from "@shared/types";
|
||||
import Integration from "~/models/Integration";
|
||||
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import { determineMentionType, isURLMentionable } from "~/utils/mention";
|
||||
import SuggestionsMenu, {
|
||||
Props as SuggestionsMenuProps,
|
||||
} from "./SuggestionsMenu";
|
||||
@@ -15,34 +23,65 @@ type Props = Omit<
|
||||
embeds: EmbedDescriptor[];
|
||||
};
|
||||
|
||||
const PasteMenu = ({ embeds, ...props }: Props) => {
|
||||
export const PasteMenu = observer(({ pastedText, embeds, ...props }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { integrations } = useStores();
|
||||
const user = useCurrentUser({ rejectOnEmpty: false });
|
||||
|
||||
let mentionType: MentionType | undefined;
|
||||
const url = pastedText ? new URL(pastedText) : undefined;
|
||||
|
||||
if (url) {
|
||||
const integration = integrations.find((intg: Integration) =>
|
||||
isURLMentionable({ url, integration: intg })
|
||||
);
|
||||
|
||||
mentionType = integration
|
||||
? determineMentionType({ url, integration })
|
||||
: undefined;
|
||||
}
|
||||
|
||||
const embed = React.useMemo(() => {
|
||||
for (const e of embeds) {
|
||||
const matches = e.matcher(props.pastedText);
|
||||
const matches = e.matcher(pastedText);
|
||||
if (matches) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}, [embeds, props.pastedText]);
|
||||
}, [embeds, pastedText]);
|
||||
|
||||
const items = React.useMemo(
|
||||
() => [
|
||||
{
|
||||
name: "noop",
|
||||
title: t("Keep as link"),
|
||||
icon: <LinkIcon />,
|
||||
},
|
||||
{
|
||||
name: "embed",
|
||||
title: t("Embed"),
|
||||
icon: embed?.icon,
|
||||
keywords: embed?.keywords,
|
||||
},
|
||||
],
|
||||
[embed, t]
|
||||
() =>
|
||||
[
|
||||
{
|
||||
name: "noop",
|
||||
title: t("Keep as link"),
|
||||
icon: <LinkIcon />,
|
||||
},
|
||||
{
|
||||
name: "mention",
|
||||
title: t("Mention"),
|
||||
icon: <EmailIcon />,
|
||||
visible: !!mentionType,
|
||||
attrs: {
|
||||
id: v4(),
|
||||
type: mentionType,
|
||||
label: pastedText,
|
||||
href: pastedText,
|
||||
modelId: v4(),
|
||||
actorId: user?.id,
|
||||
},
|
||||
appendSpace: true,
|
||||
},
|
||||
{
|
||||
name: "embed",
|
||||
title: t("Embed"),
|
||||
icon: embed?.icon,
|
||||
keywords: embed?.keywords,
|
||||
},
|
||||
] satisfies MenuItem[],
|
||||
[t, embed, mentionType, pastedText, user]
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -52,9 +91,7 @@ const PasteMenu = ({ embeds, ...props }: Props) => {
|
||||
filterable={false}
|
||||
renderMenuItem={(item, _index, options) => (
|
||||
<SuggestionsMenuItem
|
||||
onClick={() => {
|
||||
props.onSelect?.(item);
|
||||
}}
|
||||
onClick={options.onClick}
|
||||
selected={options.selected}
|
||||
title={item.title}
|
||||
icon={item.icon}
|
||||
@@ -63,6 +100,4 @@ const PasteMenu = ({ embeds, ...props }: Props) => {
|
||||
items={items}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default PasteMenu;
|
||||
});
|
||||
|
||||
@@ -174,12 +174,12 @@ export default function SelectionToolbar(props: Props) {
|
||||
const { isTemplate, rtl, canComment, canUpdate, ...rest } = props;
|
||||
const { state } = view;
|
||||
const { selection } = state;
|
||||
const isDividerSelection = isNodeActive(state.schema.nodes.hr)(state);
|
||||
|
||||
if ((readOnly && !canComment) || isDragging) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isDividerSelection = isNodeActive(state.schema.nodes.hr)(state);
|
||||
const colIndex = getColumnIndex(state);
|
||||
const rowIndex = getRowIndex(state);
|
||||
const isTableSelection = colIndex !== undefined && rowIndex !== undefined;
|
||||
|
||||
@@ -24,7 +24,7 @@ import parseCollectionSlug from "@shared/utils/parseCollectionSlug";
|
||||
import parseDocumentSlug from "@shared/utils/parseDocumentSlug";
|
||||
import { isCollectionUrl, isDocumentUrl, isUrl } from "@shared/utils/urls";
|
||||
import stores from "~/stores";
|
||||
import PasteMenu from "../components/PasteMenu";
|
||||
import { PasteMenu } from "../components/PasteMenu";
|
||||
|
||||
export default class PasteHandler extends Extension {
|
||||
state: {
|
||||
@@ -415,6 +415,21 @@ export default class PasteHandler extends Extension {
|
||||
});
|
||||
};
|
||||
|
||||
private insertMention = () => {
|
||||
const { view } = this.editor;
|
||||
const { state } = view;
|
||||
const result = this.findPlaceholder(state, this.state.pastedText);
|
||||
|
||||
// Remove just the placeholder here.
|
||||
// Mention node will be created by SuggestionsMenu.
|
||||
if (result) {
|
||||
const tr = state.tr.deleteRange(result[0], result[1]);
|
||||
view.dispatch(
|
||||
tr.setSelection(TextSelection.near(tr.doc.resolve(result[0])))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
private removePlaceholder = () => {
|
||||
const { view } = this.editor;
|
||||
const { state } = view;
|
||||
@@ -450,6 +465,11 @@ export default class PasteHandler extends Extension {
|
||||
this.insertEmbed();
|
||||
break;
|
||||
}
|
||||
case "mention": {
|
||||
this.hidePasteMenu();
|
||||
this.insertMention();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,3 @@
|
||||
import * as React from "react";
|
||||
import useIsMounted from "@shared/hooks/useIsMounted";
|
||||
|
||||
/**
|
||||
* Hook to check if component is still mounted
|
||||
*
|
||||
* @returns {boolean} true if the component is mounted, false otherwise
|
||||
*/
|
||||
export default function useIsMounted() {
|
||||
const isMounted = React.useRef(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
isMounted.current = true;
|
||||
return () => {
|
||||
isMounted.current = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return React.useCallback(() => isMounted.current, []);
|
||||
}
|
||||
export default useIsMounted;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { observable } from "mobx";
|
||||
import type {
|
||||
import {
|
||||
IntegrationService,
|
||||
IntegrationSettings,
|
||||
IntegrationType,
|
||||
type IntegrationSettings,
|
||||
type IntegrationType,
|
||||
} from "@shared/types";
|
||||
import User from "~/models/User";
|
||||
import Model from "~/models/base/Model";
|
||||
|
||||
@@ -209,7 +209,9 @@ function Invite({ onSubmit }: Props) {
|
||||
placeholder={`name@${predictedDomain}`}
|
||||
value={invite.email}
|
||||
required={index === 0}
|
||||
autoComplete="off"
|
||||
autoFocus
|
||||
data-1p-ignore
|
||||
flex
|
||||
/>
|
||||
<StyledInput
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { useTheme } from "styled-components";
|
||||
import Spinner from "@shared/components/Spinner";
|
||||
import {
|
||||
FileOperationFormat,
|
||||
FileOperationState,
|
||||
@@ -13,7 +14,6 @@ import FileOperation from "~/models/FileOperation";
|
||||
import { Action } from "~/components/Actions";
|
||||
import ConfirmationDialog from "~/components/ConfirmationDialog";
|
||||
import ListItem from "~/components/List/Item";
|
||||
import Spinner from "~/components/Spinner";
|
||||
import Time from "~/components/Time";
|
||||
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||
import useStores from "~/hooks/useStores";
|
||||
|
||||
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { useState, useRef } from "react";
|
||||
import AvatarEditor from "react-avatar-editor";
|
||||
import Dropzone from "react-dropzone";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import { s } from "@shared/styles";
|
||||
@@ -12,7 +12,6 @@ import { AttachmentValidation } from "@shared/validations";
|
||||
import ButtonLarge from "~/components/ButtonLarge";
|
||||
import Flex from "~/components/Flex";
|
||||
import LoadingIndicator from "~/components/LoadingIndicator";
|
||||
import Modal from "~/components/Modal";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import { compressImage } from "~/utils/compressImage";
|
||||
import { uploadFile, dataUrlToBlob } from "~/utils/files";
|
||||
@@ -28,81 +27,138 @@ const ImageUpload: React.FC<Props> = ({
|
||||
onSuccess,
|
||||
onError,
|
||||
submitText,
|
||||
borderRadius = 150,
|
||||
borderRadius,
|
||||
children,
|
||||
}) => {
|
||||
const { ui } = useStores();
|
||||
const { dialogs } = useStores();
|
||||
const { t } = useTranslation();
|
||||
submitText || t("Crop image");
|
||||
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
const [isCropping, setIsCropping] = useState(false);
|
||||
const [zoom, setZoom] = useState(1);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
|
||||
const avatarEditorRef = useRef<AvatarEditor>(null);
|
||||
const uploadImage = React.useCallback(
|
||||
async (blob: Blob, file: File) => {
|
||||
try {
|
||||
const compressed = await compressImage(blob, {
|
||||
maxHeight: 512,
|
||||
maxWidth: 512,
|
||||
});
|
||||
const attachment = await uploadFile(compressed, {
|
||||
name: file.name,
|
||||
preset: AttachmentPreset.Avatar,
|
||||
});
|
||||
void onSuccess(attachment.url);
|
||||
} catch (err) {
|
||||
onError(err.message);
|
||||
} finally {
|
||||
setIsUploading(false);
|
||||
setIsCropping(false);
|
||||
dialogs.closeAllModals();
|
||||
}
|
||||
},
|
||||
[dialogs, onSuccess, onError]
|
||||
);
|
||||
|
||||
const onDropAccepted = async (files: File[]) => {
|
||||
setIsCropping(true);
|
||||
setFile(files[0]);
|
||||
};
|
||||
const handleUpload = React.useCallback(
|
||||
(blob: Blob, file: File) => {
|
||||
setIsUploading(true);
|
||||
// allow the UI to update before converting the canvas to a Blob
|
||||
// for large images this can cause the page rendering to hang.
|
||||
setTimeout(() => uploadImage(blob, file), 0);
|
||||
},
|
||||
[uploadImage]
|
||||
);
|
||||
|
||||
const handleCrop = () => {
|
||||
setIsUploading(true);
|
||||
// allow the UI to update before converting the canvas to a Blob
|
||||
// for large images this can cause the page rendering to hang.
|
||||
setTimeout(uploadImage, 0);
|
||||
};
|
||||
|
||||
const uploadImage = async () => {
|
||||
const canvas = avatarEditorRef.current?.getImage();
|
||||
invariant(canvas, "canvas is not defined");
|
||||
const imageBlob = dataUrlToBlob(canvas.toDataURL());
|
||||
|
||||
try {
|
||||
const compressed = await compressImage(imageBlob, {
|
||||
maxHeight: 512,
|
||||
maxWidth: 512,
|
||||
});
|
||||
const attachment = await uploadFile(compressed, {
|
||||
name: file!.name,
|
||||
preset: AttachmentPreset.Avatar,
|
||||
});
|
||||
void onSuccess(attachment.url);
|
||||
} catch (err) {
|
||||
onError(err.message);
|
||||
} finally {
|
||||
setIsUploading(false);
|
||||
setIsCropping(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
const handleClose = React.useCallback(() => {
|
||||
setIsUploading(false);
|
||||
setIsCropping(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleZoom = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const target = event.target;
|
||||
const onDropAccepted = React.useCallback(
|
||||
async (files: File[]) => {
|
||||
setIsCropping(true);
|
||||
dialogs.openModal({
|
||||
title: "",
|
||||
content: (
|
||||
<AvatarEditorDialog
|
||||
file={files[0]}
|
||||
onUpload={handleUpload}
|
||||
isUploading={isUploading}
|
||||
borderRadius={borderRadius ?? 150}
|
||||
submitText={submitText || t("Crop image")}
|
||||
/>
|
||||
),
|
||||
onClose: handleClose,
|
||||
});
|
||||
},
|
||||
[
|
||||
t,
|
||||
dialogs,
|
||||
handleUpload,
|
||||
handleClose,
|
||||
isUploading,
|
||||
borderRadius,
|
||||
submitText,
|
||||
]
|
||||
);
|
||||
|
||||
if (target instanceof HTMLInputElement) {
|
||||
setZoom(parseFloat(target.value));
|
||||
}
|
||||
};
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
accept: AttachmentValidation.avatarContentTypes.join(", "),
|
||||
onDropAccepted,
|
||||
});
|
||||
|
||||
const renderCropping = () => (
|
||||
<Modal
|
||||
onRequestClose={handleClose}
|
||||
fullscreen={false}
|
||||
title={<> </>}
|
||||
isOpen
|
||||
>
|
||||
if (isCropping) {
|
||||
return null; // onDropAccepted would have opened a modal for cropping the image.
|
||||
}
|
||||
|
||||
return (
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type AvatarEditorDialogProps = {
|
||||
file: File;
|
||||
onUpload: (blob: Blob, file: File) => void;
|
||||
isUploading: boolean;
|
||||
borderRadius: number;
|
||||
submitText: string;
|
||||
};
|
||||
|
||||
const AvatarEditorDialog: React.FC<AvatarEditorDialogProps> = observer(
|
||||
({ file, onUpload, isUploading, borderRadius, submitText }) => {
|
||||
const { ui } = useStores();
|
||||
const { t } = useTranslation();
|
||||
const [zoom, setZoom] = useState(1);
|
||||
const avatarEditorRef = useRef<AvatarEditor>(null);
|
||||
|
||||
const handleUpload = React.useCallback(() => {
|
||||
const canvas = avatarEditorRef.current?.getImage();
|
||||
invariant(canvas, "canvas is not defined");
|
||||
const blob = dataUrlToBlob(canvas.toDataURL());
|
||||
onUpload(blob, file);
|
||||
}, [file, onUpload]);
|
||||
|
||||
const handleZoom = React.useCallback(
|
||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const target = event.target;
|
||||
|
||||
if (target instanceof HTMLInputElement) {
|
||||
setZoom(parseFloat(target.value));
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex auto column align="center" justify="center">
|
||||
{isUploading && <LoadingIndicator />}
|
||||
<AvatarEditorContainer>
|
||||
<AvatarEditor
|
||||
ref={avatarEditorRef}
|
||||
image={file!}
|
||||
image={file}
|
||||
width={250}
|
||||
height={250}
|
||||
border={25}
|
||||
@@ -121,31 +177,13 @@ const ImageUpload: React.FC<Props> = ({
|
||||
onChange={handleZoom}
|
||||
/>
|
||||
<br />
|
||||
<ButtonLarge fullwidth onClick={handleCrop} disabled={isUploading}>
|
||||
<ButtonLarge fullwidth onClick={handleUpload} disabled={isUploading}>
|
||||
{isUploading ? `${t(`Uploading`)}…` : submitText}
|
||||
</ButtonLarge>
|
||||
</Flex>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
if (isCropping && file) {
|
||||
return renderCropping();
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropzone
|
||||
accept={AttachmentValidation.avatarContentTypes.join(", ")}
|
||||
onDropAccepted={onDropAccepted}
|
||||
>
|
||||
{({ getRootProps, getInputProps }) => (
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
{children}
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
);
|
||||
};
|
||||
);
|
||||
|
||||
const AvatarEditorContainer = styled(Flex)`
|
||||
margin-bottom: 30px;
|
||||
|
||||
@@ -5,12 +5,12 @@ import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { useTheme } from "styled-components";
|
||||
import Spinner from "@shared/components/Spinner";
|
||||
import { ImportState } from "@shared/types";
|
||||
import Import from "~/models/Import";
|
||||
import { Action } from "~/components/Actions";
|
||||
import ConfirmationDialog from "~/components/ConfirmationDialog";
|
||||
import ListItem from "~/components/List/Item";
|
||||
import Spinner from "~/components/Spinner";
|
||||
import Time from "~/components/Time";
|
||||
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||
import useStores from "~/hooks/useStores";
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import {
|
||||
IntegrationService,
|
||||
IntegrationSettings,
|
||||
IntegrationType,
|
||||
MentionType,
|
||||
} from "@shared/types";
|
||||
import Integration from "~/models/Integration";
|
||||
|
||||
export const isURLMentionable = ({
|
||||
url,
|
||||
integration,
|
||||
}: {
|
||||
url: URL;
|
||||
integration: Integration;
|
||||
}): boolean => {
|
||||
const { hostname, pathname } = url;
|
||||
const pathParts = pathname.split("/");
|
||||
|
||||
switch (integration.service) {
|
||||
case IntegrationService.GitHub: {
|
||||
const settings =
|
||||
integration.settings as IntegrationSettings<IntegrationType.Embed>;
|
||||
|
||||
return (
|
||||
hostname === "github.com" &&
|
||||
settings.github?.installation.account.name === pathParts[1] // ensure installed org/account name matches with the provided url.
|
||||
);
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const determineMentionType = ({
|
||||
url,
|
||||
integration,
|
||||
}: {
|
||||
url: URL;
|
||||
integration: Integration;
|
||||
}): MentionType | undefined => {
|
||||
const { pathname } = url;
|
||||
const pathParts = pathname.split("/");
|
||||
|
||||
switch (integration.service) {
|
||||
case IntegrationService.GitHub: {
|
||||
const type = pathParts[3];
|
||||
return type === "pull"
|
||||
? MentionType.PullRequest
|
||||
: type === "issues"
|
||||
? MentionType.Issue
|
||||
: undefined;
|
||||
}
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
};
|
||||
+8
-8
@@ -48,11 +48,11 @@
|
||||
"> 0.25%, not dead"
|
||||
],
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.782.0",
|
||||
"@aws-sdk/lib-storage": "3.782.0",
|
||||
"@aws-sdk/s3-presigned-post": "3.782.0",
|
||||
"@aws-sdk/s3-request-presigner": "3.782.0",
|
||||
"@aws-sdk/signature-v4-crt": "^3.782.0",
|
||||
"@aws-sdk/client-s3": "3.787.0",
|
||||
"@aws-sdk/lib-storage": "3.787.0",
|
||||
"@aws-sdk/s3-presigned-post": "3.787.0",
|
||||
"@aws-sdk/s3-request-presigner": "3.787.0",
|
||||
"@aws-sdk/signature-v4-crt": "^3.787.0",
|
||||
"@babel/core": "^7.26.10",
|
||||
"@babel/plugin-proposal-decorators": "^7.25.9",
|
||||
"@babel/plugin-transform-class-properties": "^7.25.9",
|
||||
@@ -187,7 +187,7 @@
|
||||
"prosemirror-keymap": "^1.2.2",
|
||||
"prosemirror-markdown": "^1.13.2",
|
||||
"prosemirror-model": "^1.25.0",
|
||||
"prosemirror-schema-list": "^1.4.1",
|
||||
"prosemirror-schema-list": "^1.5.1",
|
||||
"prosemirror-state": "^1.4.3",
|
||||
"prosemirror-tables": "^1.6.4",
|
||||
"prosemirror-transform": "1.10.0",
|
||||
@@ -210,7 +210,7 @@
|
||||
"react-merge-refs": "^2.1.1",
|
||||
"react-portal": "^4.2.2",
|
||||
"react-router-dom": "^5.3.4",
|
||||
"react-virtualized-auto-sizer": "^1.0.21",
|
||||
"react-virtualized-auto-sizer": "^1.0.26",
|
||||
"react-waypoint": "^10.3.0",
|
||||
"react-window": "^1.8.11",
|
||||
"reakit": "^1.3.11",
|
||||
@@ -362,7 +362,7 @@
|
||||
"rimraf": "^2.5.4",
|
||||
"rollup-plugin-webpack-stats": "^2.0.3",
|
||||
"terser": "^5.39.0",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript": "^5.8.3",
|
||||
"vite-plugin-static-copy": "^0.17.0",
|
||||
"yarn-deduplicate": "^6.0.2"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { JSDOM } from "jsdom";
|
||||
import compact from "lodash/compact";
|
||||
import flatten from "lodash/flatten";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import isMatch from "lodash/isMatch";
|
||||
import uniq from "lodash/uniq";
|
||||
import { Node, DOMSerializer, Fragment } from "prosemirror-model";
|
||||
import * as React from "react";
|
||||
@@ -12,7 +12,7 @@ import * as Y from "yjs";
|
||||
import EditorContainer from "@shared/editor/components/Styles";
|
||||
import GlobalStyles from "@shared/styles/globals";
|
||||
import light from "@shared/styles/theme";
|
||||
import { MentionType, ProsemirrorData } from "@shared/types";
|
||||
import { MentionType, ProsemirrorData, UnfurlResponse } from "@shared/types";
|
||||
import { attachmentRedirectRegex } from "@shared/utils/ProsemirrorHelper";
|
||||
import parseDocumentSlug from "@shared/utils/parseDocumentSlug";
|
||||
import { isRTL } from "@shared/utils/rtl";
|
||||
@@ -42,6 +42,8 @@ export type MentionAttrs = {
|
||||
modelId: string;
|
||||
actorId: string | undefined;
|
||||
id: string;
|
||||
href?: string;
|
||||
unfurl?: UnfurlResponse[keyof UnfurlResponse];
|
||||
};
|
||||
|
||||
@trace()
|
||||
@@ -194,7 +196,7 @@ export class ProsemirrorHelper {
|
||||
node.descendants((childNode: Node) => {
|
||||
if (
|
||||
childNode.type.name === "mention" &&
|
||||
isEqual(childNode.attrs, mention)
|
||||
isMatch(childNode.attrs, mention)
|
||||
) {
|
||||
foundMention = true;
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import { ellipsis } from "../styles";
|
||||
|
||||
type Props = {
|
||||
/** The type of text to render */
|
||||
type?: "secondary" | "tertiary" | "danger";
|
||||
/** The size of the text */
|
||||
size?: "xlarge" | "large" | "medium" | "small" | "xsmall";
|
||||
/** The direction of the text (defaults to ltr) */
|
||||
dir?: "ltr" | "rtl" | "auto";
|
||||
/** Whether the text should be selectable (defaults to false) */
|
||||
selectable?: boolean;
|
||||
/** The font weight of the text */
|
||||
weight?: "xbold" | "bold" | "normal";
|
||||
/** Whether the text should be italic */
|
||||
italic?: boolean;
|
||||
/** Whether the text should be truncated with an ellipsis */
|
||||
ellipsis?: boolean;
|
||||
/** Whether the text should be monospaced */
|
||||
monospace?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Use this component for all interface text that should not be selectable
|
||||
* by the user, this is the majority of UI text explainers, notes, headings.
|
||||
*/
|
||||
const Text = styled.span<Props>`
|
||||
margin-top: 0;
|
||||
text-align: ${(props) => (props.dir ? props.dir : "inherit")};
|
||||
color: ${(props) =>
|
||||
props.type === "secondary"
|
||||
? props.theme.textSecondary
|
||||
: props.type === "tertiary"
|
||||
? props.theme.textTertiary
|
||||
: props.type === "danger"
|
||||
? props.theme.brand.red
|
||||
: props.theme.text};
|
||||
font-size: ${(props) =>
|
||||
props.size === "xlarge"
|
||||
? "26px"
|
||||
: props.size === "large"
|
||||
? "18px"
|
||||
: props.size === "medium"
|
||||
? "16px"
|
||||
: props.size === "small"
|
||||
? "14px"
|
||||
: props.size === "xsmall"
|
||||
? "13px"
|
||||
: "inherit"};
|
||||
|
||||
${(props) =>
|
||||
props.weight &&
|
||||
css`
|
||||
font-weight: ${props.weight === "xbold"
|
||||
? 600
|
||||
: props.weight === "bold"
|
||||
? 500
|
||||
: props.weight === "normal"
|
||||
? 400
|
||||
: "inherit"};
|
||||
`}
|
||||
|
||||
font-style: ${(props) => (props.italic ? "italic" : "normal")};
|
||||
font-family: ${(props) =>
|
||||
props.monospace ? props.theme.fontFamilyMono : "inherit"};
|
||||
|
||||
white-space: normal;
|
||||
user-select: ${(props) => (props.selectable ? "text" : "none")};
|
||||
|
||||
${(props) => props.ellipsis && ellipsis()}
|
||||
`;
|
||||
|
||||
export default Text;
|
||||
@@ -1,17 +1,49 @@
|
||||
import { observer } from "mobx-react";
|
||||
import { DocumentIcon, EmailIcon, CollectionIcon } from "outline-icons";
|
||||
import {
|
||||
DocumentIcon,
|
||||
EmailIcon,
|
||||
CollectionIcon,
|
||||
WarningIcon,
|
||||
} from "outline-icons";
|
||||
import { Node } from "prosemirror-model";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import Flex from "../../components/Flex";
|
||||
import Icon from "../../components/Icon";
|
||||
import { IssueStatusIcon } from "../../components/IssueStatusIcon";
|
||||
import { PullRequestIcon } from "../../components/PullRequestIcon";
|
||||
import Spinner from "../../components/Spinner";
|
||||
import Text from "../../components/Text";
|
||||
import useIsMounted from "../../hooks/useIsMounted";
|
||||
import useStores from "../../hooks/useStores";
|
||||
import theme from "../../styles/theme";
|
||||
import type {
|
||||
JSONValue,
|
||||
UnfurlResourceType,
|
||||
UnfurlResponse,
|
||||
} from "../../types";
|
||||
import { cn } from "../styles/utils";
|
||||
import { ComponentProps } from "../types";
|
||||
|
||||
const getAttributesFromNode = (node: Node) => {
|
||||
const spec = node.type.spec.toDOM?.(node) as any as Record<string, string>[];
|
||||
const { class: className, ...attrs } = spec[1];
|
||||
return { className, ...attrs };
|
||||
type Attrs = {
|
||||
className: string;
|
||||
unfurl?: UnfurlResponse[keyof UnfurlResponse];
|
||||
} & Record<string, JSONValue>;
|
||||
|
||||
const getAttributesFromNode = (node: Node): Attrs => {
|
||||
const spec = node.type.spec.toDOM?.(node) as any as Record<
|
||||
string,
|
||||
JSONValue
|
||||
>[];
|
||||
const { class: className, "data-unfurl": unfurl, ...attrs } = spec[1];
|
||||
|
||||
return {
|
||||
className: className as Attrs["className"],
|
||||
unfurl: unfurl ? (JSON.parse(unfurl as any) as Attrs["unfurl"]) : undefined,
|
||||
...attrs,
|
||||
};
|
||||
};
|
||||
|
||||
export const MentionUser = observer(function MentionUser_(
|
||||
@@ -20,7 +52,7 @@ export const MentionUser = observer(function MentionUser_(
|
||||
const { isSelected, node } = props;
|
||||
const { users } = useStores();
|
||||
const user = users.get(node.attrs.modelId);
|
||||
const { className, ...attrs } = getAttributesFromNode(node);
|
||||
const { className, unfurl, ...attrs } = getAttributesFromNode(node);
|
||||
|
||||
return (
|
||||
<span
|
||||
@@ -42,7 +74,7 @@ export const MentionDocument = observer(function MentionDocument_(
|
||||
const { documents } = useStores();
|
||||
const doc = documents.get(node.attrs.modelId);
|
||||
const modelId = node.attrs.modelId;
|
||||
const { className, ...attrs } = getAttributesFromNode(node);
|
||||
const { className, unfurl, ...attrs } = getAttributesFromNode(node);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (modelId) {
|
||||
@@ -75,7 +107,7 @@ export const MentionCollection = observer(function MentionCollection_(
|
||||
const { collections } = useStores();
|
||||
const collection = collections.get(node.attrs.modelId);
|
||||
const modelId = node.attrs.modelId;
|
||||
const { className, ...attrs } = getAttributesFromNode(node);
|
||||
const { className, unfurl, ...attrs } = getAttributesFromNode(node);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (modelId) {
|
||||
@@ -100,3 +132,183 @@ export const MentionCollection = observer(function MentionCollection_(
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
|
||||
type IssuePrProps = ComponentProps & {
|
||||
onChangeUnfurl: (
|
||||
unfurl:
|
||||
| UnfurlResponse[UnfurlResourceType.Issue]
|
||||
| UnfurlResponse[UnfurlResourceType.PR]
|
||||
) => void;
|
||||
};
|
||||
|
||||
export const MentionIssue = observer((props: IssuePrProps) => {
|
||||
const { unfurls } = useStores();
|
||||
const isMounted = useIsMounted();
|
||||
const [loaded, setLoaded] = React.useState(false);
|
||||
const onChangeUnfurl = React.useRef(props.onChangeUnfurl).current; // stable reference to callback function.
|
||||
|
||||
const { isSelected, node } = props;
|
||||
const {
|
||||
className,
|
||||
unfurl: unfurlAttr,
|
||||
...attrs
|
||||
} = getAttributesFromNode(node);
|
||||
|
||||
const unfurl = unfurls.get(attrs.href)?.data ?? unfurlAttr;
|
||||
|
||||
React.useEffect(() => {
|
||||
const fetchIssue = async () => {
|
||||
const unfurlModel = await unfurls.fetchUnfurl({ url: attrs.href });
|
||||
|
||||
if (!isMounted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (unfurlModel) {
|
||||
onChangeUnfurl({
|
||||
...unfurlModel.data,
|
||||
description: null,
|
||||
} satisfies UnfurlResponse[UnfurlResourceType.Issue]);
|
||||
}
|
||||
|
||||
setLoaded(true);
|
||||
};
|
||||
|
||||
void fetchIssue();
|
||||
}, [unfurls, attrs.href, isMounted, onChangeUnfurl]);
|
||||
|
||||
if (!unfurl) {
|
||||
return !loaded ? (
|
||||
<MentionLoading className={className} />
|
||||
) : (
|
||||
<MentionError className={className} />
|
||||
);
|
||||
}
|
||||
|
||||
const issue = unfurl as UnfurlResponse[UnfurlResourceType.Issue];
|
||||
|
||||
return (
|
||||
<a
|
||||
{...attrs}
|
||||
className={cn(className, {
|
||||
"ProseMirror-selectednode": isSelected,
|
||||
})}
|
||||
href={attrs.href as string}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer nofollow"
|
||||
>
|
||||
<Flex align="center" gap={6}>
|
||||
<IssueStatusIcon
|
||||
size={14}
|
||||
status={issue.state.name}
|
||||
color={issue.state.color}
|
||||
/>
|
||||
<Flex align="center" gap={4}>
|
||||
<Text>{issue.title}</Text>
|
||||
<Text type="tertiary">{issue.id}</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</a>
|
||||
);
|
||||
});
|
||||
|
||||
export const MentionPullRequest = observer((props: IssuePrProps) => {
|
||||
const { unfurls } = useStores();
|
||||
const isMounted = useIsMounted();
|
||||
const [loaded, setLoaded] = React.useState(false);
|
||||
const onChangeUnfurl = React.useRef(props.onChangeUnfurl).current; // stable reference to callback function.
|
||||
|
||||
const { isSelected, node } = props;
|
||||
const {
|
||||
className,
|
||||
unfurl: unfurlAttr,
|
||||
...attrs
|
||||
} = getAttributesFromNode(node);
|
||||
|
||||
const unfurl = unfurls.get(attrs.href)?.data ?? unfurlAttr;
|
||||
|
||||
React.useEffect(() => {
|
||||
const fetchPR = async () => {
|
||||
const unfurlModel = await unfurls.fetchUnfurl({ url: attrs.href });
|
||||
|
||||
if (!isMounted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (unfurlModel) {
|
||||
onChangeUnfurl({
|
||||
...unfurlModel.data,
|
||||
description: null,
|
||||
} satisfies UnfurlResponse[UnfurlResourceType.PR]);
|
||||
}
|
||||
|
||||
setLoaded(true);
|
||||
};
|
||||
|
||||
void fetchPR();
|
||||
}, [unfurls, attrs.href, isMounted, onChangeUnfurl]);
|
||||
|
||||
const sharedProps = {
|
||||
className: cn(className, {
|
||||
"ProseMirror-selectednode": isSelected,
|
||||
}),
|
||||
};
|
||||
|
||||
if (!unfurl) {
|
||||
return !loaded ? (
|
||||
<MentionLoading {...sharedProps} />
|
||||
) : (
|
||||
<MentionError {...sharedProps} />
|
||||
);
|
||||
}
|
||||
|
||||
const pullRequest = unfurl as UnfurlResponse[UnfurlResourceType.PR];
|
||||
|
||||
return (
|
||||
<a
|
||||
{...attrs}
|
||||
{...sharedProps}
|
||||
href={attrs.href as string}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer nofollow"
|
||||
>
|
||||
<Flex align="center" gap={6}>
|
||||
<PullRequestIcon
|
||||
size={14}
|
||||
status={pullRequest.state.name}
|
||||
color={pullRequest.state.color}
|
||||
/>
|
||||
<Flex align="center" gap={4}>
|
||||
<Text>{pullRequest.title}</Text>
|
||||
<Text type="tertiary">{pullRequest.id}</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</a>
|
||||
);
|
||||
});
|
||||
|
||||
const MentionLoading = ({ className }: { className: string }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<span className={className}>
|
||||
<Spinner />
|
||||
<Text type="tertiary">{`${t("Loading")}…`}</Text>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const MentionError = ({ className }: { className: string }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<span className={className}>
|
||||
<StyledWarningIcon size={20} color={theme.danger} />
|
||||
<Text type="secondary">{`${t("Error loading data")}`}</Text>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const StyledWarningIcon = styled(WarningIcon)`
|
||||
margin: 0 -2px;
|
||||
`;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Node } from "prosemirror-model";
|
||||
import { Plugin, PluginKey, Transaction } from "prosemirror-state";
|
||||
import { Decoration, DecorationSet } from "prosemirror-view";
|
||||
import refractor from "refractor/core";
|
||||
import { getRefractorLangForLanguage } from "../lib/code";
|
||||
import { getLoaderForLanguage, getRefractorLangForLanguage } from "../lib/code";
|
||||
import { isRemoteTransaction } from "../lib/multiplayer";
|
||||
import { findBlockNodes } from "../queries/findChildren";
|
||||
|
||||
@@ -15,32 +15,42 @@ type ParsedNode = {
|
||||
|
||||
const cache: Record<number, { node: Node; decorations: Decoration[] }> = {};
|
||||
const languagesToImport = new Set<string>();
|
||||
const languagePromises: Record<
|
||||
string,
|
||||
Promise<string | undefined> | undefined
|
||||
> = {};
|
||||
|
||||
async function loadLanguage(language: string) {
|
||||
if (!language || refractor.registered(language)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// @ts-expect-error we are adding a module to the window object to work
|
||||
// around the fact that refractor doesn't export ESM but import expects it.
|
||||
// See the rules of dynamic imports:
|
||||
// https://github.com/rollup/plugins/blob/e1a5ef99f1578eb38a8c87563cb9651db228f3bd/packages/dynamic-import-vars/README.md#limitations
|
||||
window.module ??= {};
|
||||
return import(`../../../node_modules/refractor/lang/${language}.js`).then(
|
||||
() => {
|
||||
refractor.register(window.module.exports);
|
||||
return language;
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
// It will retry loading the language on the next render
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
`Failed to load language ${language} for code highlighting`,
|
||||
err
|
||||
);
|
||||
|
||||
if (languagePromises[language]) {
|
||||
return languagePromises[language];
|
||||
}
|
||||
return;
|
||||
|
||||
const loader = getLoaderForLanguage(language);
|
||||
if (!loader) {
|
||||
return;
|
||||
}
|
||||
|
||||
languagePromises[language] = loader()
|
||||
.then((syntax) => {
|
||||
refractor.register(syntax);
|
||||
return language;
|
||||
})
|
||||
.catch((err) => {
|
||||
// It will retry loading the language on the next render
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
`Failed to load language ${language} for code highlighting`,
|
||||
err
|
||||
);
|
||||
delete languagePromises[language]; // Remove failed promise from cache
|
||||
return undefined;
|
||||
});
|
||||
|
||||
return languagePromises[language];
|
||||
}
|
||||
|
||||
function getDecorations({
|
||||
@@ -85,7 +95,8 @@ function getDecorations({
|
||||
|
||||
blocks.forEach((block) => {
|
||||
let startPos = block.pos + 1;
|
||||
const language = getRefractorLangForLanguage(block.node.attrs.language);
|
||||
const language = block.node.attrs.language;
|
||||
const lang = getRefractorLangForLanguage(language);
|
||||
const lineDecorations = [];
|
||||
|
||||
if (!cache[block.pos] || !cache[block.pos].node.eq(block.node)) {
|
||||
@@ -119,12 +130,12 @@ function getDecorations({
|
||||
decorations: lineDecorations,
|
||||
};
|
||||
|
||||
if (!language) {
|
||||
if (!lang) {
|
||||
// do nothing
|
||||
} else if (refractor.registered(language)) {
|
||||
} else if (refractor.registered(lang)) {
|
||||
languagesToImport.delete(language);
|
||||
|
||||
const nodes = refractor.highlight(block.node.textContent, language);
|
||||
const nodes = refractor.highlight(block.node.textContent, lang);
|
||||
const newDecorations = parseNodes(nodes)
|
||||
.map((node: ParsedNode) => {
|
||||
const from = startPos;
|
||||
@@ -229,14 +240,15 @@ export function CodeHighlighting({
|
||||
}
|
||||
|
||||
void Promise.all([...languagesToImport].map(loadLanguage)).then(
|
||||
(language) =>
|
||||
languagesToImport.size
|
||||
? view.dispatch(
|
||||
view.state.tr.setMeta("codeHighlighting", {
|
||||
langLoaded: language,
|
||||
})
|
||||
)
|
||||
: null
|
||||
(language) => {
|
||||
if (language && languagesToImport.size) {
|
||||
view.dispatch(
|
||||
view.state.tr.setMeta("codeHighlighting", {
|
||||
langLoaded: language,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
+304
-58
@@ -1,3 +1,4 @@
|
||||
import type { RefractorSyntax } from "refractor";
|
||||
import Storage from "../../utils/Storage";
|
||||
|
||||
const RecentlyUsedStorageKey = "rme-code-language";
|
||||
@@ -5,6 +6,12 @@ const StorageKey = "frequent-code-languages";
|
||||
const frequentLanguagesToGet = 5;
|
||||
const frequentLanguagesToTrack = 10;
|
||||
|
||||
type CodeLanguage = {
|
||||
lang: string;
|
||||
label: string;
|
||||
loader?: () => Promise<RefractorSyntax>;
|
||||
};
|
||||
|
||||
/**
|
||||
* List of supported code languages.
|
||||
*
|
||||
@@ -12,65 +19,295 @@ const frequentLanguagesToTrack = 10;
|
||||
* language identifier used by Refractor. Note mismatches such as `markup` and
|
||||
* `mermaid`.
|
||||
*/
|
||||
export const codeLanguages = {
|
||||
export const codeLanguages: Record<string, CodeLanguage> = {
|
||||
none: { lang: "", label: "Plain text" },
|
||||
bash: { lang: "bash", label: "Bash" },
|
||||
clike: { lang: "clike", label: "C" },
|
||||
cpp: { lang: "cpp", label: "C++" },
|
||||
csharp: { lang: "csharp", label: "C#" },
|
||||
css: { lang: "css", label: "CSS" },
|
||||
csv: { lang: "csv", label: "CSV" },
|
||||
docker: { lang: "docker", label: "Docker" },
|
||||
elixir: { lang: "elixir", label: "Elixir" },
|
||||
erb: { lang: "erb", label: "ERB" },
|
||||
erlang: { lang: "erlang", label: "Erlang" },
|
||||
go: { lang: "go", label: "Go" },
|
||||
graphql: { lang: "graphql", label: "GraphQL" },
|
||||
groovy: { lang: "groovy", label: "Groovy" },
|
||||
haskell: { lang: "haskell", label: "Haskell" },
|
||||
hcl: { lang: "hcl", label: "HCL" },
|
||||
markup: { lang: "markup", label: "HTML" },
|
||||
ini: { lang: "ini", label: "INI" },
|
||||
java: { lang: "java", label: "Java" },
|
||||
javascript: { lang: "javascript", label: "JavaScript" },
|
||||
json: { lang: "json", label: "JSON" },
|
||||
jsx: { lang: "jsx", label: "JSX" },
|
||||
kotlin: { lang: "kotlin", label: "Kotlin" },
|
||||
kusto: { lang: "kusto", label: "Kusto" },
|
||||
lisp: { lang: "lisp", label: "Lisp" },
|
||||
lua: { lang: "lua", label: "Lua" },
|
||||
makefile: { lang: "makefile", label: "Makefile" },
|
||||
markdown: { lang: "markdown", label: "Markdown" },
|
||||
mermaidjs: { lang: "mermaid", label: "Mermaid Diagram" },
|
||||
nginx: { lang: "nginx", label: "Nginx" },
|
||||
nix: { lang: "nix", label: "Nix" },
|
||||
objectivec: { lang: "objectivec", label: "Objective-C" },
|
||||
ocaml: { lang: "ocaml", label: "OCaml" },
|
||||
perl: { lang: "perl", label: "Perl" },
|
||||
php: { lang: "php", label: "PHP" },
|
||||
powershell: { lang: "powershell", label: "Powershell" },
|
||||
protobuf: { lang: "protobuf", label: "Protobuf" },
|
||||
python: { lang: "python", label: "Python" },
|
||||
r: { lang: "r", label: "R" },
|
||||
regex: { lang: "regex", label: "Regex" },
|
||||
ruby: { lang: "ruby", label: "Ruby" },
|
||||
rust: { lang: "rust", label: "Rust" },
|
||||
scala: { lang: "scala", label: "Scala" },
|
||||
sass: { lang: "sass", label: "Sass" },
|
||||
scss: { lang: "scss", label: "SCSS" },
|
||||
"splunk-spl": { lang: "splunk-spl", label: "Splunk SPL" },
|
||||
sql: { lang: "sql", label: "SQL" },
|
||||
solidity: { lang: "solidity", label: "Solidity" },
|
||||
swift: { lang: "swift", label: "Swift" },
|
||||
toml: { lang: "toml", label: "TOML" },
|
||||
tsx: { lang: "tsx", label: "TSX" },
|
||||
typescript: { lang: "typescript", label: "TypeScript" },
|
||||
vb: { lang: "vb", label: "Visual Basic" },
|
||||
verilog: { lang: "verilog", label: "Verilog" },
|
||||
vhdl: { lang: "vhdl", label: "VHDL" },
|
||||
yaml: { lang: "yaml", label: "YAML" },
|
||||
xml: { lang: "markup", label: "XML" },
|
||||
zig: { lang: "zig", label: "Zig" },
|
||||
bash: {
|
||||
lang: "bash",
|
||||
label: "Bash",
|
||||
loader: () => import("refractor/lang/bash").then((m) => m.default),
|
||||
},
|
||||
clike: {
|
||||
lang: "clike",
|
||||
label: "C",
|
||||
loader: () => import("refractor/lang/clike").then((m) => m.default),
|
||||
},
|
||||
cpp: {
|
||||
lang: "cpp",
|
||||
label: "C++",
|
||||
loader: () => import("refractor/lang/cpp").then((m) => m.default),
|
||||
},
|
||||
csharp: {
|
||||
lang: "csharp",
|
||||
label: "C#",
|
||||
loader: () => import("refractor/lang/csharp").then((m) => m.default),
|
||||
},
|
||||
css: {
|
||||
lang: "css",
|
||||
label: "CSS",
|
||||
loader: () => import("refractor/lang/css").then((m) => m.default),
|
||||
},
|
||||
csv: {
|
||||
lang: "csv",
|
||||
label: "CSV",
|
||||
loader: () => import("refractor/lang/csv").then((m) => m.default),
|
||||
},
|
||||
docker: {
|
||||
lang: "docker",
|
||||
label: "Docker",
|
||||
loader: () => import("refractor/lang/docker").then((m) => m.default),
|
||||
},
|
||||
elixir: {
|
||||
lang: "elixir",
|
||||
label: "Elixir",
|
||||
loader: () => import("refractor/lang/elixir").then((m) => m.default),
|
||||
},
|
||||
erb: {
|
||||
lang: "erb",
|
||||
label: "ERB",
|
||||
loader: () => import("refractor/lang/erb").then((m) => m.default),
|
||||
},
|
||||
erlang: {
|
||||
lang: "erlang",
|
||||
label: "Erlang",
|
||||
loader: () => import("refractor/lang/erlang").then((m) => m.default),
|
||||
},
|
||||
go: {
|
||||
lang: "go",
|
||||
label: "Go",
|
||||
loader: () => import("refractor/lang/go").then((m) => m.default),
|
||||
},
|
||||
graphql: {
|
||||
lang: "graphql",
|
||||
label: "GraphQL",
|
||||
loader: () => import("refractor/lang/graphql").then((m) => m.default),
|
||||
},
|
||||
groovy: {
|
||||
lang: "groovy",
|
||||
label: "Groovy",
|
||||
loader: () => import("refractor/lang/groovy").then((m) => m.default),
|
||||
},
|
||||
haskell: {
|
||||
lang: "haskell",
|
||||
label: "Haskell",
|
||||
loader: () => import("refractor/lang/haskell").then((m) => m.default),
|
||||
},
|
||||
hcl: {
|
||||
lang: "hcl",
|
||||
label: "HCL",
|
||||
loader: () => import("refractor/lang/hcl").then((m) => m.default),
|
||||
},
|
||||
markup: {
|
||||
lang: "markup",
|
||||
label: "HTML",
|
||||
loader: () => import("refractor/lang/markup").then((m) => m.default),
|
||||
},
|
||||
ini: {
|
||||
lang: "ini",
|
||||
label: "INI",
|
||||
loader: () => import("refractor/lang/ini").then((m) => m.default),
|
||||
},
|
||||
java: {
|
||||
lang: "java",
|
||||
label: "Java",
|
||||
loader: () => import("refractor/lang/java").then((m) => m.default),
|
||||
},
|
||||
javascript: {
|
||||
lang: "javascript",
|
||||
label: "JavaScript",
|
||||
loader: () => import("refractor/lang/javascript").then((m) => m.default),
|
||||
},
|
||||
json: {
|
||||
lang: "json",
|
||||
label: "JSON",
|
||||
loader: () => import("refractor/lang/json").then((m) => m.default),
|
||||
},
|
||||
jsx: {
|
||||
lang: "jsx",
|
||||
label: "JSX",
|
||||
loader: () => import("refractor/lang/jsx").then((m) => m.default),
|
||||
},
|
||||
kotlin: {
|
||||
lang: "kotlin",
|
||||
label: "Kotlin",
|
||||
loader: () => import("refractor/lang/kotlin").then((m) => m.default),
|
||||
},
|
||||
kusto: {
|
||||
lang: "kusto",
|
||||
label: "Kusto",
|
||||
// @ts-expect-error Mermaid is not in types but exists
|
||||
loader: () => import("refractor/lang/kusto").then((m) => m.default),
|
||||
},
|
||||
lisp: {
|
||||
lang: "lisp",
|
||||
label: "Lisp",
|
||||
loader: () => import("refractor/lang/lisp").then((m) => m.default),
|
||||
},
|
||||
lua: {
|
||||
lang: "lua",
|
||||
label: "Lua",
|
||||
loader: () => import("refractor/lang/lua").then((m) => m.default),
|
||||
},
|
||||
makefile: {
|
||||
lang: "makefile",
|
||||
label: "Makefile",
|
||||
loader: () => import("refractor/lang/makefile").then((m) => m.default),
|
||||
},
|
||||
markdown: {
|
||||
lang: "markdown",
|
||||
label: "Markdown",
|
||||
loader: () => import("refractor/lang/markdown").then((m) => m.default),
|
||||
},
|
||||
mermaidjs: {
|
||||
lang: "mermaid",
|
||||
label: "Mermaid Diagram",
|
||||
// @ts-expect-error Mermaid is not in types but exists
|
||||
loader: () => import("refractor/lang/mermaid").then((m) => m.default),
|
||||
},
|
||||
nginx: {
|
||||
lang: "nginx",
|
||||
label: "Nginx",
|
||||
loader: () => import("refractor/lang/nginx").then((m) => m.default),
|
||||
},
|
||||
nix: {
|
||||
lang: "nix",
|
||||
label: "Nix",
|
||||
loader: () => import("refractor/lang/nix").then((m) => m.default),
|
||||
},
|
||||
objectivec: {
|
||||
lang: "objectivec",
|
||||
label: "Objective-C",
|
||||
loader: () => import("refractor/lang/objectivec").then((m) => m.default),
|
||||
},
|
||||
ocaml: {
|
||||
lang: "ocaml",
|
||||
label: "OCaml",
|
||||
loader: () => import("refractor/lang/ocaml").then((m) => m.default),
|
||||
},
|
||||
perl: {
|
||||
lang: "perl",
|
||||
label: "Perl",
|
||||
loader: () => import("refractor/lang/perl").then((m) => m.default),
|
||||
},
|
||||
php: {
|
||||
lang: "php",
|
||||
label: "PHP",
|
||||
loader: () => import("refractor/lang/php").then((m) => m.default),
|
||||
},
|
||||
powershell: {
|
||||
lang: "powershell",
|
||||
label: "Powershell",
|
||||
loader: () => import("refractor/lang/powershell").then((m) => m.default),
|
||||
},
|
||||
protobuf: {
|
||||
lang: "protobuf",
|
||||
label: "Protobuf",
|
||||
loader: () => import("refractor/lang/protobuf").then((m) => m.default),
|
||||
},
|
||||
python: {
|
||||
lang: "python",
|
||||
label: "Python",
|
||||
loader: () => import("refractor/lang/python").then((m) => m.default),
|
||||
},
|
||||
r: {
|
||||
lang: "r",
|
||||
label: "R",
|
||||
loader: () => import("refractor/lang/r").then((m) => m.default),
|
||||
},
|
||||
regex: {
|
||||
lang: "regex",
|
||||
label: "Regex",
|
||||
loader: () => import("refractor/lang/regex").then((m) => m.default),
|
||||
},
|
||||
ruby: {
|
||||
lang: "ruby",
|
||||
label: "Ruby",
|
||||
loader: () => import("refractor/lang/ruby").then((m) => m.default),
|
||||
},
|
||||
rust: {
|
||||
lang: "rust",
|
||||
label: "Rust",
|
||||
loader: () => import("refractor/lang/rust").then((m) => m.default),
|
||||
},
|
||||
scala: {
|
||||
lang: "scala",
|
||||
label: "Scala",
|
||||
loader: () => import("refractor/lang/scala").then((m) => m.default),
|
||||
},
|
||||
sass: {
|
||||
lang: "sass",
|
||||
label: "Sass",
|
||||
loader: () => import("refractor/lang/sass").then((m) => m.default),
|
||||
},
|
||||
scss: {
|
||||
lang: "scss",
|
||||
label: "SCSS",
|
||||
loader: () => import("refractor/lang/scss").then((m) => m.default),
|
||||
},
|
||||
"splunk-spl": {
|
||||
lang: "splunk-spl",
|
||||
label: "Splunk SPL",
|
||||
loader: () => import("refractor/lang/splunk-spl").then((m) => m.default),
|
||||
},
|
||||
sql: {
|
||||
lang: "sql",
|
||||
label: "SQL",
|
||||
loader: () => import("refractor/lang/sql").then((m) => m.default),
|
||||
},
|
||||
solidity: {
|
||||
lang: "solidity",
|
||||
label: "Solidity",
|
||||
loader: () => import("refractor/lang/solidity").then((m) => m.default),
|
||||
},
|
||||
swift: {
|
||||
lang: "swift",
|
||||
label: "Swift",
|
||||
loader: () => import("refractor/lang/swift").then((m) => m.default),
|
||||
},
|
||||
toml: {
|
||||
lang: "toml",
|
||||
label: "TOML",
|
||||
loader: () => import("refractor/lang/toml").then((m) => m.default),
|
||||
},
|
||||
tsx: {
|
||||
lang: "tsx",
|
||||
label: "TSX",
|
||||
loader: () => import("refractor/lang/tsx").then((m) => m.default),
|
||||
},
|
||||
typescript: {
|
||||
lang: "typescript",
|
||||
label: "TypeScript",
|
||||
loader: () => import("refractor/lang/typescript").then((m) => m.default),
|
||||
},
|
||||
vb: {
|
||||
lang: "vbnet",
|
||||
label: "Visual Basic",
|
||||
loader: () => import("refractor/lang/vbnet").then((m) => m.default),
|
||||
},
|
||||
verilog: {
|
||||
lang: "verilog",
|
||||
label: "Verilog",
|
||||
loader: () => import("refractor/lang/verilog").then((m) => m.default),
|
||||
},
|
||||
vhdl: {
|
||||
lang: "vhdl",
|
||||
label: "VHDL",
|
||||
loader: () => import("refractor/lang/vhdl").then((m) => m.default),
|
||||
},
|
||||
yaml: {
|
||||
lang: "yaml",
|
||||
label: "YAML",
|
||||
loader: () => import("refractor/lang/yaml").then((m) => m.default),
|
||||
},
|
||||
xml: {
|
||||
lang: "markup",
|
||||
label: "XML",
|
||||
loader: () => import("refractor/lang/markup").then((m) => m.default),
|
||||
},
|
||||
zig: {
|
||||
lang: "zig",
|
||||
label: "Zig",
|
||||
loader: () => import("refractor/lang/zig").then((m) => m.default),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -96,6 +333,15 @@ export const getRefractorLangForLanguage = (
|
||||
): string | undefined =>
|
||||
codeLanguages[language as keyof typeof codeLanguages]?.lang;
|
||||
|
||||
/**
|
||||
* Get the loader function for a given language.
|
||||
*
|
||||
* @param language The language identifier.
|
||||
* @returns The loader function for the language, or undefined if not available.
|
||||
*/
|
||||
export const getLoaderForLanguage = (language: string) =>
|
||||
codeLanguages[language as keyof typeof codeLanguages]?.loader;
|
||||
|
||||
/**
|
||||
* Set the most recent code language used.
|
||||
*
|
||||
|
||||
@@ -408,10 +408,14 @@ export default class Image extends SimpleImage {
|
||||
if (!(state.selection instanceof NodeSelection)) {
|
||||
return false;
|
||||
}
|
||||
let layoutClass: string | null = "full-width";
|
||||
if (state.selection.node.attrs.layoutClass === layoutClass) {
|
||||
layoutClass = null;
|
||||
}
|
||||
const attrs = {
|
||||
...state.selection.node.attrs,
|
||||
title: null,
|
||||
layoutClass: "full-width",
|
||||
layoutClass,
|
||||
};
|
||||
const { selection } = state;
|
||||
dispatch?.(state.tr.setNodeMarkup(selection.from, undefined, attrs));
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import isMatch from "lodash/isMatch";
|
||||
import { Token } from "markdown-it";
|
||||
import {
|
||||
NodeSpec,
|
||||
@@ -15,10 +16,12 @@ import * as React from "react";
|
||||
import { Primitive } from "utility-types";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import env from "../../env";
|
||||
import { MentionType } from "../../types";
|
||||
import { MentionType, UnfurlResourceType, UnfurlResponse } from "../../types";
|
||||
import {
|
||||
MentionCollection,
|
||||
MentionDocument,
|
||||
MentionIssue,
|
||||
MentionPullRequest,
|
||||
MentionUser,
|
||||
} from "../components/Mentions";
|
||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||
@@ -50,6 +53,12 @@ export default class Mention extends Node {
|
||||
id: {
|
||||
default: undefined,
|
||||
},
|
||||
href: {
|
||||
default: undefined,
|
||||
},
|
||||
unfurl: {
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
inline: true,
|
||||
marks: "",
|
||||
@@ -73,6 +82,10 @@ export default class Mention extends Node {
|
||||
actorId: dom.dataset.actorid,
|
||||
label: dom.innerText,
|
||||
id: dom.id,
|
||||
href: dom.getAttribute("href"),
|
||||
unfurl: dom.dataset.unfurl
|
||||
? JSON.parse(dom.dataset.unfurl)
|
||||
: undefined,
|
||||
};
|
||||
},
|
||||
},
|
||||
@@ -87,11 +100,18 @@ export default class Mention extends Node {
|
||||
? undefined
|
||||
: node.attrs.type === MentionType.Document
|
||||
? `${env.URL}/doc/${node.attrs.modelId}`
|
||||
: `${env.URL}/collection/${node.attrs.modelId}`,
|
||||
: node.attrs.type === MentionType.Collection
|
||||
? `${env.URL}/collection/${node.attrs.modelId}`
|
||||
: node.attrs.href,
|
||||
"data-type": node.attrs.type,
|
||||
"data-id": node.attrs.modelId,
|
||||
"data-actorid": node.attrs.actorId,
|
||||
"data-url": `mention://${node.attrs.id}/${node.attrs.type}/${node.attrs.modelId}`,
|
||||
"data-url":
|
||||
node.attrs.type === MentionType.PullRequest ||
|
||||
node.attrs.type === MentionType.Issue
|
||||
? node.attrs.href
|
||||
: `mention://${node.attrs.id}/${node.attrs.type}/${node.attrs.modelId}`,
|
||||
"data-unfurl": JSON.stringify(node.attrs.unfurl),
|
||||
},
|
||||
toPlainText(node),
|
||||
],
|
||||
@@ -107,6 +127,20 @@ export default class Mention extends Node {
|
||||
return <MentionDocument {...props} />;
|
||||
case MentionType.Collection:
|
||||
return <MentionCollection {...props} />;
|
||||
case MentionType.Issue:
|
||||
return (
|
||||
<MentionIssue
|
||||
{...props}
|
||||
onChangeUnfurl={this.handleChangeUnfurl(props)}
|
||||
/>
|
||||
);
|
||||
case MentionType.PullRequest:
|
||||
return (
|
||||
<MentionPullRequest
|
||||
{...props}
|
||||
onChangeUnfurl={this.handleChangeUnfurl(props)}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -149,29 +183,42 @@ export default class Mention extends Node {
|
||||
}
|
||||
|
||||
keys(): Record<string, Command> {
|
||||
const NavigableMention = [
|
||||
MentionType.Collection,
|
||||
MentionType.Document,
|
||||
MentionType.Issue,
|
||||
MentionType.PullRequest,
|
||||
];
|
||||
|
||||
return {
|
||||
Enter: (state) => {
|
||||
const { selection } = state;
|
||||
if (
|
||||
selection instanceof NodeSelection &&
|
||||
selection.node.type.name === this.name &&
|
||||
(selection.node.attrs.type === MentionType.Document ||
|
||||
selection.node.attrs.type === MentionType.Collection)
|
||||
NavigableMention.includes(selection.node.attrs.type)
|
||||
) {
|
||||
const { modelId } = selection.node.attrs;
|
||||
const mentionType = selection.node.attrs.type;
|
||||
|
||||
const linkType =
|
||||
selection.node.attrs.type === MentionType.Document
|
||||
? "doc"
|
||||
: selection.node.attrs.type === MentionType.Collection
|
||||
? "collection"
|
||||
: undefined;
|
||||
let link: string;
|
||||
|
||||
if (!linkType) {
|
||||
return false;
|
||||
if (
|
||||
mentionType === MentionType.Issue ||
|
||||
mentionType === MentionType.PullRequest
|
||||
) {
|
||||
link = selection.node.attrs.href;
|
||||
} else {
|
||||
const { modelId } = selection.node.attrs;
|
||||
|
||||
const linkType =
|
||||
selection.node.attrs.type === MentionType.Document
|
||||
? "doc"
|
||||
: "collection";
|
||||
|
||||
link = `/${linkType}/${modelId}`;
|
||||
}
|
||||
|
||||
this.editor.props.onClickLink?.(`/${linkType}/${modelId}`);
|
||||
this.editor.props.onClickLink?.(link);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -218,4 +265,30 @@ export default class Mention extends Node {
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
handleChangeUnfurl =
|
||||
({ node, getPos }: { node: ProsemirrorNode; getPos: () => number }) =>
|
||||
(unfurl: UnfurlResponse[keyof UnfurlResponse]) => {
|
||||
const { view } = this.editor;
|
||||
const { tr } = view.state;
|
||||
|
||||
const label =
|
||||
unfurl.type === UnfurlResourceType.Issue ||
|
||||
unfurl.type === UnfurlResourceType.PR
|
||||
? unfurl.title
|
||||
: undefined;
|
||||
|
||||
const overrides: Record<string, unknown> = label ? { label } : {};
|
||||
overrides.unfurl = unfurl;
|
||||
|
||||
const pos = getPos();
|
||||
|
||||
if (!isMatch(node.attrs, overrides)) {
|
||||
const transaction = tr.setNodeMarkup(pos, undefined, {
|
||||
...node.attrs,
|
||||
...overrides,
|
||||
});
|
||||
view.dispatch(transaction);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NodeType } from "prosemirror-model";
|
||||
import { EditorState } from "prosemirror-state";
|
||||
import { EditorState, NodeSelection } from "prosemirror-state";
|
||||
import { Primitive } from "utility-types";
|
||||
import { findParentNode } from "./findParentNode";
|
||||
|
||||
@@ -25,12 +25,24 @@ export const isNodeActive =
|
||||
return false;
|
||||
}
|
||||
|
||||
let nodeWithPos;
|
||||
const { from, to } = state.selection;
|
||||
const nodeWithPos = findParentNode(
|
||||
|
||||
if (
|
||||
state.selection instanceof NodeSelection &&
|
||||
state.selection.node.type === type &&
|
||||
state.selection.node.hasMarkup(type, {
|
||||
...state.selection.node.attrs,
|
||||
...attrs,
|
||||
})
|
||||
) {
|
||||
nodeWithPos = { pos: from, node: state.selection.node };
|
||||
}
|
||||
|
||||
nodeWithPos ??= findParentNode(
|
||||
(node) =>
|
||||
node.type === type &&
|
||||
(!attrs ||
|
||||
Object.keys(attrs).every((key) => node.attrs[key] === attrs[key]))
|
||||
(!attrs || node.hasMarkup(type, { ...node.attrs, ...attrs }))
|
||||
)(state.selection);
|
||||
|
||||
if (!nodeWithPos) {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import * as React from "react";
|
||||
|
||||
/**
|
||||
* Hook to check if component is still mounted
|
||||
*
|
||||
* @returns {boolean} true if the component is mounted, false otherwise
|
||||
*/
|
||||
export default function useIsMounted() {
|
||||
const isMounted = React.useRef(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
isMounted.current = true;
|
||||
return () => {
|
||||
isMounted.current = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return React.useCallback(() => isMounted.current, []);
|
||||
}
|
||||
@@ -428,6 +428,7 @@
|
||||
"Create a new doc": "Create a new doc",
|
||||
"{{ userName }} won't be notified, as they do not have access to this document": "{{ userName }} won't be notified, as they do not have access to this document",
|
||||
"Keep as link": "Keep as link",
|
||||
"Mention": "Mention",
|
||||
"Embed": "Embed",
|
||||
"Add column after": "Add column after",
|
||||
"Add column before": "Add column before",
|
||||
@@ -1155,5 +1156,6 @@
|
||||
"You updated {{ timeAgo }}": "You updated {{ timeAgo }}",
|
||||
"{{ user }} updated {{ timeAgo }}": "{{ user }} updated {{ timeAgo }}",
|
||||
"You created {{ timeAgo }}": "You created {{ timeAgo }}",
|
||||
"{{ user }} created {{ timeAgo }}": "{{ user }} created {{ timeAgo }}"
|
||||
"{{ user }} created {{ timeAgo }}": "{{ user }} created {{ timeAgo }}",
|
||||
"Error loading data": "Error loading data"
|
||||
}
|
||||
|
||||
+4
-2
@@ -75,6 +75,8 @@ export enum MentionType {
|
||||
User = "user",
|
||||
Document = "document",
|
||||
Collection = "collection",
|
||||
Issue = "issue",
|
||||
PullRequest = "pull_request",
|
||||
}
|
||||
|
||||
export type PublicEnv = {
|
||||
@@ -416,7 +418,7 @@ export type UnfurlResponse = {
|
||||
/** Issue title */
|
||||
title: string;
|
||||
/** Issue description */
|
||||
description: string;
|
||||
description: string | null;
|
||||
/** Issue's author */
|
||||
author: { name: string; avatarUrl: string };
|
||||
/** Issue's labels */
|
||||
@@ -436,7 +438,7 @@ export type UnfurlResponse = {
|
||||
/** Pull Request title */
|
||||
title: string;
|
||||
/** Pull Request description */
|
||||
description: string;
|
||||
description: string | null;
|
||||
/** Pull Request author */
|
||||
author: { name: string; avatarUrl: string };
|
||||
/** Pull Request status */
|
||||
|
||||
@@ -105,32 +105,32 @@
|
||||
"@smithy/util-utf8" "^2.0.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/client-s3@3.782.0":
|
||||
version "3.782.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.782.0.tgz#ed807a1cd9a3e7a55bf41dfb486da0bbd1f4852d"
|
||||
integrity sha512-V6JR2JAGYQY7J8wk5un5n/ja2nfCUyyoRCF8Du8JL91NGI8i41Mdr/TzuOGwTgFl6RSXb/ge1K1jk30OH4MugQ==
|
||||
"@aws-sdk/client-s3@3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.787.0.tgz#ebb55ec36cd8f0b7e5e89e48c4d1b6ed1f6156dc"
|
||||
integrity sha512-eGLCWkN0NlntJ9yPU6OKUggVS4cFvuZJog+cFg1KD5hniLqz7Y0YRtB4uBxW212fK3XCfddgyscEOEeHaTQQTw==
|
||||
dependencies:
|
||||
"@aws-crypto/sha1-browser" "5.2.0"
|
||||
"@aws-crypto/sha256-browser" "5.2.0"
|
||||
"@aws-crypto/sha256-js" "5.2.0"
|
||||
"@aws-sdk/core" "3.775.0"
|
||||
"@aws-sdk/credential-provider-node" "3.782.0"
|
||||
"@aws-sdk/credential-provider-node" "3.787.0"
|
||||
"@aws-sdk/middleware-bucket-endpoint" "3.775.0"
|
||||
"@aws-sdk/middleware-expect-continue" "3.775.0"
|
||||
"@aws-sdk/middleware-flexible-checksums" "3.775.0"
|
||||
"@aws-sdk/middleware-flexible-checksums" "3.787.0"
|
||||
"@aws-sdk/middleware-host-header" "3.775.0"
|
||||
"@aws-sdk/middleware-location-constraint" "3.775.0"
|
||||
"@aws-sdk/middleware-logger" "3.775.0"
|
||||
"@aws-sdk/middleware-recursion-detection" "3.775.0"
|
||||
"@aws-sdk/middleware-sdk-s3" "3.775.0"
|
||||
"@aws-sdk/middleware-ssec" "3.775.0"
|
||||
"@aws-sdk/middleware-user-agent" "3.782.0"
|
||||
"@aws-sdk/middleware-user-agent" "3.787.0"
|
||||
"@aws-sdk/region-config-resolver" "3.775.0"
|
||||
"@aws-sdk/signature-v4-multi-region" "3.775.0"
|
||||
"@aws-sdk/types" "3.775.0"
|
||||
"@aws-sdk/util-endpoints" "3.782.0"
|
||||
"@aws-sdk/util-endpoints" "3.787.0"
|
||||
"@aws-sdk/util-user-agent-browser" "3.775.0"
|
||||
"@aws-sdk/util-user-agent-node" "3.782.0"
|
||||
"@aws-sdk/util-user-agent-node" "3.787.0"
|
||||
"@aws-sdk/xml-builder" "3.775.0"
|
||||
"@smithy/config-resolver" "^4.1.0"
|
||||
"@smithy/core" "^3.2.0"
|
||||
@@ -167,10 +167,10 @@
|
||||
"@smithy/util-waiter" "^4.0.3"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/client-sso@3.782.0":
|
||||
version "3.782.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.782.0.tgz#072cbb23a90ec6fd53145ff75bef8329a857f362"
|
||||
integrity sha512-5GlJBejo8wqMpSSEKb45WE82YxI2k73YuebjLH/eWDNQeE6VI5Bh9lA1YQ7xNkLLH8hIsb0pSfKVuwh0VEzVrg==
|
||||
"@aws-sdk/client-sso@3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.787.0.tgz#39f1182296b586cb957b449b5f0dabd8f378cf1a"
|
||||
integrity sha512-L8R+Mh258G0DC73ktpSVrG4TT9i2vmDLecARTDR/4q5sRivdDQSL5bUp3LKcK80Bx+FRw3UETIlX6mYMLL9PJQ==
|
||||
dependencies:
|
||||
"@aws-crypto/sha256-browser" "5.2.0"
|
||||
"@aws-crypto/sha256-js" "5.2.0"
|
||||
@@ -178,12 +178,12 @@
|
||||
"@aws-sdk/middleware-host-header" "3.775.0"
|
||||
"@aws-sdk/middleware-logger" "3.775.0"
|
||||
"@aws-sdk/middleware-recursion-detection" "3.775.0"
|
||||
"@aws-sdk/middleware-user-agent" "3.782.0"
|
||||
"@aws-sdk/middleware-user-agent" "3.787.0"
|
||||
"@aws-sdk/region-config-resolver" "3.775.0"
|
||||
"@aws-sdk/types" "3.775.0"
|
||||
"@aws-sdk/util-endpoints" "3.782.0"
|
||||
"@aws-sdk/util-endpoints" "3.787.0"
|
||||
"@aws-sdk/util-user-agent-browser" "3.775.0"
|
||||
"@aws-sdk/util-user-agent-node" "3.782.0"
|
||||
"@aws-sdk/util-user-agent-node" "3.787.0"
|
||||
"@smithy/config-resolver" "^4.1.0"
|
||||
"@smithy/core" "^3.2.0"
|
||||
"@smithy/fetch-http-handler" "^5.0.2"
|
||||
@@ -255,18 +255,18 @@
|
||||
"@smithy/util-stream" "^4.2.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/credential-provider-ini@3.782.0":
|
||||
version "3.782.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.782.0.tgz#4ffd90f6b3b6b34d1dabcba875e5d00fc5da23f1"
|
||||
integrity sha512-wd4KdRy2YjLsE4Y7pz00470Iip06GlRHkG4dyLW7/hFMzEO2o7ixswCWp6J2VGZVAX64acknlv2Q0z02ebjmhw==
|
||||
"@aws-sdk/credential-provider-ini@3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.787.0.tgz#906ece004141462ae695504b6c07d1200688fd6c"
|
||||
integrity sha512-hc2taRoDlXn2uuNuHWDJljVWYrp3r9JF1a/8XmOAZhVUNY+ImeeStylHXhXXKEA4JOjW+5PdJj0f1UDkVCHJiQ==
|
||||
dependencies:
|
||||
"@aws-sdk/core" "3.775.0"
|
||||
"@aws-sdk/credential-provider-env" "3.775.0"
|
||||
"@aws-sdk/credential-provider-http" "3.775.0"
|
||||
"@aws-sdk/credential-provider-process" "3.775.0"
|
||||
"@aws-sdk/credential-provider-sso" "3.782.0"
|
||||
"@aws-sdk/credential-provider-web-identity" "3.782.0"
|
||||
"@aws-sdk/nested-clients" "3.782.0"
|
||||
"@aws-sdk/credential-provider-sso" "3.787.0"
|
||||
"@aws-sdk/credential-provider-web-identity" "3.787.0"
|
||||
"@aws-sdk/nested-clients" "3.787.0"
|
||||
"@aws-sdk/types" "3.775.0"
|
||||
"@smithy/credential-provider-imds" "^4.0.2"
|
||||
"@smithy/property-provider" "^4.0.2"
|
||||
@@ -274,17 +274,17 @@
|
||||
"@smithy/types" "^4.2.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/credential-provider-node@3.782.0":
|
||||
version "3.782.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.782.0.tgz#81a798710d0567b26cd20a105790b49586e68d40"
|
||||
integrity sha512-HZiAF+TCEyKjju9dgysjiPIWgt/+VerGaeEp18mvKLNfgKz1d+/82A2USEpNKTze7v3cMFASx3CvL8yYyF7mJw==
|
||||
"@aws-sdk/credential-provider-node@3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.787.0.tgz#3e5cdafb0fecca25b7430f848cbca85000b25c33"
|
||||
integrity sha512-JioVi44B1vDMaK2CdzqimwvJD3uzvzbQhaEWXsGMBcMcNHajXAXf08EF50JG3ZhLrhhUsT1ObXpbTaPINOhh+g==
|
||||
dependencies:
|
||||
"@aws-sdk/credential-provider-env" "3.775.0"
|
||||
"@aws-sdk/credential-provider-http" "3.775.0"
|
||||
"@aws-sdk/credential-provider-ini" "3.782.0"
|
||||
"@aws-sdk/credential-provider-ini" "3.787.0"
|
||||
"@aws-sdk/credential-provider-process" "3.775.0"
|
||||
"@aws-sdk/credential-provider-sso" "3.782.0"
|
||||
"@aws-sdk/credential-provider-web-identity" "3.782.0"
|
||||
"@aws-sdk/credential-provider-sso" "3.787.0"
|
||||
"@aws-sdk/credential-provider-web-identity" "3.787.0"
|
||||
"@aws-sdk/types" "3.775.0"
|
||||
"@smithy/credential-provider-imds" "^4.0.2"
|
||||
"@smithy/property-provider" "^4.0.2"
|
||||
@@ -304,45 +304,45 @@
|
||||
"@smithy/types" "^4.2.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/credential-provider-sso@3.782.0":
|
||||
version "3.782.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.782.0.tgz#f644884cae368204f750c35d8a61a04d77c02674"
|
||||
integrity sha512-1y1ucxTtTIGDSNSNxriQY8msinilhe9gGvQpUDYW9gboyC7WQJPDw66imy258V6osdtdi+xoHzVCbCz3WhosMQ==
|
||||
"@aws-sdk/credential-provider-sso@3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.787.0.tgz#77ab6c01e4497d7ff2e6c7f081f3d8695744884b"
|
||||
integrity sha512-fHc08bsvwm4+dEMEQKnQ7c1irEQmmxbgS+Fq41y09pPvPh31nAhoMcjBSTWAaPHvvsRbTYvmP4Mf12ZGr8/nfg==
|
||||
dependencies:
|
||||
"@aws-sdk/client-sso" "3.782.0"
|
||||
"@aws-sdk/client-sso" "3.787.0"
|
||||
"@aws-sdk/core" "3.775.0"
|
||||
"@aws-sdk/token-providers" "3.782.0"
|
||||
"@aws-sdk/token-providers" "3.787.0"
|
||||
"@aws-sdk/types" "3.775.0"
|
||||
"@smithy/property-provider" "^4.0.2"
|
||||
"@smithy/shared-ini-file-loader" "^4.0.2"
|
||||
"@smithy/types" "^4.2.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/credential-provider-web-identity@3.782.0":
|
||||
version "3.782.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.782.0.tgz#5dbab53a7b49dcf8390d71415855e78b911a4742"
|
||||
integrity sha512-xCna0opVPaueEbJoclj5C6OpDNi0Gynj+4d7tnuXGgQhTHPyAz8ZyClkVqpi5qvHTgxROdUEDxWqEO5jqRHZHQ==
|
||||
"@aws-sdk/credential-provider-web-identity@3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.787.0.tgz#d492d1f4a90b70f3a71a65f11b8d3ef79fb2759e"
|
||||
integrity sha512-SobmCwNbk6TfEsF283mZPQEI5vV2j6eY5tOCj8Er4Lzraxu9fBPADV+Bib2A8F6jlB1lMPJzOuDCbEasSt/RIw==
|
||||
dependencies:
|
||||
"@aws-sdk/core" "3.775.0"
|
||||
"@aws-sdk/nested-clients" "3.782.0"
|
||||
"@aws-sdk/nested-clients" "3.787.0"
|
||||
"@aws-sdk/types" "3.775.0"
|
||||
"@smithy/property-provider" "^4.0.2"
|
||||
"@smithy/types" "^4.2.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/crt-loader@3.782.0":
|
||||
version "3.782.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/crt-loader/-/crt-loader-3.782.0.tgz#a2f8d34a22155d9730426cb727694c54dc0299c4"
|
||||
integrity sha512-S3fDfZfVhGBvyseJEIcVwa7pumyjqhMKIGHYzcxpthBptJ9gcpYgyHSC6Z7hguQfsfurawe7MNvFnx8O6oLFLA==
|
||||
"@aws-sdk/crt-loader@3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/crt-loader/-/crt-loader-3.787.0.tgz#570c7e3cc79d20b2391beb1dafe2215343a9ac29"
|
||||
integrity sha512-NQWFDkYF/lzz2m3icdVr+a0Ua/fN4dij3GPwU+Hr/nzrFR6z7txG3U4m2zkSELJ0PDT4k/1NsgmnQlpyxg0NDg==
|
||||
dependencies:
|
||||
"@aws-sdk/util-user-agent-node" "3.782.0"
|
||||
"@aws-sdk/util-user-agent-node" "3.787.0"
|
||||
aws-crt "^1.24.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/lib-storage@3.782.0":
|
||||
version "3.782.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/lib-storage/-/lib-storage-3.782.0.tgz#721fd62f01805a9eed661f19f556dc7c6f17a70e"
|
||||
integrity sha512-UQYnIzpBReLko2XhDgG/rWpoHTWv4/zqUNl4XJXZRo9akLzrxGKtPrp5nJ4OLUkH3tIm1cvmI3XlSjHUW/OxWw==
|
||||
"@aws-sdk/lib-storage@3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/lib-storage/-/lib-storage-3.787.0.tgz#fb7af2a869c31948073e7480ed1003c9755d35c4"
|
||||
integrity sha512-iIMbmd9uASD3KFfGeH/OeVo4oxAeqbuXDmhoVEJb4M0hZ4yJ2o9v1V0TEaHwIXcrdlc0H8rCpd9opmV74MBxrA==
|
||||
dependencies:
|
||||
"@smithy/abort-controller" "^4.0.2"
|
||||
"@smithy/middleware-endpoint" "^4.1.0"
|
||||
@@ -375,10 +375,10 @@
|
||||
"@smithy/types" "^4.2.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/middleware-flexible-checksums@3.775.0":
|
||||
version "3.775.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.775.0.tgz#d76a5eabb13ddc3d29b834335387ebe321e0291e"
|
||||
integrity sha512-OmHLfRIb7IIXsf9/X/pMOlcSV3gzW/MmtPSZTkrz5jCTKzWXd7eRoyOJqewjsaC6KMAxIpNU77FoAd16jOZ21A==
|
||||
"@aws-sdk/middleware-flexible-checksums@3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.787.0.tgz#bc887dcfd0193a41eef6b58c18c682478a108b07"
|
||||
integrity sha512-X71qEwWoixFmwowWzlPoZUR3u1CWJ7iAzU0EzIxqmPhQpQJLFmdL1+SRjqATynDPZQzLs1a5HBtPT++EnZ+Quw==
|
||||
dependencies:
|
||||
"@aws-crypto/crc32" "5.2.0"
|
||||
"@aws-crypto/crc32c" "5.2.0"
|
||||
@@ -461,23 +461,23 @@
|
||||
"@smithy/types" "^4.2.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/middleware-user-agent@3.782.0":
|
||||
version "3.782.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.782.0.tgz#60e47e365a39cfa64aa81c3c881896face74da45"
|
||||
integrity sha512-i32H2R6IItX+bQ2p4+v2gGO2jA80jQoJO2m1xjU9rYWQW3+ErWy4I5YIuQHTBfb6hSdAHbaRfqPDgbv9J2rjEg==
|
||||
"@aws-sdk/middleware-user-agent@3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.787.0.tgz#3d657c0ba1aec72bca079f4691ba20f25569fcfc"
|
||||
integrity sha512-Lnfj8SmPLYtrDFthNIaNj66zZsBCam+E4XiUDr55DIHTGstH6qZ/q6vg0GfbukxwSmUcGMwSR4Qbn8rb8yd77g==
|
||||
dependencies:
|
||||
"@aws-sdk/core" "3.775.0"
|
||||
"@aws-sdk/types" "3.775.0"
|
||||
"@aws-sdk/util-endpoints" "3.782.0"
|
||||
"@aws-sdk/util-endpoints" "3.787.0"
|
||||
"@smithy/core" "^3.2.0"
|
||||
"@smithy/protocol-http" "^5.1.0"
|
||||
"@smithy/types" "^4.2.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/nested-clients@3.782.0":
|
||||
version "3.782.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.782.0.tgz#73f56fc4d22e1be342e00b7eba9de4d192521a05"
|
||||
integrity sha512-QOYC8q7luzHFXrP0xYAqBctoPkynjfV0r9dqntFu4/IWMTyC1vlo1UTxFAjIPyclYw92XJyEkVCVg9v/nQnsUA==
|
||||
"@aws-sdk/nested-clients@3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.787.0.tgz#e8a5a6e7d0b599a7f9f15b900d3223ad080b0a81"
|
||||
integrity sha512-xk03q1xpKNHgbuo+trEf1dFrI239kuMmjKKsqLEsHlAZbuFq4yRGMlHBrVMnKYOPBhVFDS/VineM991XI52fKg==
|
||||
dependencies:
|
||||
"@aws-crypto/sha256-browser" "5.2.0"
|
||||
"@aws-crypto/sha256-js" "5.2.0"
|
||||
@@ -485,12 +485,12 @@
|
||||
"@aws-sdk/middleware-host-header" "3.775.0"
|
||||
"@aws-sdk/middleware-logger" "3.775.0"
|
||||
"@aws-sdk/middleware-recursion-detection" "3.775.0"
|
||||
"@aws-sdk/middleware-user-agent" "3.782.0"
|
||||
"@aws-sdk/middleware-user-agent" "3.787.0"
|
||||
"@aws-sdk/region-config-resolver" "3.775.0"
|
||||
"@aws-sdk/types" "3.775.0"
|
||||
"@aws-sdk/util-endpoints" "3.782.0"
|
||||
"@aws-sdk/util-endpoints" "3.787.0"
|
||||
"@aws-sdk/util-user-agent-browser" "3.775.0"
|
||||
"@aws-sdk/util-user-agent-node" "3.782.0"
|
||||
"@aws-sdk/util-user-agent-node" "3.787.0"
|
||||
"@smithy/config-resolver" "^4.1.0"
|
||||
"@smithy/core" "^3.2.0"
|
||||
"@smithy/fetch-http-handler" "^5.0.2"
|
||||
@@ -530,12 +530,12 @@
|
||||
"@smithy/util-middleware" "^4.0.2"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/s3-presigned-post@3.782.0":
|
||||
version "3.782.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/s3-presigned-post/-/s3-presigned-post-3.782.0.tgz#bf6fd60c20a511e6e039c6740f1a80a078d2469e"
|
||||
integrity sha512-JlgZ3wZ3wId0zT0hiEkL6GapAWWh6m4P3uJsDWpcSv1yOA+9bqA3fLcxofnNodL2/7fxam2elI+jVvx/wXIqlQ==
|
||||
"@aws-sdk/s3-presigned-post@3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/s3-presigned-post/-/s3-presigned-post-3.787.0.tgz#06b73ff35afc165a3208fc9a858b12a1e53d5148"
|
||||
integrity sha512-/mM9VvdjC5fBa1WqlygmjTZ4R2fNsMjSc03JfdtcLWDShFZ61gungGZVSvsBbwh/YcVe1sKOnJz4qBQxl/ey8g==
|
||||
dependencies:
|
||||
"@aws-sdk/client-s3" "3.782.0"
|
||||
"@aws-sdk/client-s3" "3.787.0"
|
||||
"@aws-sdk/types" "3.775.0"
|
||||
"@aws-sdk/util-format-url" "3.775.0"
|
||||
"@smithy/middleware-endpoint" "^4.1.0"
|
||||
@@ -545,10 +545,10 @@
|
||||
"@smithy/util-utf8" "^4.0.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/s3-request-presigner@3.782.0":
|
||||
version "3.782.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.782.0.tgz#bc71d5b9377f5c9e4170368da56745bb5164e466"
|
||||
integrity sha512-Er8hdjc9zkxTh15MjdnMYggtUrGknDiuD1FwdW035kn/kwWop587G9rnRa1crhmyKRjLMn0Ki3fsyFUm/943XA==
|
||||
"@aws-sdk/s3-request-presigner@3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.787.0.tgz#1a8eadf330446e53cdca11fa0c08ae3d45a00f9c"
|
||||
integrity sha512-WBm0AS3RRURNN0yjYXHaiI692boVwWXGt3RLdI7tYBX58E1Zb5nzC8rlk81O9Xe7ZTgTC1KCr8y4+jcBD+zwJg==
|
||||
dependencies:
|
||||
"@aws-sdk/signature-v4-multi-region" "3.775.0"
|
||||
"@aws-sdk/types" "3.775.0"
|
||||
@@ -559,12 +559,12 @@
|
||||
"@smithy/types" "^4.2.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/signature-v4-crt@^3.782.0":
|
||||
version "3.782.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-crt/-/signature-v4-crt-3.782.0.tgz#c90ef0f30bd73a0b9fcdaf31049a2adc7c15b405"
|
||||
integrity sha512-QuVSoJITPZxK0lBA2uIDQFjvLdqEvjXrnLYGpRIvIoHdL4RtcDoad9NRKP6Lb34AoB4tubVF6g85mNNOVsqVAw==
|
||||
"@aws-sdk/signature-v4-crt@^3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-crt/-/signature-v4-crt-3.787.0.tgz#6932f0f437536fc528d4cc45a79daf08819d64a3"
|
||||
integrity sha512-TATbx7B/54UIyLawAM0eTkQfnfn9KlEXV1jymniEHQtsfL68VND9/uFdOp51Ob9eTo5Q3qghH0RMHZaOpRVuGA==
|
||||
dependencies:
|
||||
"@aws-sdk/crt-loader" "3.782.0"
|
||||
"@aws-sdk/crt-loader" "3.787.0"
|
||||
"@aws-sdk/signature-v4-multi-region" "3.775.0"
|
||||
"@aws-sdk/types" "3.775.0"
|
||||
"@smithy/querystring-parser" "^4.0.2"
|
||||
@@ -585,12 +585,12 @@
|
||||
"@smithy/types" "^4.2.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/token-providers@3.782.0":
|
||||
version "3.782.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.782.0.tgz#a6a12f9358f8897f5d1af311da60f90a7d384eac"
|
||||
integrity sha512-4tPuk/3+THPrzKaXW4jE2R67UyGwHLFizZ47pcjJWbhb78IIJAy94vbeqEQ+veS84KF5TXcU7g5jGTXC0D70Wg==
|
||||
"@aws-sdk/token-providers@3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.787.0.tgz#18c761fb21ee25c8c3a35703876f0c733b4ae743"
|
||||
integrity sha512-d7/NIqxq308Zg0RPMNrmn0QvzniL4Hx8Qdwzr6YZWLYAbUSvZYS2ppLR3BFWSkV6SsTJUx8BuDaj3P8vttkrog==
|
||||
dependencies:
|
||||
"@aws-sdk/nested-clients" "3.782.0"
|
||||
"@aws-sdk/nested-clients" "3.787.0"
|
||||
"@aws-sdk/types" "3.775.0"
|
||||
"@smithy/property-provider" "^4.0.2"
|
||||
"@smithy/shared-ini-file-loader" "^4.0.2"
|
||||
@@ -612,10 +612,10 @@
|
||||
dependencies:
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/util-endpoints@3.782.0":
|
||||
version "3.782.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.782.0.tgz#3b8de42c5fe951337d070432d4a5eba166c07bb7"
|
||||
integrity sha512-/RJOAO7o7HI6lEa4ASbFFLHGU9iPK876BhsVfnl54MvApPVYWQ9sHO0anOUim2S5lQTwd/6ghuH3rFYSq/+rdw==
|
||||
"@aws-sdk/util-endpoints@3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.787.0.tgz#1398f0bd87f19e615ae920c73e16d9d5e5cb76d1"
|
||||
integrity sha512-fd3zkiOkwnbdbN0Xp9TsP5SWrmv0SpT70YEdbb8wAj2DWQwiCmFszaSs+YCvhoCdmlR3Wl9Spu0pGpSAGKeYvQ==
|
||||
dependencies:
|
||||
"@aws-sdk/types" "3.775.0"
|
||||
"@smithy/types" "^4.2.0"
|
||||
@@ -649,12 +649,12 @@
|
||||
bowser "^2.11.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/util-user-agent-node@3.782.0":
|
||||
version "3.782.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.782.0.tgz#795f2c22882f1ddbbe83bd324f0ceac1c4b07c89"
|
||||
integrity sha512-dMFkUBgh2Bxuw8fYZQoH/u3H4afQ12VSkzEi//qFiDTwbKYq+u+RYjc8GLDM6JSK1BShMu5AVR7HD4ap1TYUnA==
|
||||
"@aws-sdk/util-user-agent-node@3.787.0":
|
||||
version "3.787.0"
|
||||
resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.787.0.tgz#58e63e99586cde1c1314f74b94596780321442f5"
|
||||
integrity sha512-mG7Lz8ydfG4SF9e8WSXiPQ/Lsn3n8A5B5jtPROidafi06I3ckV2WxyMLdwG14m919NoS6IOfWHyRGSqWIwbVKA==
|
||||
dependencies:
|
||||
"@aws-sdk/middleware-user-agent" "3.782.0"
|
||||
"@aws-sdk/middleware-user-agent" "3.787.0"
|
||||
"@aws-sdk/types" "3.775.0"
|
||||
"@smithy/node-config-provider" "^4.0.2"
|
||||
"@smithy/types" "^4.2.0"
|
||||
@@ -13122,10 +13122,10 @@ prosemirror-model@^1.0.0, prosemirror-model@^1.20.0, prosemirror-model@^1.21.0,
|
||||
dependencies:
|
||||
orderedmap "^2.0.0"
|
||||
|
||||
prosemirror-schema-list@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.4.1.tgz#78b8d25531db48ca9688836dbde50e13ac19a4a1"
|
||||
integrity sha512-jbDyaP/6AFfDfu70VzySsD75Om2t3sXTOdl5+31Wlxlg62td1haUpty/ybajSfJ1pkGadlOfwQq9kgW5IMo1Rg==
|
||||
prosemirror-schema-list@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz#5869c8f749e8745c394548bb11820b0feb1e32f5"
|
||||
integrity sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==
|
||||
dependencies:
|
||||
prosemirror-model "^1.0.0"
|
||||
prosemirror-state "^1.0.0"
|
||||
@@ -13492,10 +13492,10 @@ react-virtual@^2.8.2:
|
||||
dependencies:
|
||||
"@reach/observe-rect" "^1.1.0"
|
||||
|
||||
react-virtualized-auto-sizer@^1.0.21:
|
||||
version "1.0.25"
|
||||
resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.25.tgz#b13cbc528ac200be2bd1ffa40c8bb19bcc60ac3f"
|
||||
integrity sha512-YHsksEGDfsHbHuaBVDYwJmcktblcHGafz4ZVuYPQYuSHMUGjpwmUCrAOcvMSGMwwk1eFWj1M/1GwYpNPuyhaBg==
|
||||
react-virtualized-auto-sizer@^1.0.26:
|
||||
version "1.0.26"
|
||||
resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.26.tgz#e9470ef6a778dc4f1d5fd76305fa2d8b610c357a"
|
||||
integrity sha512-CblNyiNVw2o+hsa5/49NH2ogGxZ+t+3aweRvNSq7TVjDIlwk7ir4lencEg5HxHeSzwNarSkNkiu0qJSOXtxm5A==
|
||||
|
||||
react-waypoint@^10.3.0:
|
||||
version "10.3.0"
|
||||
@@ -15263,10 +15263,10 @@ typedarray@^0.0.6:
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
|
||||
|
||||
typescript@^5.0.4, typescript@^5.7.3:
|
||||
version "5.8.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.2.tgz#8170b3702f74b79db2e5a96207c15e65807999e4"
|
||||
integrity sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==
|
||||
typescript@^5.0.4, typescript@^5.8.3:
|
||||
version "5.8.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e"
|
||||
integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==
|
||||
|
||||
uc.micro@^1.0.1, uc.micro@^1.0.5:
|
||||
version "1.0.6"
|
||||
|
||||
Reference in New Issue
Block a user