chore(release): prepare v0.2.3 for community review feedback

This commit is contained in:
WesternGua 2026-05-13 14:28:28 +08:00
parent 87d23b88b7
commit c574699f3e
15 changed files with 338 additions and 300 deletions

View file

@ -6,6 +6,22 @@
---
## [0.2.3] - 2026-05-13
### 改进
- README 结构调整:`README.md` 现在为英文主文档,并在顶部提供中文文档入口;中文说明迁移到 `README_zh.md`
- 兼容社区审查建议:移除 `styles.css` 中的 `!important``:has()` 选择器,降低样式覆盖副作用与性能风险
- 弹窗/悬浮层兼容性改进:搜索与导出模态框、脚注悬浮层的定时器与窗口引用改为可兼容 popout 场景的实现
- 前端 DOM 构建规范对齐:搜索结果渲染与脚注标记创建改为 Obsidian 友好写法(`createDiv` / `createSpan` 风格)
### 修复
- 解决社区审查中指出的若干未使用符号与类型噪音问题(如设置模块未使用导入)
- 构建链补充 `pnpm.onlyBuiltDependencies``esbuild`),避免受限环境下安装阶段因 build script 审批失败
---
## [0.2.2] - 2026-05-12
### 修复

172
README.md
View file

@ -1,150 +1,148 @@
# Zotero Citations
> 在 Obsidian 中管理 Zotero 引用,支持脚注/尾注模式、Word 风格显示,并一键导出为带格式脚注的 Word 文档。
> Manage Zotero citations in Obsidian with footnote/endnote modes, Word-style display, and one-click export to Word with formatted footnotes.
[English](./README_EN.md)
[简体中文](./README_zh.md)
---
## 功能亮点
## Highlights
- **插入引用** — 调用 Zotero 原生引文选择器或插件内搜索面板,支持页码/段落定位符
- **脚注 & 尾注** — 自由切换脚注模式(`^[引用文本]`)和尾注模式(`[^1]` + 文末定义)
- **Word 风格显示** — 编辑器中脚注标记以上标数字呈现,悬停可查看完整引用并编辑定位符
- **文档首选项** — 动态读取 Zotero 中已安装的 CSL 样式,一键切换引用格式与引用模式
- **参考书目** — 自动汇总当前文档所有引用,生成格式化参考文献列表
- **导出为 Word** — 通过 Pandoc 将 Markdown 转换为 `.docx`,完整保留脚注/尾注结构
- **中英双语界面** — 设置中一键切换
- **Insert citations** — Invokes Zotero's native citation picker or an in-plugin search modal, with support for page/paragraph locators
- **Footnote & endnote** — Freely switch between footnote mode (`^[citation text]`) and endnote mode (`[^1]` + endnote definitions)
- **Word-style display** — Footnote markers render as superscript numbers in the editor; hover to preview the full citation and edit locators
- **Document preferences** — Dynamically reads all CSL styles installed in Zotero, with one-click style and mode switching
- **Bibliography** — Auto-generates a formatted reference list from all citations in the current document
- **Export to Word** — Converts Markdown to `.docx` via Pandoc, preserving footnote/endnote structure
- **Bilingual UI** — Switch between Chinese and English in settings
---
## 前置要求
## Prerequisites
| 组件 | 说明 |
|------|------|
| Obsidian 桌面版 1.5.7+ | 插件仅支持桌面端(`isDesktopOnly: true` |
| [Zotero](https://www.zotero.org/) | 文献管理器,需保持运行 |
| [Better BibTeX](https://github.com/retorquere/zotero-better-bibtex/releases) | Zotero 插件,提供 API 通信 |
| [Pandoc](https://pandoc.org/installing.html)(可选) | 仅导出 Word 时需要 |
| Component | Description |
|-----------|-------------|
| Obsidian Desktop 1.5.7+ | Desktop-only plugin (`isDesktopOnly: true`) |
| [Zotero](https://www.zotero.org/) | Reference manager; should be running |
| [Better BibTeX](https://github.com/retorquere/zotero-better-bibtex/releases) | Zotero plugin that provides the API layer |
| [Pandoc](https://pandoc.org/installing.html) (optional) | Required only for Word export |
---
## 安装
## Installation
1. 从 GitHub Releases 页面下载以下文件:
- `main.js`插件主程序
- `manifest.json`插件清单
- `styles.css`插件样式
2. 将以上文件放入 vault 的 `.obsidian/plugins/zotero-citations/` 目录(如目录不存在则手动创建)
3. 在 Obsidian 设置 → 第三方插件中启用 **Zotero Citations**
4. 确保 Zotero 已打开且 Better BibTeX 已安装
5. (如需导出 Word安装 Pandoc 并确保其在系统 PATH 中
1. Download the following files from the GitHub Releases page:
- `main.js`plugin runtime
- `manifest.json`plugin manifest
- `styles.css`plugin styles
2. Place the files into your vault's `.obsidian/plugins/zotero-citations/` directory (create it if it does not exist)
3. Enable **Zotero Citations** in Obsidian Settings → Community plugins
4. Make sure Zotero is running and Better BibTeX is installed
5. (For Word export) Install Pandoc and ensure it is on your system PATH
---
## 兼容性说明
## Compatibility Note
当前版本主要在 **macOS** 环境下开发与调试。Linux 与 Windows 平台目前尚未充分测试,因此界面显示、窗口聚焦、原生弹窗、导出链路等行为可能存在差异,暂时不能保证完全兼容。
This version has been developed and tested primarily on **macOS**. Linux and Windows have not yet been fully validated, so UI rendering, window focus behavior, native dialogs, and export-related workflows may behave differently and are not guaranteed to be perfectly compatible at this stage.
---
## 披露说明
## Disclosures
- **网络访问**:插件会通过 `127.0.0.1` 本地回环地址与 Zotero / Better BibTeX 通信,不会连接插件自建服务器。
- **外部文件与可执行程序访问**:插件会读取本机 Zotero 样式目录;在部分回退场景下可能复制并读取本机 Zotero 数据库到系统临时目录;导出 Word 时会调用本地 `pandoc`;数据库回退解析时可能调用本地 `sqlite3`;在 macOS 上,为了从 Zotero 选择器返回 Obsidian插件可能调用系统 `osascript`
- **本地数据存储**:插件会在 Obsidian 的插件数据中保存设置项和引用缓存。
- **账户 / 付费 / 广告 / 遥测**:插件不要求登录账户,不含广告,不含内购,也不主动收集遥测数据。
- **源码状态**:插件源码已公开在 GitHub 仓库中,并以 MIT 许可证发布:<https://github.com/WesternGua/obsidian-zotero-citations>
- **Network use**: the plugin talks to Zotero / Better BibTeX over the local loopback address `127.0.0.1`; it does not rely on a plugin-operated remote server.
- **External files and executables**: the plugin reads the local Zotero styles directory; in fallback scenarios it may copy and read the local Zotero database in the system temp directory; for Word export it invokes the local `pandoc` executable; for database fallback parsing it may invoke the local `sqlite3` executable; on macOS it may invoke the system `osascript` command to return focus to Obsidian after the Zotero picker closes.
- **Local data storage**: the plugin stores settings and citation cache data in Obsidian's plugin data storage.
- **Accounts / payments / ads / telemetry**: the plugin does not require an account, does not include ads, does not include in-app payments, and does not intentionally collect telemetry.
- **Source availability**: the plugin source code is published on GitHub under the MIT license: <https://github.com/WesternGua/obsidian-zotero-citations>
---
## 快速上手
## Quick Start
### 1. 插入引用
### 1. Insert a Citation
在命令面板中搜索“插入引用”,或点击标题栏中的插入引用图标。
Search for `Insert citation` in the command palette, or click the citation icon in the title bar.
插件会优先调用 Zotero 原生引文选择器——在弹出窗口中搜索并选择条目,可填写页码等定位符,点击右上角 ✔ 确认插入。
The plugin will first try to open Zotero's native citation picker — search for items, add a page number or other locator, and confirm with the checkmark button.
引用插入后以脚注或尾注形式存在(取决于当前引用格式模式设置)。
Inserted citations appear as footnotes or endnotes depending on your current citation mode setting.
> **注意**
> - 插件会在脚注正文开头写入 `<!-- zotero:ITEMKEY:locator -->` 形式的隐藏元数据,请勿手动删除,否则插件无法识别该引用。
> - 定稿后如需移除插件元数据,可执行“解除引用链接”(不可逆),插件会删除隐藏元数据并保留可见引用文本。
> **Note**:
> - The plugin writes hidden metadata (`<!-- zotero:ITEMKEY:locator -->`) at the beginning of each note. Do not remove it manually, or the plugin will not be able to track the citation.
> - When you are ready to finalize, run `Unlink citations` (irreversible) to strip the hidden metadata while keeping the visible citation text.
![insert-citation-preview](assets/screenshots/insert-citation-preview.png)
### 2. 悬停编辑定位符
### 2. Hover to Edit Locators
开启 Word 风格脚注显示后,鼠标悬停上标数字即可查看完整引用内容,并直接修改页码/段落定位符:
With Word-style footnote display enabled, hover over a superscript number to preview the full citation and edit the page/paragraph locator directly:
![悬停预览与定位符编辑](assets/screenshots/zh-hover-preview.png)
![en-hover-preview](assets/screenshots/en-hover-preview.png)
### 3. 切换引用样式
### 3. Switch Citation Style
执行“文档首选项”打开文档首选项窗口。插件会动态读取 Zotero 中已安装的所有 CSL 样式,以可搜索列表形式展示;选择所需样式后,可同时切换脚注/尾注模式,并一键应用到当前文档的所有引用:
Run `Document preferences` to open the preferences panel. The plugin dynamically reads all CSL styles installed in your Zotero and presents them in a searchable list. Pick a style, optionally switch between footnote/endnote mode, and apply the change to all citations in the current document at once:
![文档首选项](assets/screenshots/zh-preferences.png)
![en-preferences](assets/screenshots/en-preferences.png)
### 4. 插入参考书目
### 4. Insert a Bibliography
执行“插入参考书目”,插件会在光标位置生成当前文档所有引用的参考文献列表。参考书目在导出 Word 时也会保留。
Run `Insert bibliography` to generate a formatted reference list at the cursor position. The bibliography is also preserved when exporting to Word.
![insert-bibliography-preview](assets/screenshots/insert-bibliography-preview.png)
![en-insert-bibliography](assets/screenshots/en-insert-bibliography.png)
### 5. 导出为 Word
### 5. Export to Word
1. 在命令面板中执行“检测 Pandoc 是否可用”,确认 Pandoc 正常工作
2. 执行“导出为 Word (.docx)”
1. Run `Check whether Pandoc is available` first to confirm Pandoc is working.
2. Run `Export to Word (.docx)`.
导出的 Word 文档包含正确格式的脚注正文为宋体小四、1.5 倍行距、两端对齐、首行缩进两字符,标题为黑体。
The exported Word document contains properly formatted footnotes, with body text in SimSun 12pt, 1.5 line spacing, justified alignment, first-line indent, and headings in SimHei.
![export-to-word-preview](assets/screenshots/export-to-word-preview.png)
![en-export-to-word-preview](assets/screenshots/en-export-to-word-preview.png)
---
## 设置
## Settings
![设置页面上半部分](assets/screenshots/zh-settings.png)
![en-settings](assets/screenshots/en-settings.png)
![设置页面下半部分](assets/screenshots/zh-settings-2.png)
### 主要设置项
| 设置 | 说明 |
|------|------|
| 界面语言 | 中文 / English |
| 默认 CSL 样式 | 新插入引用使用的格式 |
| 引用格式模式 | 脚注 / 尾注 |
| Word 风格脚注显示 | 上标数字 + 悬停预览 |
| 标题栏按钮 | 主开关 + 6 个子开关(插入引用、切换 Word 风格脚注显示、刷新所有引用、修改引用格式、解除引用链接、导出为 Word可在设置中分别控制每个按钮的显隐 |
| Pandoc 路径 | 默认 `pandoc`,可填完整路径 |
| 额外 Pandoc 参数 | 如 `--reference-doc=template.docx` |
| 固定导出目录 | 不设则每次弹出选择框 |
| 默认导出目录 | 仅在开启固定导出目录时显示;留空则使用当前文档所在目录 |
![en-settings-2](assets/screenshots/en-settings-2.png)
### Main Settings
| Setting | Description |
|---------|-------------|
| Interface language | Chinese / English |
| Default CSL style | Format used for newly inserted citations |
| Citation mode | Footnote / Endnote |
| Word-style footnote display | Superscript numbers + hover preview |
| Title bar buttons | Master toggle + 6 individual toggles (Insert citation, Toggle Word-style footnote display, Refresh all citations, Change citation style, Unlink citations, Export to Word), each controllable independently in settings |
| Pandoc path | Defaults to `pandoc`; accepts full paths |
| Extra Pandoc arguments | e.g. `--reference-doc=template.docx` |
| Fixed export directory | If unset, prompts for output location each time |
| Default export directory | Shown only when fixed export directory is enabled; if blank, uses the current note's folder |
---
## 命令列表
## Command List
以下为插件提供的命令;在命令面板中可直接按命令名称搜索:
The plugin provides the following commands; you can search for them directly by name in the command palette:
| 命令 | 说明 |
|------|------|
| 插入引用 | 打开 Zotero 引文选择器 |
| 插入参考书目 | 在光标处生成参考文献列表 |
| 刷新所有引用 | 从 Zotero 重新拉取并更新 |
| 文档首选项 | 切换 CSL 样式和引用模式 |
| 导出为 Word (.docx) | Pandoc 转换为 Word |
| 解除引用链接 | 移除插件元数据(不可逆) |
| 切换 Word 风格脚注显示 | 开/关上标数字显示 |
| 切换标题栏功能区显示 | 显示/隐藏标题栏图标 |
| 检测 Pandoc 是否可用 | 验证 Pandoc 安装 |
| Command | Description |
|---------|-------------|
| Insert citation | Open the Zotero citation picker |
| Insert bibliography | Generate a reference list at the cursor |
| Refresh all citations | Re-fetch item data from Zotero and update |
| Document preferences | Switch CSL style and citation mode |
| Export to Word (.docx) | Convert to Word via Pandoc |
| Unlink citations | Remove plugin metadata (irreversible) |
| Toggle Word-style footnote display | Enable/disable superscript markers |
| Toggle title bar actions | Show/hide the title bar icons |
| Check whether Pandoc is available | Verify Pandoc installation |
---
## 许可证
## License
MIT

View file

@ -1,146 +0,0 @@
# Zotero Citations
> Manage Zotero citations in Obsidian with footnote/endnote modes, Word-style display, and one-click export to Word with formatted footnotes.
---
## Highlights
- **Insert citations** — Invokes Zotero's native citation picker or an in-plugin search modal, with support for page/paragraph locators
- **Footnote & endnote** — Freely switch between footnote mode (`^[citation text]`) and endnote mode (`[^1]` + endnote definitions)
- **Word-style display** — Footnote markers render as superscript numbers in the editor; hover to preview the full citation and edit locators
- **Document preferences** — Dynamically reads all CSL styles installed in Zotero, with one-click style and mode switching
- **Bibliography** — Auto-generates a formatted reference list from all citations in the current document
- **Export to Word** — Converts Markdown to `.docx` via Pandoc, preserving footnote/endnote structure
- **Bilingual UI** — Switch between Chinese and English in settings
---
## Prerequisites
| Component | Description |
|-----------|-------------|
| Obsidian Desktop 1.5.7+ | Desktop-only plugin (`isDesktopOnly: true`) |
| [Zotero](https://www.zotero.org/) | Reference manager; should be running |
| [Better BibTeX](https://github.com/retorquere/zotero-better-bibtex/releases) | Zotero plugin that provides the API layer |
| [Pandoc](https://pandoc.org/installing.html) (optional) | Required only for Word export |
---
## Installation
1. Download the following files from the GitHub Releases page:
- `main.js` — plugin runtime
- `manifest.json` — plugin manifest
- `styles.css` — plugin styles
2. Place the files into your vault's `.obsidian/plugins/zotero-citations/` directory (create it if it does not exist)
3. Enable **Zotero Citations** in Obsidian Settings → Community plugins
4. Make sure Zotero is running and Better BibTeX is installed
5. (For Word export) Install Pandoc and ensure it is on your system PATH
---
## Compatibility Note
This version has been developed and tested primarily on **macOS**. Linux and Windows have not yet been fully validated, so UI rendering, window focus behavior, native dialogs, and export-related workflows may behave differently and are not guaranteed to be perfectly compatible at this stage.
---
## Disclosures
- **Network use**: the plugin talks to Zotero / Better BibTeX over the local loopback address `127.0.0.1`; it does not rely on a plugin-operated remote server.
- **External files and executables**: the plugin reads the local Zotero styles directory; in fallback scenarios it may copy and read the local Zotero database in the system temp directory; for Word export it invokes the local `pandoc` executable; for database fallback parsing it may invoke the local `sqlite3` executable; on macOS it may invoke the system `osascript` command to return focus to Obsidian after the Zotero picker closes.
- **Local data storage**: the plugin stores settings and citation cache data in Obsidian's plugin data storage.
- **Accounts / payments / ads / telemetry**: the plugin does not require an account, does not include ads, does not include in-app payments, and does not intentionally collect telemetry.
- **Source availability**: the plugin source code is published on GitHub under the MIT license: <https://github.com/WesternGua/obsidian-zotero-citations>
---
## Quick Start
### 1. Insert a Citation
Search for `Insert citation` in the command palette, or click the citation icon in the title bar.
The plugin will first try to open Zotero's native citation picker — search for items, add a page number or other locator, and confirm with the checkmark button.
Inserted citations appear as footnotes or endnotes depending on your current citation mode setting.
> **Note**:
> - The plugin writes hidden metadata (`<!-- zotero:ITEMKEY:locator -->`) at the beginning of each note. Do not remove it manually, or the plugin will not be able to track the citation.
> - When you are ready to finalize, run `Unlink citations` (irreversible) to strip the hidden metadata while keeping the visible citation text.
![insert-citation-preview](assets/screenshots/insert-citation-preview.png)
### 2. Hover to Edit Locators
With Word-style footnote display enabled, hover over a superscript number to preview the full citation and edit the page/paragraph locator directly:
![en-hover-preview](assets/screenshots/en-hover-preview.png)
### 3. Switch Citation Style
Run `Document preferences` to open the preferences panel. The plugin dynamically reads all CSL styles installed in your Zotero and presents them in a searchable list. Pick a style, optionally switch between footnote/endnote mode, and apply the change to all citations in the current document at once:
![en-preferences](assets/screenshots/en-preferences.png)
### 4. Insert a Bibliography
Run `Insert bibliography` to generate a formatted reference list at the cursor position. The bibliography is also preserved when exporting to Word.
![en-insert-bibliography](assets/screenshots/en-insert-bibliography.png)
### 5. Export to Word
1. Run `Check whether Pandoc is available` first to confirm Pandoc is working.
2. Run `Export to Word (.docx)`.
The exported Word document contains properly formatted footnotes, with body text in SimSun 12pt, 1.5 line spacing, justified alignment, first-line indent, and headings in SimHei.
![en-export-to-word-preview](assets/screenshots/en-export-to-word-preview.png)
---
## Settings
![en-settings](assets/screenshots/en-settings.png)
![en-settings-2](assets/screenshots/en-settings-2.png)
### Main Settings
| Setting | Description |
|---------|-------------|
| Interface language | Chinese / English |
| Default CSL style | Format used for newly inserted citations |
| Citation mode | Footnote / Endnote |
| Word-style footnote display | Superscript numbers + hover preview |
| Title bar buttons | Master toggle + 6 individual toggles (Insert citation, Toggle Word-style footnote display, Refresh all citations, Change citation style, Unlink citations, Export to Word), each controllable independently in settings |
| Pandoc path | Defaults to `pandoc`; accepts full paths |
| Extra Pandoc arguments | e.g. `--reference-doc=template.docx` |
| Fixed export directory | If unset, prompts for output location each time |
| Default export directory | Shown only when fixed export directory is enabled; if blank, uses the current note's folder |
---
## Command List
The plugin provides the following commands; you can search for them directly by name in the command palette:
| Command | Description |
|---------|-------------|
| Insert citation | Open the Zotero citation picker |
| Insert bibliography | Generate a reference list at the cursor |
| Refresh all citations | Re-fetch item data from Zotero and update |
| Document preferences | Switch CSL style and citation mode |
| Export to Word (.docx) | Convert to Word via Pandoc |
| Unlink citations | Remove plugin metadata (irreversible) |
| Toggle Word-style footnote display | Enable/disable superscript markers |
| Toggle title bar actions | Show/hide the title bar icons |
| Check whether Pandoc is available | Verify Pandoc installation |
---
## License
MIT

150
README_zh.md Normal file
View file

@ -0,0 +1,150 @@
# Zotero Citations
> 在 Obsidian 中管理 Zotero 引用,支持脚注/尾注模式、Word 风格显示,并一键导出为带格式脚注的 Word 文档。
[English](./README.md)
---
## 功能亮点
- **插入引用** — 调用 Zotero 原生引文选择器或插件内搜索面板,支持页码/段落定位符
- **脚注 & 尾注** — 自由切换脚注模式(`^[引用文本]`)和尾注模式(`[^1]` + 文末定义)
- **Word 风格显示** — 编辑器中脚注标记以上标数字呈现,悬停可查看完整引用并编辑定位符
- **文档首选项** — 动态读取 Zotero 中已安装的 CSL 样式,一键切换引用格式与引用模式
- **参考书目** — 自动汇总当前文档所有引用,生成格式化参考文献列表
- **导出为 Word** — 通过 Pandoc 将 Markdown 转换为 `.docx`,完整保留脚注/尾注结构
- **中英双语界面** — 设置中一键切换
---
## 前置要求
| 组件 | 说明 |
|------|------|
| Obsidian 桌面版 1.5.7+ | 插件仅支持桌面端(`isDesktopOnly: true` |
| [Zotero](https://www.zotero.org/) | 文献管理器,需保持运行 |
| [Better BibTeX](https://github.com/retorquere/zotero-better-bibtex/releases) | Zotero 插件,提供 API 通信 |
| [Pandoc](https://pandoc.org/installing.html)(可选) | 仅导出 Word 时需要 |
---
## 安装
1. 从 GitHub Releases 页面下载以下文件:
- `main.js` — 插件主程序
- `manifest.json` — 插件清单
- `styles.css` — 插件样式
2. 将以上文件放入 vault 的 `.obsidian/plugins/zotero-citations/` 目录(如目录不存在则手动创建)
3. 在 Obsidian 设置 → 第三方插件中启用 **Zotero Citations**
4. 确保 Zotero 已打开且 Better BibTeX 已安装
5. (如需导出 Word安装 Pandoc 并确保其在系统 PATH 中
---
## 兼容性说明
当前版本主要在 **macOS** 环境下开发与调试。Linux 与 Windows 平台目前尚未充分测试,因此界面显示、窗口聚焦、原生弹窗、导出链路等行为可能存在差异,暂时不能保证完全兼容。
---
## 披露说明
- **网络访问**:插件会通过 `127.0.0.1` 本地回环地址与 Zotero / Better BibTeX 通信,不会连接插件自建服务器。
- **外部文件与可执行程序访问**:插件会读取本机 Zotero 样式目录;在部分回退场景下可能复制并读取本机 Zotero 数据库到系统临时目录;导出 Word 时会调用本地 `pandoc`;数据库回退解析时可能调用本地 `sqlite3`;在 macOS 上,为了从 Zotero 选择器返回 Obsidian插件可能调用系统 `osascript`
- **本地数据存储**:插件会在 Obsidian 的插件数据中保存设置项和引用缓存。
- **账户 / 付费 / 广告 / 遥测**:插件不要求登录账户,不含广告,不含内购,也不主动收集遥测数据。
- **源码状态**:插件源码已公开在 GitHub 仓库中,并以 MIT 许可证发布:<https://github.com/WesternGua/obsidian-zotero-citations>
---
## 快速上手
### 1. 插入引用
在命令面板中搜索“插入引用”,或点击标题栏中的插入引用图标。
插件会优先调用 Zotero 原生引文选择器——在弹出窗口中搜索并选择条目,可填写页码等定位符,点击右上角 ✔ 确认插入。
引用插入后以脚注或尾注形式存在(取决于当前引用格式模式设置)。
> **注意**
> - 插件会在脚注正文开头写入 `<!-- zotero:ITEMKEY:locator -->` 形式的隐藏元数据,请勿手动删除,否则插件无法识别该引用。
> - 定稿后如需移除插件元数据,可执行“解除引用链接”(不可逆),插件会删除隐藏元数据并保留可见引用文本。
![insert-citation-preview](assets/screenshots/insert-citation-preview.png)
### 2. 悬停编辑定位符
开启 Word 风格脚注显示后,鼠标悬停上标数字即可查看完整引用内容,并直接修改页码/段落定位符:
![悬停预览与定位符编辑](assets/screenshots/zh-hover-preview.png)
### 3. 切换引用样式
执行“文档首选项”打开文档首选项窗口。插件会动态读取 Zotero 中已安装的所有 CSL 样式,以可搜索列表形式展示;选择所需样式后,可同时切换脚注/尾注模式,并一键应用到当前文档的所有引用:
![文档首选项](assets/screenshots/zh-preferences.png)
### 4. 插入参考书目
执行“插入参考书目”,插件会在光标位置生成当前文档所有引用的参考文献列表。参考书目在导出 Word 时也会保留。
![insert-bibliography-preview](assets/screenshots/insert-bibliography-preview.png)
### 5. 导出为 Word
1. 在命令面板中执行“检测 Pandoc 是否可用”,确认 Pandoc 正常工作
2. 执行“导出为 Word (.docx)”
导出的 Word 文档包含正确格式的脚注正文为宋体小四、1.5 倍行距、两端对齐、首行缩进两字符,标题为黑体。
![export-to-word-preview](assets/screenshots/export-to-word-preview.png)
---
## 设置
![设置页面上半部分](assets/screenshots/zh-settings.png)
![设置页面下半部分](assets/screenshots/zh-settings-2.png)
### 主要设置项
| 设置 | 说明 |
|------|------|
| 界面语言 | 中文 / English |
| 默认 CSL 样式 | 新插入引用使用的格式 |
| 引用格式模式 | 脚注 / 尾注 |
| Word 风格脚注显示 | 上标数字 + 悬停预览 |
| 标题栏按钮 | 主开关 + 6 个子开关(插入引用、切换 Word 风格脚注显示、刷新所有引用、修改引用格式、解除引用链接、导出为 Word可在设置中分别控制每个按钮的显隐 |
| Pandoc 路径 | 默认 `pandoc`,可填完整路径 |
| 额外 Pandoc 参数 | 如 `--reference-doc=template.docx` |
| 固定导出目录 | 不设则每次弹出选择框 |
| 默认导出目录 | 仅在开启固定导出目录时显示;留空则使用当前文档所在目录 |
---
## 命令列表
以下为插件提供的命令;在命令面板中可直接按命令名称搜索:
| 命令 | 说明 |
|------|------|
| 插入引用 | 打开 Zotero 引文选择器 |
| 插入参考书目 | 在光标处生成参考文献列表 |
| 刷新所有引用 | 从 Zotero 重新拉取并更新 |
| 文档首选项 | 切换 CSL 样式和引用模式 |
| 导出为 Word (.docx) | Pandoc 转换为 Word |
| 解除引用链接 | 移除插件元数据(不可逆) |
| 切换 Word 风格脚注显示 | 开/关上标数字显示 |
| 切换标题栏功能区显示 | 显示/隐藏标题栏图标 |
| 检测 Pandoc 是否可用 | 验证 Pandoc 安装 |
---
## 许可证
MIT

View file

@ -1,14 +1,12 @@
# Zotero Citations v0.2.2
# Zotero Citations v0.2.3
## Bug Fixes
## Community review follow-ups
- **Connection status false negative fixed** — Settings page now uses multiple probes to detect Zotero reliably (plugin API ping, connector ping, Better BibTeX endpoint fallback).
- **Style refresh parity fixed** — Settings page style selector now supports dynamic Zotero CSL style loading/refresh behavior aligned with the “Document Preferences / Change Citation Format” flow.
- **Footnote highlight rendering fixed**`[^n]` markers now correctly inherit highlight in cases like `==你好吧[^1]。`, where highlight boundaries were previously misdetected.
## Improvements
- Added a **Refresh style list** control in plugin settings, with localized UI copy.
- **README language flow reversed**`README.md` is now the English primary document, with a link to the Chinese guide (`README_zh.md`).
- **CSS lint improvements** — removed `!important` usage and `:has()` selectors from `styles.css` to improve compatibility and performance.
- **Popout compatibility hardening** — timer/window handling in search/export modals and footnote popovers now uses active-window-aware fallbacks.
- **UI construction cleanup** — result rows and footnote marker elements now use Obsidian-friendly element creation patterns.
- **Build-environment robustness** — added `pnpm.onlyBuiltDependencies` for `esbuild` to reduce build-install failures in constrained CI/review sandboxes.
---

View file

@ -12,7 +12,7 @@ zotero-citations/
├── CHANGELOG.md
├── LICENSE
├── README.md
├── README_EN.md
├── README_zh.md
├── RELEASE_NOTES.md
├── esbuild.config.mjs
├── manifest.json

View file

@ -1,7 +1,7 @@
{
"id": "zotero-citations",
"name": "Zotero Citations",
"version": "0.2.2",
"version": "0.2.3",
"minAppVersion": "1.5.7",
"description": "Zotero citation management with Word export via Pandoc.",
"author": "WesternGua",

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "zotero-citations",
"version": "0.2.0",
"version": "0.2.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "zotero-citations",
"version": "0.2.0",
"version": "0.2.3",
"devDependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",

View file

@ -1,6 +1,6 @@
{
"name": "zotero-citations",
"version": "0.2.2",
"version": "0.2.3",
"description": "Zotero citation management with Word export via Pandoc.",
"main": "main.js",
"scripts": {
@ -18,5 +18,10 @@
"@codemirror/view": "^6.0.0",
"@codemirror/state": "^6.0.0",
"jszip": "^3.10.1"
},
"pnpm": {
"onlyBuiltDependencies": [
"esbuild"
]
}
}

View file

@ -65,15 +65,12 @@ class FnWidget extends WidgetType {
}
toDOM(): HTMLElement {
const wrapper = document.createElement("span");
wrapper.className = "zotero-fn-widget";
const wrapper = createSpanEl({ cls: "zotero-fn-widget" });
const sup = document.createElement("sup");
sup.className = "zotero-fn-num footnote-ref";
const sup = wrapper.createEl("sup", { cls: "zotero-fn-num footnote-ref" });
sup.setAttribute("data-footnote-id", `fnref-${this.domId}`);
const marker = document.createElement("span");
marker.className = "footnote-link zotero-footnote-marker";
const marker = sup.createSpan({ cls: "footnote-link zotero-footnote-marker" });
marker.setAttribute("data-footref", this.identifier);
marker.setAttribute("tabindex", "0");
marker.textContent = `[${this.domId}]`;
@ -94,8 +91,6 @@ class FnWidget extends WidgetType {
edit: this.preview.edit || undefined,
});
sup.appendChild(marker);
wrapper.appendChild(sup);
return wrapper;
}
@ -116,7 +111,7 @@ class FnWidget extends WidgetType {
export function createFootnoteExtension(options: FootnoteExtensionOptions) {
return ViewPlugin.fromClass(
class {
decorations: any;
decorations: ReturnType<typeof buildDeco>;
constructor(view: EditorView) {
this.decorations = buildDeco(view, options);
}
@ -126,7 +121,7 @@ export function createFootnoteExtension(options: FootnoteExtensionOptions) {
}
}
},
{ decorations: (v: any) => v.decorations },
{ decorations: (v) => v.decorations },
);
}
@ -349,6 +344,28 @@ type AppWithPlugins = App & {
};
};
function getActiveWindow(): Window {
const maybeWin = window as Window & { activeWindow?: Window };
return maybeWin.activeWindow ?? window;
}
function getActiveDocument(): Document {
const maybeWin = window as Window & { activeDocument?: Document };
return maybeWin.activeDocument ?? document;
}
function createDivEl(options?: { cls?: string }): HTMLDivElement {
const el = getActiveDocument().createElement("div");
if (options?.cls) el.className = options.cls;
return el;
}
function createSpanEl(options?: { cls?: string }): HTMLSpanElement {
const el = getActiveDocument().createElement("span");
if (options?.cls) el.className = options.cls;
return el;
}
function attachRenderedPopover(target: HTMLElement, spec: PopoverSpec): void {
const show = () => showRenderedPopover(target, spec);
const scheduleHide = () => schedulePopoverHide(target);
@ -371,8 +388,7 @@ function showRenderedPopover(target: HTMLElement, spec: PopoverSpec): void {
}
destroyActivePopover();
const popover = document.createElement("div");
popover.className = "popover hover-popover zotero-footnote-popover";
const popover = createDivEl({ cls: "popover hover-popover zotero-footnote-popover" });
const embed = popover.createDiv({ cls: "markdown-embed", attr: { "data-type": "footnote" } });
const embedContent = embed.createDiv({ cls: "markdown-embed-content" });
@ -384,15 +400,15 @@ function showRenderedPopover(target: HTMLElement, spec: PopoverSpec): void {
}
if (!spec.markdown.trim()) embed.addClass("mod-empty");
document.body.appendChild(popover);
getActiveDocument().body.appendChild(popover);
const component = new Component();
const reposition = () => positionPopover(target, popover);
const onPopoverEnter = () => cancelPopoverHide();
const onPopoverLeave = () => schedulePopoverHide(target);
popover.addEventListener("mouseenter", onPopoverEnter);
popover.addEventListener("mouseleave", onPopoverLeave);
window.addEventListener("scroll", reposition, true);
window.addEventListener("resize", reposition);
getActiveWindow().addEventListener("scroll", reposition, true);
getActiveWindow().addEventListener("resize", reposition);
activePopover = { target, popover, component, hideTimer: null, reposition, onPopoverEnter, onPopoverLeave };
reposition();
@ -470,9 +486,9 @@ function positionPopover(target: HTMLElement, popover: HTMLElement): void {
const popoverRect = popover.getBoundingClientRect();
let top = rect.top - popoverRect.height - gap;
if (top < margin) top = rect.bottom + gap;
top = Math.max(margin, Math.min(window.innerHeight - popoverRect.height - margin, top));
top = Math.max(margin, Math.min(getActiveWindow().innerHeight - popoverRect.height - margin, top));
const left = Math.min(
window.innerWidth - popoverRect.width - margin,
getActiveWindow().innerWidth - popoverRect.width - margin,
Math.max(margin, rect.left + rect.width / 2 - popoverRect.width / 2),
);
popover.style.top = `${top}px`;
@ -482,25 +498,25 @@ function positionPopover(target: HTMLElement, popover: HTMLElement): void {
function schedulePopoverHide(target: HTMLElement): void {
if (!activePopover || activePopover.target !== target) return;
cancelPopoverHide();
activePopover.hideTimer = window.setTimeout(() => {
activePopover.hideTimer = getActiveWindow().setTimeout(() => {
if (activePopover?.target === target) destroyActivePopover();
}, 80);
}
function cancelPopoverHide(): void {
if (!activePopover?.hideTimer) return;
window.clearTimeout(activePopover.hideTimer);
getActiveWindow().clearTimeout(activePopover.hideTimer);
activePopover.hideTimer = null;
}
function destroyActivePopover(): void {
if (!activePopover) return;
const { popover, component, reposition, onPopoverEnter, onPopoverLeave, hideTimer } = activePopover;
if (hideTimer) window.clearTimeout(hideTimer);
if (hideTimer) getActiveWindow().clearTimeout(hideTimer);
popover.removeEventListener("mouseenter", onPopoverEnter);
popover.removeEventListener("mouseleave", onPopoverLeave);
window.removeEventListener("scroll", reposition, true);
window.removeEventListener("resize", reposition);
getActiveWindow().removeEventListener("scroll", reposition, true);
getActiveWindow().removeEventListener("resize", reposition);
component.unload();
popover.remove();
activePopover = null;

View file

@ -38,7 +38,8 @@ export class ExportModal extends Modal {
const confirm = btnRow.createEl("button", { text: appT(this.app, "common.export"), cls: "mod-cta" });
confirm.addEventListener("click", () => this.doConfirm());
setTimeout(() => {
const win = (window as Window & { activeWindow?: Window }).activeWindow ?? window;
win.setTimeout(() => {
this.input.focus();
this.input.select();
}, 50);

View file

@ -89,18 +89,20 @@ export class SearchModal extends Modal {
if (e.key === "Escape") this.close();
});
window.setTimeout(() => { this.searchInput.focus(); }, 50);
const win = (window as Window & { activeWindow?: Window }).activeWindow ?? window;
win.setTimeout(() => { this.searchInput.focus(); }, 50);
}
private onSearchInput(): void {
if (this.debounceTimer) clearTimeout(this.debounceTimer);
const win = (window as Window & { activeWindow?: Window }).activeWindow ?? window;
if (this.debounceTimer) win.clearTimeout(this.debounceTimer);
const q = this.searchInput.value.trim();
if (!q) {
this.resultsEl.empty();
this.resultsEl.createEl("p", { text: appT(this.app, "search.enterQuery"), cls: "zotero-results-placeholder" });
return;
}
this.debounceTimer = window.setTimeout(() => { void this.doSearch(q); }, 300);
this.debounceTimer = win.setTimeout(() => { void this.doSearch(q); }, 300);
}
private async doSearch(query: string): Promise<void> {
@ -138,8 +140,8 @@ export class SearchModal extends Modal {
const year = CitationManager.getYear(item);
const typeLabel = SearchModal.typeLabel(item.itemType, getAppSettings(this.app) || DEFAULT_SETTINGS);
row.createEl("div", { text: item.title, cls: "zotero-result-title" });
row.createEl("div", {
row.createDiv({ text: item.title, cls: "zotero-result-title" });
row.createDiv({
text: `${authors}${authors ? " · " : ""}${year} · ${typeLabel}`,
cls: "zotero-result-meta",
});
@ -182,7 +184,10 @@ export class SearchModal extends Modal {
onClose(): void {
this.contentEl.empty();
if (this.debounceTimer) clearTimeout(this.debounceTimer);
if (this.debounceTimer) {
const win = (window as Window & { activeWindow?: Window }).activeWindow ?? window;
win.clearTimeout(this.debounceTimer);
}
}
static typeLabel(itemType: string, settings: { language?: string } | Language = DEFAULT_SETTINGS): string {

View file

@ -3,7 +3,7 @@
*/
import { PluginSettingTab, Setting, App, requestUrl, Notice } from "obsidian";
import * as nodeHttp from "http";
import { t, getLanguage, I18N, appT } from "./i18n";
import { t, getLanguage } from "./i18n";
import { CitationManager } from "./CitationManager";
// ── Hardcoded fallback CSL styles ──────────────────────────────────────────

View file

@ -10,47 +10,47 @@ sup.zotero-fn-num,
}
sup.zotero-fn-num > .zotero-footnote-marker {
color: var(--text-accent) !important;
color: var(--text-accent);
display: inline;
font-size: 0.75em;
font-weight: 500;
cursor: pointer;
font-family: var(--font-text);
pointer-events: auto;
text-decoration: none !important;
box-shadow: none !important;
border: none !important;
text-decoration: none;
box-shadow: none;
border: none;
}
sup.zotero-fn-num > .zotero-footnote-marker:hover,
sup.zotero-fn-num > .zotero-footnote-marker:focus-visible {
color: var(--text-accent-hover, var(--text-accent)) !important;
text-decoration: none !important;
color: var(--text-accent-hover, var(--text-accent));
text-decoration: none;
outline: none;
}
.zotero-fn-widget.zotero-fn-highlighted {
background: var(--text-highlight-bg) !important;
color: var(--text-normal) !important;
box-shadow: none !important;
border: none !important;
background: var(--text-highlight-bg);
color: var(--text-normal);
box-shadow: none;
border: none;
}
.zotero-fn-widget.zotero-fn-highlighted > sup.zotero-fn-num,
.zotero-fn-widget.zotero-fn-highlighted > sup.zotero-fn-num > .zotero-footnote-marker,
mark .zotero-rendered-footnote-sup,
mark .zotero-rendered-footnote-ref {
background: transparent !important;
box-shadow: none !important;
border: none !important;
background: transparent;
box-shadow: none;
border: none;
}
a.zotero-rendered-footnote-ref {
color: var(--text-accent);
font-size: 0.75em;
text-decoration: none;
box-shadow: none !important;
border: none !important;
box-shadow: none;
border: none;
}
.zotero-footnote-popover {
@ -169,16 +169,10 @@ a.zotero-rendered-footnote-ref {
}
/* Modals */
.modal:has(.zotero-prefs-modal),
.modal:has(.zotero-unlink-modal),
.modal:has(.zotero-search-modal) {
padding-top: 12px;
}
.zotero-prefs-modal,
.zotero-unlink-modal,
.zotero-search-modal {
padding-top: 0 !important;
padding-top: 12px;
}
.zotero-prefs-section {

View file

@ -1,5 +1,6 @@
{
"0.2.0": "1.5.7",
"0.2.1": "1.5.7",
"0.2.2": "1.5.7"
"0.2.2": "1.5.7",
"0.2.3": "1.5.7"
}