diff --git a/.gitignore b/.gitignore index 92f64ba49..476c2dd24 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,8 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +.pem + .idea/ .vercel test-results/ diff --git a/examples/01-basic/01-minimal/App.tsx b/examples/01-basic/01-minimal/App.tsx index a3b92bafd..ac4d8909b 100644 --- a/examples/01-basic/01-minimal/App.tsx +++ b/examples/01-basic/01-minimal/App.tsx @@ -1,12 +1,105 @@ +import { + BlockNoteSchema, + defaultBlockSpecs, + defaultInlineContentSpecs, + filterSuggestionItems, +} from "@blocknote/core"; import "@blocknote/core/fonts/inter.css"; import { BlockNoteView } from "@blocknote/mantine"; import "@blocknote/mantine/style.css"; -import { useCreateBlockNote } from "@blocknote/react"; +import { + getBibliographyReactSlashMenuItems, + getDefaultReactSlashMenuItems, + ReactBibliographyBlockContent, + ReactReferenceInlineContent, + SuggestionMenuController, + useCreateBlockNote, + useSingleBibliographyBlock, +} from "@blocknote/react"; + +import "./styles.css"; export default function App() { - // Creates a new editor instance. - const editor = useCreateBlockNote(); + const schema = BlockNoteSchema.create({ + blockSpecs: { + ...defaultBlockSpecs, + bibliography: ReactBibliographyBlockContent, + }, + inlineContentSpecs: { + ...defaultInlineContentSpecs, + reference: ReactReferenceInlineContent, + }, + }); + + const editor = useCreateBlockNote({ + schema, + initialContent: [ + { + type: "paragraph", + content: "Welcome to this demo!", + }, + { + type: "paragraph", + content: [ + { + type: "text", + text: "Woah, you can add a reference like this: ", + styles: {}, + }, + { + type: "reference", + props: { + doi: "10.1093/ajae/aaq063", + // doi: "", + }, + }, + { + type: "text", + text: " <- This is an example reference", + styles: {}, + }, + ], + }, + { + type: "paragraph", + content: + "Press the '@' key to open the references menu and add another", + }, + { + type: "bibliography", + props: { + bibTexJSON: JSON.stringify([ + "10.1093/ajae/aaq063", // Example DOI + "https://doi.org/10.48550/arXiv.2505.23896", // Another example DOI + "https://doi.org/10.48550/arXiv.2505.23900", + "https://doi.org/10.48550/arXiv.2505.23904", + "https://doi.org/10.48550/arXiv.2505.24234", + ]), + }, + }, + { + type: "paragraph", + }, + ], + }); + + useSingleBibliographyBlock(editor); // Renders the editor instance using a React component. - return ; + return ( + + + filterSuggestionItems( + [ + ...getDefaultReactSlashMenuItems(editor), + ...getBibliographyReactSlashMenuItems(editor), + ], + query, + ) + } + /> + + ); } diff --git a/examples/01-basic/01-minimal/main.tsx b/examples/01-basic/01-minimal/main.tsx index 6284417d6..eb7c9b1b0 100644 --- a/examples/01-basic/01-minimal/main.tsx +++ b/examples/01-basic/01-minimal/main.tsx @@ -7,5 +7,5 @@ const root = createRoot(document.getElementById("root")!); root.render( - + , ); diff --git a/examples/01-basic/01-minimal/package.json b/examples/01-basic/01-minimal/package.json index 5179e1057..dc94a9305 100644 --- a/examples/01-basic/01-minimal/package.json +++ b/examples/01-basic/01-minimal/package.json @@ -10,10 +10,10 @@ "preview": "vite preview" }, "dependencies": { - "@blocknote/core": "latest", - "@blocknote/react": "latest", "@blocknote/ariakit": "latest", + "@blocknote/core": "latest", "@blocknote/mantine": "latest", + "@blocknote/react": "latest", "@blocknote/shadcn": "latest", "react": "^18.3.1", "react-dom": "^18.3.1" @@ -24,4 +24,4 @@ "@vitejs/plugin-react": "^4.3.1", "vite": "^5.3.4" } -} \ No newline at end of file +} diff --git a/examples/01-basic/01-minimal/styles.css b/examples/01-basic/01-minimal/styles.css new file mode 100644 index 000000000..32757d586 --- /dev/null +++ b/examples/01-basic/01-minimal/styles.css @@ -0,0 +1,9 @@ +.floating { + background-color: black; + padding: 5px 10px; + border-radius: 5px; +} + +.reference-panel { + position: absolute; +} diff --git a/packages/core/src/blocks/defaultBlockTypeGuards.ts b/packages/core/src/blocks/defaultBlockTypeGuards.ts index 5db988da9..b66fa5a81 100644 --- a/packages/core/src/blocks/defaultBlockTypeGuards.ts +++ b/packages/core/src/blocks/defaultBlockTypeGuards.ts @@ -1,9 +1,11 @@ import { CellSelection } from "prosemirror-tables"; import type { BlockNoteEditor } from "../editor/BlockNoteEditor.js"; import { + BlockConfig, BlockFromConfig, BlockSchema, FileBlockConfig, + InlineContentConfig, InlineContentSchema, StyleSchema, } from "../schema/index.js"; @@ -31,6 +33,20 @@ export function checkDefaultBlockTypeInSchema< ); } +export function checkBlockTypeInSchema< + BlockType extends string, + Config extends BlockConfig, +>( + blockType: BlockType, + blockConfig: Config, + editor: BlockNoteEditor, +): editor is BlockNoteEditor<{ [T in BlockType]: Config }, any, any> { + return ( + blockType in editor.schema.blockSchema && + editor.schema.blockSchema[blockType] === blockConfig + ); +} + export function checkDefaultInlineContentTypeInSchema< InlineContentType extends keyof DefaultInlineContentSchema, B extends BlockSchema, @@ -50,6 +66,20 @@ export function checkDefaultInlineContentTypeInSchema< ); } +export function checkInlineContentTypeInSchema< + InlineContentType extends string, + Config extends InlineContentConfig, +>( + inlineContentType: InlineContentType, + inlineContentConfig: Config, + editor: BlockNoteEditor, +): editor is BlockNoteEditor { + return ( + inlineContentType in editor.schema.inlineContentSchema && + editor.schema.inlineContentSchema[inlineContentType] === inlineContentConfig + ); +} + export function checkBlockIsDefaultType< BlockType extends keyof DefaultBlockSchema, I extends InlineContentSchema, diff --git a/packages/core/src/editor/BlockNoteEditor.ts b/packages/core/src/editor/BlockNoteEditor.ts index 8008312c3..897793ced 100644 --- a/packages/core/src/editor/BlockNoteEditor.ts +++ b/packages/core/src/editor/BlockNoteEditor.ts @@ -540,6 +540,8 @@ export class BlockNoteEditor< protected constructor( protected readonly options: Partial>, ) { + // eslint-disable-next-line no-console + console.log("test"); super(); const anyOpts = options as any; if (anyOpts.onEditorContentChange) { diff --git a/packages/core/src/schema/inlineContent/types.ts b/packages/core/src/schema/inlineContent/types.ts index 6ec87055d..6415a86f6 100644 --- a/packages/core/src/schema/inlineContent/types.ts +++ b/packages/core/src/schema/inlineContent/types.ts @@ -22,6 +22,13 @@ export type InlineContentImplementation = node: Node; }; +export type InlineContentSchemaWithInlineContent< + IType extends string, + C extends InlineContentConfig, +> = { + [k in IType]: C; +}; + // Container for both the config and implementation of InlineContent, // and the type of `implementation` is based on that of the config export type InlineContentSpec = { diff --git a/packages/react/package.json b/packages/react/package.json index 57eda00c1..df3e8ae78 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -59,6 +59,9 @@ }, "dependencies": { "@blocknote/core": "0.31.1", + "@citation-js/core": "^0.7.18", + "@citation-js/plugin-csl": "^0.7.18", + "@citation-js/plugin-doi": "^0.7.18", "@emoji-mart/data": "^1.2.1", "@floating-ui/react": "^0.26.4", "@tiptap/core": "^2.12.0", diff --git a/packages/react/src/blocks/BibliographyBlockContent/BibliographyBlockContent.tsx b/packages/react/src/blocks/BibliographyBlockContent/BibliographyBlockContent.tsx new file mode 100644 index 000000000..58f905e85 --- /dev/null +++ b/packages/react/src/blocks/BibliographyBlockContent/BibliographyBlockContent.tsx @@ -0,0 +1,53 @@ +import { BlockConfig } from "@blocknote/core"; +// @ts-ignore +import { Cite } from "@citation-js/core"; +import "@citation-js/plugin-csl"; +import "@citation-js/plugin-doi"; +import { useState, useEffect } from "react"; + +import { + createReactBlockSpec, + ReactCustomBlockRenderProps, +} from "../../schema/ReactBlockSpec.js"; + +export const bibliographyBlockConfig = { + type: "bibliography", + propSchema: { + bibTexJSON: { + default: "[]", + }, + }, + content: "none", + isSelectable: false, +} as const satisfies BlockConfig; + +export const Bibliography = ( + props: ReactCustomBlockRenderProps, +) => { + const [bibliography, setBibliography] = useState([]); + + useEffect(() => { + async function fetchBibliography() { + const dois: string[] = JSON.parse(props.block.props.bibTexJSON); + const cites = await Promise.all(dois.map((doi) => Cite.async(doi))); + + setBibliography(cites); + } + + fetchBibliography(); + }, [props.block.props.bibTexJSON]); + + return ( +
+

Bibliography

+ {bibliography.map((cite: any) => ( +
{cite.format("bibliography")}
+ ))} +
+ ); +}; + +export const ReactBibliographyBlockContent = createReactBlockSpec( + bibliographyBlockConfig, + { render: Bibliography }, +); diff --git a/packages/react/src/blocks/BibliographyBlockContent/hooks/useSingleBibliographyBlock.tsx b/packages/react/src/blocks/BibliographyBlockContent/hooks/useSingleBibliographyBlock.tsx new file mode 100644 index 000000000..65c4a59b1 --- /dev/null +++ b/packages/react/src/blocks/BibliographyBlockContent/hooks/useSingleBibliographyBlock.tsx @@ -0,0 +1,31 @@ +import { + BlockSchema, + InlineContentSchema, + StyleSchema, + BlockNoteEditor, +} from "@blocknote/core"; +import { useEditorChange } from "../../../hooks/useEditorChange.js"; + +export const useSingleBibliographyBlock = < + B extends BlockSchema, + I extends InlineContentSchema, + S extends StyleSchema, +>( + editor?: BlockNoteEditor, +) => { + useEditorChange((editor) => { + const bibliographyBlockIds: string[] = []; + + editor.forEachBlock((block) => { + if (block.type === "bibliography") { + bibliographyBlockIds.push(block.id); + } + + return true; + }, true); + + if (bibliographyBlockIds.length > 1) { + editor.removeBlocks(bibliographyBlockIds.slice(1)); + } + }, editor); +}; diff --git a/packages/react/src/components/SuggestionMenu/getBibliographyReactSlashMenuItems.tsx b/packages/react/src/components/SuggestionMenu/getBibliographyReactSlashMenuItems.tsx new file mode 100644 index 000000000..fcf8e7a33 --- /dev/null +++ b/packages/react/src/components/SuggestionMenu/getBibliographyReactSlashMenuItems.tsx @@ -0,0 +1,79 @@ +import { + BlockSchema, + InlineContentSchema, + StyleSchema, + BlockNoteEditor, + checkBlockTypeInSchema, + checkInlineContentTypeInSchema, +} from "@blocknote/core"; +import { RiFileListFill, RiLink } from "react-icons/ri"; +import { DefaultReactSuggestionItem } from "./types.js"; +import { referenceInlineContentConfig } from "../../inlineContent/ReferenceInlineContent/ReferenceInlineContent.js"; +import { bibliographyBlockConfig } from "../../blocks/BibliographyBlockContent/BibliographyBlockContent.js"; + +export const getBibliographyReactSlashMenuItems = < + B extends BlockSchema, + I extends InlineContentSchema, + S extends StyleSchema, +>( + editor: BlockNoteEditor, +) => { + const items: DefaultReactSuggestionItem[] = []; + + if ( + checkInlineContentTypeInSchema( + "reference", + referenceInlineContentConfig, + editor, + ) + ) { + items.push({ + title: "Reference", + subtext: "Reference to a bibliography block source", + icon: , + aliases: ["ciataion", "cite", "bib"], + onItemClick: () => { + editor.insertInlineContent([ + { + type: "reference", + }, + ]); + }, + }); + } + + const bibliographyBlockInSchema = checkBlockTypeInSchema( + "bibliography", + bibliographyBlockConfig, + editor, + ); + let bibliographyBlockAlreadyExists = false; + editor.forEachBlock((block) => { + if (block.type === "bibliography") { + bibliographyBlockAlreadyExists = true; + return false; + } + return true; + }); + + if (bibliographyBlockInSchema && !bibliographyBlockAlreadyExists) { + items.push({ + title: "Bibliography", + subtext: "Insert a bibliography block", + icon: , + onItemClick: () => { + editor.insertBlocks( + [ + { + type: "bibliography", + }, + ], + editor.document[editor.document.length - 1], + "after", + ); + }, + }); + } + + return items; +}; diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index a506cdfbe..25ecd58eb 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -6,6 +6,8 @@ export * from "./editor/ComponentsContext.js"; export * from "./i18n/dictionary.js"; export * from "./blocks/AudioBlockContent/AudioBlockContent.js"; +export * from "./blocks/BibliographyBlockContent/BibliographyBlockContent.js"; +export * from "./blocks/BibliographyBlockContent/hooks/useSingleBibliographyBlock.js"; export * from "./blocks/FileBlockContent/FileBlockContent.js"; export * from "./blocks/FileBlockContent/helpers/render/AddFileButton.js"; export * from "./blocks/FileBlockContent/helpers/render/FileBlockWrapper.js"; @@ -18,6 +20,8 @@ export * from "./blocks/ImageBlockContent/ImageBlockContent.js"; export * from "./blocks/PageBreakBlockContent/getPageBreakReactSlashMenuItems.js"; export * from "./blocks/VideoBlockContent/VideoBlockContent.js"; +export * from "./inlineContent/ReferenceInlineContent/ReferenceInlineContent.js"; + export * from "./components/FormattingToolbar/DefaultButtons/AddCommentButton.js"; export * from "./components/FormattingToolbar/DefaultButtons/AddTiptapCommentButton.js"; export * from "./components/FormattingToolbar/DefaultButtons/BasicTextStyleButton.js"; @@ -61,6 +65,7 @@ export * from "./components/SideMenu/DragHandleMenu/DragHandleMenuProps.js"; export * from "./components/SuggestionMenu/SuggestionMenuController.js"; export * from "./components/SuggestionMenu/SuggestionMenuWrapper.js"; export * from "./components/SuggestionMenu/getDefaultReactSlashMenuItems.js"; +export * from "./components/SuggestionMenu/getBibliographyReactSlashMenuItems.js"; export * from "./components/SuggestionMenu/hooks/useCloseSuggestionMenuNoItems.js"; export * from "./components/SuggestionMenu/hooks/useLoadSuggestionMenuItems.js"; export * from "./components/SuggestionMenu/hooks/useSuggestionMenuKeyboardNavigation.js"; diff --git a/packages/react/src/inlineContent/ReferenceInlineContent/ReferenceInlineContent.tsx b/packages/react/src/inlineContent/ReferenceInlineContent/ReferenceInlineContent.tsx new file mode 100644 index 000000000..791a9f534 --- /dev/null +++ b/packages/react/src/inlineContent/ReferenceInlineContent/ReferenceInlineContent.tsx @@ -0,0 +1,237 @@ +import { + Block, + BlockSchemaWithBlock, + InlineContentConfig, + InlineContentSchemaWithInlineContent, +} from "@blocknote/core"; +import { + createReactInlineContentSpec, + ReactCustomInlineContentRenderProps, + useComponentsContext, +} from "@blocknote/react"; +// @ts-ignore +import { Cite } from "@citation-js/core"; +import "@citation-js/plugin-csl"; +import "@citation-js/plugin-doi"; +import { + useClick, + // useDismiss, + useFloating, + useHover, + useInteractions, +} from "@floating-ui/react"; +import { useCallback, useEffect, useState } from "react"; + +import { bibliographyBlockConfig } from "../../blocks/BibliographyBlockContent/BibliographyBlockContent.js"; + +export const referenceInlineContentConfig = { + type: "reference", + propSchema: { + doi: { + default: "", + }, + }, + content: "none", +} satisfies InlineContentConfig; + +const useFloatingHover = () => { + const [isHovered, setIsHovered] = useState(false); + + const { refs, floatingStyles, context } = useFloating({ + open: isHovered, + onOpenChange: setIsHovered, + }); + + const hover = useHover(context); + + const { getReferenceProps, getFloatingProps } = useInteractions([hover]); + + return { + isHovered, + referenceElementProps: { + ref: refs.setReference, + ...getReferenceProps(), + }, + floatingElementProps: { + ref: refs.setFloating, + style: floatingStyles, + ...getFloatingProps(), + }, + }; +}; + +const useFloatingClick = () => { + const [isOpen, setIsOpen] = useState(false); + + const { refs, floatingStyles, context } = useFloating({ + open: isOpen, + onOpenChange: setIsOpen, + }); + + const open = useClick(context); + // const dismiss = useDismiss(context); + + const { getReferenceProps, getFloatingProps } = useInteractions([ + open, + // dismiss, + ]); + + return { + isOpen, + referenceElementProps: { + ref: refs.setReference, + ...getReferenceProps(), + }, + floatingElementProps: { + ref: refs.setFloating, + style: floatingStyles, + ...getFloatingProps(), + }, + }; +}; + +export const Reference = ( + props: ReactCustomInlineContentRenderProps< + typeof referenceInlineContentConfig, + any + >, +) => { + const Components = useComponentsContext()!; + + const referenceDetailsFloating = useFloatingHover(); + const referenceEditFloating = useFloatingClick(); + + const citation = props.inlineContent.props; + + const [newDOI, setNewDOI] = useState(citation.doi); + + const [bibliography, setBibliography] = useState(null); + + useEffect(() => { + Cite.async(props.inlineContent.props.doi).then(setBibliography); + }, [props.inlineContent.props]); + + const applyNewDOI = useCallback(() => { + props.updateInlineContent({ + type: "reference", + props: { + ...citation, + doi: newDOI, + }, + }); + + let bibliographyBlock: + | Block< + BlockSchemaWithBlock<"bibliography", typeof bibliographyBlockConfig>, + InlineContentSchemaWithInlineContent< + "reference", + typeof referenceInlineContentConfig + >, + any + > + | undefined = undefined; + + props.editor.forEachBlock((block) => { + if (block.type === "bibliography") { + bibliographyBlock = block as any; + } + + if (bibliographyBlock) { + return false; + } + + return true; + }); + + if (!bibliographyBlock) { + props.editor.insertBlocks( + [ + { + type: "bibliography", + }, + ], + props.editor.document[props.editor.document.length - 1], + "after", + ); + } + }, [citation, newDOI, props]); + + if (!bibliography) { + return Loading...; + } + + if (!citation.doi) { + return ( + + + {referenceEditFloating.isOpen && ( + { + // Do nothing until we have more tabs + }} + tabs={[ + { + name: "DOI", + tabPanel: ( + + setNewDOI(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + applyNewDOI(); + } + }} + data-test={"embed-input"} + /> + applyNewDOI()} + data-test="embed-input-button" + > + Update Reference + + + ), + }, + ]} + loading={false} + /> + )} + + ); + } + + return ( + + + {bibliography.format("citation")} + + {referenceDetailsFloating.isHovered && ( +
+ {/* FIXME do not use `dangerouslySetInnerHTML` to embed citation */} +
+
+ )} + + ); +}; + +export const ReactReferenceInlineContent = createReactInlineContentSpec( + referenceInlineContentConfig, + { render: Reference }, +); diff --git a/packages/react/src/schema/ReactInlineContentSpec.tsx b/packages/react/src/schema/ReactInlineContentSpec.tsx index 63d81c014..67033ac2d 100644 --- a/packages/react/src/schema/ReactInlineContentSpec.tsx +++ b/packages/react/src/schema/ReactInlineContentSpec.tsx @@ -15,6 +15,7 @@ import { propsToAttributes, StyleSchema, BlockNoteEditor, + InlineContentSchemaWithInlineContent, } from "@blocknote/core"; import { NodeViewProps, @@ -27,19 +28,29 @@ import { FC } from "react"; import { renderToDOMSpec } from "./@util/ReactRenderUtil.js"; // this file is mostly analogoues to `customBlocks.ts`, but for React blocks +export type ReactCustomInlineContentRenderProps< + T extends CustomInlineContentConfig, + S extends StyleSchema, +> = { + inlineContent: InlineContentFromConfig; + updateInlineContent: ( + update: PartialCustomInlineContentFromConfig, + ) => void; + editor: BlockNoteEditor< + any, + InlineContentSchemaWithInlineContent, + S + >; + contentRef: (node: HTMLElement | null) => void; +}; + // extend BlockConfig but use a React render function export type ReactInlineContentImplementation< T extends CustomInlineContentConfig, // I extends InlineContentSchema, S extends StyleSchema, > = { - render: FC<{ - inlineContent: InlineContentFromConfig; - updateInlineContent: ( - update: PartialCustomInlineContentFromConfig, - ) => void; - contentRef: (node: HTMLElement | null) => void; - }>; + render: FC>; // TODO? // toExternalHTML?: FC<{ // block: BlockFromConfig; @@ -133,6 +144,7 @@ export function createReactInlineContentSpec< updateInlineContent={() => { // No-op }} + editor={editor} contentRef={refCB} /> ), @@ -168,6 +180,7 @@ export function createReactInlineContentSpec< > =16.0.0'} + + '@citation-js/date@0.5.1': + resolution: {integrity: sha512-1iDKAZ4ie48PVhovsOXQ+C6o55dWJloXqtznnnKy6CltJBQLIuLLuUqa8zlIvma0ZigjVjgDUhnVaNU1MErtZw==} + engines: {node: '>=10.0.0'} + + '@citation-js/name@0.4.2': + resolution: {integrity: sha512-brSPsjs2fOVzSnARLKu0qncn6suWjHVQtrqSUrnqyaRH95r/Ad4wPF5EsoWr+Dx8HzkCGb/ogmoAzfCsqlTwTQ==} + engines: {node: '>=6'} + + '@citation-js/plugin-csl@0.7.18': + resolution: {integrity: sha512-cJcOdEZurmtIxNj0d4cOERHpVQJB/mN3YPSDNqfI/xTFRN3bWDpFAsaqubPtMO2ZPpoDS+ZGIP1kggbwCfMmlA==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@citation-js/core': ^0.7.0 + + '@citation-js/plugin-doi@0.7.18': + resolution: {integrity: sha512-7ccmhfJJSDUhUqpWxesLAp3m1P5dhnZ4QNMctwJnU41T9vKGF7MXPKqMONSvL5JDZ7o7iWQTj2BFhSmh0euQxw==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@citation-js/core': ^0.7.0 + '@cloudflare/workerd-darwin-64@1.20240129.0': resolution: {integrity: sha512-DfVVB5IsQLVcWPJwV019vY3nEtU88c2Qu2ST5SQxqcGivZ52imagLRK0RHCIP8PK4piSiq90qUC6ybppUsw8eg==} engines: {node: '>=16'} @@ -6106,6 +6139,12 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' + '@floating-ui/react-dom@2.1.3': + resolution: {integrity: sha512-huMBfiU9UnQ2oBwIhgzyIiSpVgvlDstU8CX0AF+wS+KzmYMs0J2a3GwuFHV1Lz+jlrQGeC1fF+Nv0QoumyV0bA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + '@floating-ui/react@0.26.28': resolution: {integrity: sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==} peerDependencies: @@ -9591,6 +9630,9 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} + citeproc@2.4.63: + resolution: {integrity: sha512-68F95Bp4UbgZU/DBUGQn0qV3HDZLCdI9+Bb2ByrTaNJDL5VEm9LqaiNaxljsvoaExSLEXe1/r6n2Z06SCzW3/Q==} + cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} @@ -10657,6 +10699,9 @@ packages: picomatch: optional: true + fetch-ponyfill@7.1.0: + resolution: {integrity: sha512-FhbbL55dj/qdVO3YNK7ZEkshvj3eQ7EuIGV2I6ic/2YiocvyWv+7jg2s4AyS0wdRU75s3tA8ZxI/xPigb0v5Aw==} + fflate@0.7.4: resolution: {integrity: sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==} @@ -12372,6 +12417,15 @@ packages: node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-fetch@2.6.13: + resolution: {integrity: sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -13923,6 +13977,10 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + sync-fetch@0.4.5: + resolution: {integrity: sha512-esiWJ7ixSKGpd9DJPBTC4ckChqdOjIwJfYhVHkcQ2Gnm41323p1TRmEI+esTQ9ppD+b5opps2OTEGTCGX5kF+g==} + engines: {node: '>=14'} + system-architecture@0.1.0: resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} engines: {node: '>=18'} @@ -16328,6 +16386,30 @@ snapshots: '@types/tough-cookie': 4.0.5 tough-cookie: 4.1.4 + '@citation-js/core@0.7.18(encoding@0.1.13)': + dependencies: + '@citation-js/date': 0.5.1 + '@citation-js/name': 0.4.2 + fetch-ponyfill: 7.1.0(encoding@0.1.13) + sync-fetch: 0.4.5(encoding@0.1.13) + transitivePeerDependencies: + - encoding + + '@citation-js/date@0.5.1': {} + + '@citation-js/name@0.4.2': {} + + '@citation-js/plugin-csl@0.7.18(@citation-js/core@0.7.18(encoding@0.1.13))': + dependencies: + '@citation-js/core': 0.7.18(encoding@0.1.13) + '@citation-js/date': 0.5.1 + citeproc: 2.4.63 + + '@citation-js/plugin-doi@0.7.18(@citation-js/core@0.7.18(encoding@0.1.13))': + dependencies: + '@citation-js/core': 0.7.18(encoding@0.1.13) + '@citation-js/date': 0.5.1 + '@cloudflare/workerd-darwin-64@1.20240129.0': optional: true @@ -16812,6 +16894,12 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + '@floating-ui/react-dom@2.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/dom': 1.6.13 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@floating-ui/react@0.26.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -18213,7 +18301,7 @@ snapshots: '@radix-ui/react-popper@1.2.2(@types/react-dom@18.3.5(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/react-dom': 2.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-arrow': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.20)(react@18.3.1) '@radix-ui/react-context': 1.1.1(@types/react@18.3.20)(react@18.3.1) @@ -20833,6 +20921,8 @@ snapshots: ci-info@3.9.0: {} + citeproc@2.4.63: {} + cjs-module-lexer@1.4.3: {} class-variance-authority@0.7.1: @@ -22143,6 +22233,12 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fetch-ponyfill@7.1.0(encoding@0.1.13): + dependencies: + node-fetch: 2.6.13(encoding@0.1.13) + transitivePeerDependencies: + - encoding + fflate@0.7.4: {} fflate@0.8.2: {} @@ -24522,6 +24618,12 @@ snapshots: node-addon-api@7.1.1: {} + node-fetch@2.6.13(encoding@0.1.13): + dependencies: + whatwg-url: 5.0.0 + optionalDependencies: + encoding: 0.1.13 + node-fetch@2.7.0(encoding@0.1.13): dependencies: whatwg-url: 5.0.0 @@ -26338,6 +26440,13 @@ snapshots: symbol-tree@3.2.4: {} + sync-fetch@0.4.5(encoding@0.1.13): + dependencies: + buffer: 5.7.1 + node-fetch: 2.7.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + system-architecture@0.1.0: {} tabbable@6.2.0: {}