Metadata fetching interrupts manual tag entry
I add a document to Zotero 7.0.20. Zotero creates a parent item with metadata.
Then I start typing tags into the tags field of the parent item. Meanwhile Zotero is fetching more metadata for the item, updating the abstract, etc. But when it adds the extra metadata, it interrupts my typing in the middle of a word and moves my cursor into the second tag field while I'm still typing the first tag. So then I have to go back and correct the tag I was typing.
This is very annoying and happens all the time. I wish it would wait until I am done typing, or otherwise preserve my cursor location so it wouldn't interfere with what I'm doing.
Then I start typing tags into the tags field of the parent item. Meanwhile Zotero is fetching more metadata for the item, updating the abstract, etc. But when it adds the extra metadata, it interrupts my typing in the middle of a word and moves my cursor into the second tag field while I'm still typing the first tag. So then I have to go back and correct the tag I was typing.
This is very annoying and happens all the time. I wish it would wait until I am done typing, or otherwise preserve my cursor location so it wouldn't interfere with what I'm doing.
Upgrade Storage
I don't have any other plugins installed. This all happens within the first few seconds. Fetching the abstract etc seems to be as part of the process of creating the parent item. But by that point I'm already typing the tags, in the Zotero desktop interface.
Claude:
=======
# Tag input focus loss bug during Connector save
## Symptom
When saving a document from Chrome's Zotero Connector, then switching to Zotero and typing a tag, the tag input loses focus mid-keystroke. The partially typed text gets saved as one tag, and the remaining keystrokes become a second tag (e.g., "foobar" → "foob" + "ar").
## Root cause
Two bugs compound:
### Bug 1: `attachmentBox.js` line 229-230
```javascript
if (this._item && val.id != this._item.id && document.activeElement.closest("editable-text")) {
document.activeElement.blur();
}
```
The `attachmentBox` item setter blurs `document.activeElement` whenever ANY editable-text in the entire document is focused — not just ones inside attachmentBox. The intent was to prevent stale title data from being saved when switching between attachments, but the check is document-global.
`this._item` is only updated when the assigned item is an attachment (line 232-233). For regular items, `this._item` retains whatever attachment was last viewed, making `val.id != this._item.id` always true when displaying a regular item.
### Bug 2: `itemDetails.js` line 419-426
```javascript
notify = async (action, type, ids, _extraData) => {
if (action == 'refresh' && this.item) {
// No check that ids includes this.item.id
await this.render();
}
};
```
The notify handler triggers a full `itemDetails.render()` for ANY `refresh/item` notification, regardless of which item was refreshed.
## Trigger chain
1. User saves a document from Chrome Connector. Zotero creates the parent item plus child attachments (PDFs, snapshots).
2. Several seconds later, fulltext indexing completes on a child attachment. This fires `refresh/item` for the attachment item.
3. `itemDetails.notify()` receives `refresh/item` for the attachment. It doesn't check whether the refreshed item matches the displayed item, so it calls `this.render()`.
4. `itemDetails.render()` iterates all panes and sets `box.item = item` on each, including attachmentBox.
5. `attachmentBox.item` setter fires. `this._item` is stale (the last attachment ever viewed). `val` is the current parent item. Since their IDs differ, and the tag input is a focused editable-text, it calls `document.activeElement.blur()`.
6. The tag input loses focus. The blur event triggers `saveTag()`, which saves the partial text as a tag, clears the input, and schedules a `setTimeout` to refocus it.
7. The refocused input captures the remaining keystrokes. The user presses Enter thinking they're confirming the full tag but actually confirms a fragment.
## Why mouse interaction matters
The bug requires `this._item` on attachmentBox to be non-null, which only happens after the user has previously viewed an attachment item (by clicking on one). After that, the stale reference persists for the lifetime of the pane.
## Debug evidence
Three occurrences captured via runtime logging. Each showed the same pattern:
```
NOTIFY refresh/item ids=34106 ← unrelated item (attachment being indexed)
NOTIFY activeElement=input#.input ← user is typing in tag input
Viewing item ← itemDetails.render() fires
Refreshing item box ← pane loop runs
Setting mode to 'edit' ← attachmentBox renders (blur already happened)
Stopping autocomplete search ← autocomplete torn down due to focus loss
Saving tag ← saveTag fires from blur
TAG FOCUSOUT: relatedTarget=null ← focus gone, nowhere specific
```
`relatedTarget` being null confirmed `.blur()` was called directly.
## Fixes
**Minimal fix** — scope the blur to attachmentBox's own elements (`attachmentBox.js:229`):
```javascript
if (this._item && val.id != this._item.id && this.contains(document.activeElement)) {
```
**Defense in depth** — don't re-render for unrelated items (`itemDetails.js:420`):
```javascript
if (action == 'refresh' && this.item && ids.includes(this.item.id)) {
```
## Key files
- `chrome/content/zotero/elements/attachmentBox.js` — item setter with document-global blur
- `chrome/content/zotero/elements/itemDetails.js` — notify handler lacking item ID filter
- `chrome/content/zotero/elements/tagsBox.js` — saveTag triggered by blur
- `chrome/content/zotero/xpcom/notifier.js` — notification dispatch system