Claude Code and I spent some time building a plugin that adds narrative citations – e.g., "Smith and Jones (2024)" rather than "(Smith & Jones, 2024)" – by setting citeproc-js's cluster-level composite mode from the integration layer. It's currently pre-alpha, but works: correct APA 7 output including the "&"/"and" distinction, surviving refresh and style changes, tested against Zotero 10.0.2 with Word on macOS. The code and design notes are at
https://github.com/jderrfuss/zotero-narrative-citations.
Claude had a question about the citation JSON for a future mode natively supporting narrative citations:
citeproc-js has had composite mode and <intext> since 1.1.225, and processCitationCluster() already honours citation.properties.mode. What's missing is only that nothing in Zotero ever sets a mode, and there's nowhere in the stored citation to remember that the user asked for one. That last part is the one decision I can't sensibly make on my own:
When Zotero supports author-only or narrative citations natively, what do you expect the citation JSON in the document field to look like?I ask because there's already a hint in the tree that I can't interpret. Citation.prototype.toJSON() decides what is written to the field using two allowlists: saveProperties, for properties of the citation as a whole, and saveCitationItemKeys, for each item within it. The per-item list already includes "author-only":
const saveCitationItemKeys = ["locator", "label", "suppress-author",
"author-only", "prefix", "suffix", "ignoreRetraction"];
As far as I can tell, nothing in Zotero ever sets that key. The only other place it appears is citeprocRsBridge.js, which reads it. So the slot exists, is saved in the field and is passed on to citeproc, but nothing ever fills it. That could mean it's reserved for exactly this feature, or it could just be there for completeness against the CSL citation schema.
What I've done in the meantime, so there's something concrete to react to: the flag is stored on the citation as a whole, as citation.properties.mode = "composite". That is citeproc's own key, and "composite" is its own value for "Author (Year)" (alongside "author-only" and "suppress-author"). The plugin adds mode to the citation-level allowlist, saveProperties; the per-item list above is left unchanged.
I stored it on the citation rather than reusing the per-item author-only, for two reasons. First, narrative form describes how the whole citation is rendered, not one item within it. Second, the two fail differently on a Zotero install without the plugin, such as a co-author's. author-only is on the per-item allowlist, so stock Zotero saves it and passes it to citeproc, where CSL.getCitationCluster sets suppress_decorations and stops after the first item: the co-author sees bare, unbracketed, truncated text. mode is not on the citation-level allowlist, so stock Zotero drops it and the citation comes out as an ordinary parenthetical one, which seems like the right way to fail.
If you'd prefer a different shape, including one not yet implemented, I'd much rather write documents in the format Zotero will eventually read than invent a private one and leave users stranded in it. And if this hasn't been decided yet: would you have any objection to properties.mode being used, or would you expect a per-item representation instead?
For context on scope: it's restricted to single-item citations (a narrative citation of several works is ill-defined), it synthesizes an <intext> element at runtime from the active style's own name macro rather than shipping modified styles, and it falls back silently to an ordinary parenthetical citation for styles that can't support narrative citations: numeric, note, and author-page styles like MLA, which have no year to put in the parentheses. It needs no changes to Zotero itself.