Skip to content

add standalone insert citation dialog for hosts without an editor - #1126

Merged
vezwork merged 4 commits into
quarto-dev:mainfrom
kevinushey:feature/standalone-insert-citation
Nov 5, 2026
Merged

vezwork merged 4 commits into
quarto-dev:mainfrom
kevinushey:feature/standalone-insert-citation

Conversation

@kevinushey

Copy link
Copy Markdown
Contributor

This makes the visual editor's Insert Citation dialog usable without an editor instance, so RStudio can offer it in its plain-text source editor (rstudio/rstudio#7876, a long-standing request).

The dialog itself never needed the ProseMirror document. It only read the YAML front matter to find bibliography:, references: and zotero:. InsertCitationCommand then applied the result with a transaction: the [@key] text and, when a new bibliography file was created, a bibliography: line in the front matter. This PR separates those two seams from the rest.

Changes

  • BibliographyManager and its providers accept either a document or plain YAML blocks (YamlBlockSource). Existing callers keep passing the doc and are unchanged.
  • ensureSourcesInBibliography is split. The new writeSourcesToBibliography does the server writes and the Better BibTeX warning, and the wrapper still adds bibliography: to the doc in the same transaction.
  • showInsertCitationDialog takes a YamlBlockSource. The unused doc parameter is removed from the panel and the bibliography source panel.
  • A new InsertCitationDialog class (editor/editor-citation.ts) is exported from editor and from the panmirror bundle. show({ yaml, theme?, selectionKey? }) returns { citationIds, intextCitationStyle, bibliographyFile?, selectionKey? }. The host inserts the citation text and declares bibliographyFile in its front matter when set.
  • editorUIWithDefaultImages is extracted from Editor.create. This also corrects the omni_insert and citations merges, which spread the whole images object instead of the nested one. editor-ui passes the full defaults, so its images are unchanged.
  • ensureTheme applies the default theme CSS when no editor has applied a theme yet, since the panel relies on the pm-* color classes.

Verification

tsc --noEmit passes for apps/panmirror and apps/vscode-editor, and eslint passes on the changed files.

Standalone dialog. I loaded the built bundle in headless Chromium with a stub host and server and drove the dialog:

Scenario Result Bibliography write
Existing local source, doc has no bibliography: citationIds: ["smith2020"], bibliographyFile: "references.bib" none, already present
From DOI, doc declares refs.bib citationIds: ["rivera2021"], no bibliographyFile BibTeX entry appended to refs.bib
Cancel null none

Visual editor, before and after. In an RStudio development server, I inserted an R Package citation from the visual editor's Insert Citation dialog in a new .md document. I ran it once with the current bundle and once with this branch's bundle served in its place:

[@knitr] inserted bibliography: references.bib added references.bib has the knitr entry
before yes yes yes
after yes yes yes

Notes

  • There's no apps/vscode/CHANGELOG.md entry, because nothing changes for VS Code or Positron users. The new export is for RStudio. Happy to add one if you'd prefer.
  • packages/editor-collab and packages/editor-codemirror type-check editor/src without JSX enabled, so their tsc already reports TS6142 for the .tsx modules it reaches. The new file adds one more of the same, and neither package has a tsc script or CI step.

The Insert Citation dialog only used the ProseMirror document to read the
YAML front matter, and the command applied the result with a transaction.
Let the bibliography manager accept YAML blocks directly, split the
bibliography writes out of ensureSourcesInBibliography, and export an
InsertCitationDialog class (also from the panmirror bundle) that returns the
citation ids and any bibliography file the host should declare, so plain-text
hosts can offer the dialog.

Addresses rstudio/rstudio#7876.
@posit-snyk-bot

posit-snyk-bot commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@vezwork vezwork left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good with small comments

Comment on lines +158 to +164
// parse yaml blocks from their code (with or w/o enclosing ---)
export function parseYamlBlocks(yamlBlocks: string[]): YamlBlock[] {
return yamlBlocks.map(yamlText => {
const yamlCode = stripYamlDelimeters(yamlText);
return { yamlCode, yaml: parseYaml(yamlCode) };
});
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// parse yaml blocks from their code (with or w/o enclosing ---)
export function parseYamlBlocks(yamlBlocks: string[]): YamlBlock[] {
return yamlBlocks.map(yamlText => {
const yamlCode = stripYamlDelimeters(yamlText);
return { yamlCode, yaml: parseYaml(yamlCode) };
});
}
export const parseYamlBlocks(yamlBlocks: string[]): YamlBlock[] => yamlBlocks.map(parseYamlText);
// parse a yaml block from its code (with or w/o enclosing ---)
const parseYamlText(yamlText: string): YamlBlock => {
const yamlCode = stripYamlDelimeters(yamlText);
return { yamlCode, yaml: parseYaml(yamlCode) };
};

nitpick readability suggestion

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Done in 78cc65f, though as a function declaration to match the rest of this file (the suggestion also dropped an =). parseYamlNodes now shares the same helper, so the strip-and-parse step lives in one place.

Comment on lines -819 to -822

if (!bibliographyFile.isProject) {
ensureBibliographyFileForDoc(tr, bibliographyFile.displayPath);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this removed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It moved rather than went away. The old code called ensureBibliographyFileForDoc from inside the per-source loop, once per source with an id. That loop now lives in writeSourcesToBibliography, which only does the server writes so the standalone dialog (no document, no transaction) can share it. The document edit is in the wrapper ensureSourcesInBibliography just above, at line 770: one call after the write succeeds, when the bibliography is not project-level and any source has an id. Since the function returns early once the document already declares the bibliography, calling it once after the loop gives the same result as calling it per source. The standalone host gets bibliographyFile back in the result instead and declares it in its own front matter.

@vezwork

vezwork commented Nov 5, 2026

Copy link
Copy Markdown
Member

Thanks! Let's merge!

@vezwork
vezwork merged commit 152624f into quarto-dev:main Nov 5, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants