zotlib 0.4.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,37 @@
1
+ name: Release
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - main
7
+ types:
8
+ - closed
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ pypi-publish:
15
+ name: Upload release to PyPI
16
+ runs-on: ubuntu-latest
17
+ if: github.event.pull_request.merged == true
18
+ environment:
19
+ name: pypi
20
+ url: https://pypi.org/project/zotlib/
21
+ permissions:
22
+ id-token: write
23
+ steps:
24
+ - name: Checkout code
25
+ uses: actions/checkout@v4
26
+
27
+ - name: Install uv
28
+ uses: astral-sh/setup-uv@v5
29
+
30
+ - name: Set up Python 3.14
31
+ run: uv python install 3.14
32
+
33
+ - name: Package project
34
+ run: uv build
35
+
36
+ - name: Publish package distributions to PyPI
37
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,29 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [dev, main]
6
+ pull_request:
7
+ branches: [dev, main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v5
21
+
22
+ - name: Set up Python ${{ matrix.python-version }}
23
+ run: uv python install ${{ matrix.python-version }}
24
+
25
+ - name: Install dependencies
26
+ run: uv sync --all-groups --python ${{ matrix.python-version }}
27
+
28
+ - name: Run tests with coverage
29
+ run: uv run pytest -v --cov=zotlib --cov-report=term-missing
@@ -0,0 +1,32 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+
8
+ # Environment
9
+ .env
10
+ .venv/
11
+
12
+ # IDE
13
+ .vscode/
14
+ .idea/
15
+ .claude
16
+
17
+ # Testing
18
+ .pytest_cache
19
+ .coverage
20
+
21
+ # Linting
22
+ .ruff_cache
23
+
24
+ # Data/output
25
+ data
26
+ output/
27
+ *.csv
28
+ *.sqlite
29
+ zotlib.toml
30
+
31
+ # Archive
32
+ .archive/
@@ -0,0 +1 @@
1
+ 3.14
zotlib-0.4.3/PKG-INFO ADDED
@@ -0,0 +1,219 @@
1
+ Metadata-Version: 2.4
2
+ Name: zotlib
3
+ Version: 0.4.3
4
+ Summary: Extract and format bibliographic data from Zotero databases
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: pillow<13.0.0,>=12.1.0
7
+ Requires-Dist: polars>=1.39.2
8
+ Requires-Dist: pymupdf<2.0.0,>=1.26.7
9
+ Requires-Dist: rich>=13.0.0
10
+ Requires-Dist: typer>=0.12.0
11
+ Description-Content-Type: text/markdown
12
+
13
+ # zotlib
14
+
15
+ Tools for extracting and formatting bibliographic data from Zotero 8 databases.
16
+
17
+ Reads directly from Zotero's local SQLite database — no API key needed. Export collections as CSV or APA-formatted references, generate PDF cover images with thumbnails, and extract annotated PDFs with baked-in highlights and markdown notes. Includes a CLI for common workflows and a Python API for custom pipelines. Built for Zotero 8; older versions are untested and likely incompatible due to schema differences.
18
+
19
+ ## Project Structure
20
+
21
+ ```
22
+ zotlib/
23
+ ├── zotlib/ # Python library
24
+ │ ├── cli.py # CLI commands
25
+ │ ├── config.py # Database path discovery
26
+ │ ├── database.py # SQLite interface
27
+ │ ├── extractors.py # Data extraction functions
28
+ │ ├── exporters.py # Collection export (annotations + PDFs)
29
+ │ ├── backup.py # Zotero directory backup
30
+ │ ├── tables.py # Zotero database table definitions
31
+ │ ├── covers.py # PDF cover generation
32
+ │ ├── paths.py # Path resolution and filename utilities
33
+ │ └── formatters/apa.py # APA citation formatter
34
+ ├── scripts/ # Utility scripts
35
+ │ ├── extract-annotations.js # Annotation extractor (interactive + headless)
36
+ │ ├── create-parent-item.js # Create parents for standalone PDFs
37
+ │ └── run-extract.sh # Shell wrapper for headless extraction
38
+ ├── tests/ # Test suite
39
+ └── pyproject.toml # Project configuration
40
+ ```
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ uv add zotlib
46
+ ```
47
+
48
+ From source:
49
+
50
+ ```bash
51
+ git clone https://github.com/gitronald/zotlib.git
52
+ cd zotlib
53
+ uv sync
54
+ ```
55
+
56
+ From a specific branch:
57
+
58
+ ```bash
59
+ uv add git+https://github.com/gitronald/zotlib.git@dev
60
+ ```
61
+
62
+ ## Configuration
63
+
64
+ Run `zotlib init` to auto-discover Zotero paths and save them to `zotlib.toml`:
65
+
66
+ ```bash
67
+ zotlib init
68
+ ```
69
+
70
+ ```
71
+ database: /mnt/c/Users/rer/Zotero/zotero.sqlite
72
+ pdfs_dir: /mnt/i/My Drive/zotero-pdfs
73
+
74
+ Saved to zotlib.toml
75
+ ```
76
+
77
+ The config file stores the database and linked PDFs directory:
78
+
79
+ ```toml
80
+ [zotlib]
81
+ database = "/path/to/zotero.sqlite"
82
+ pdfs_dir = "/path/to/linked-pdfs"
83
+ ```
84
+
85
+ Path resolution priority (for both database and PDFs dir):
86
+
87
+ 1. **CLI flag**: `--database`, `--pdfs-dir`
88
+ 2. **Environment variable**: `ZOTERO_DATABASE`
89
+ 3. **Config file**: `zotlib.toml`
90
+ 4. **Auto-discovery**: Checks common locations (Linux, WSL, macOS)
91
+
92
+ ## CLI Commands
93
+
94
+ ### Explore
95
+
96
+ Browse collections and inspect database schema. The `show-tables` command documents Zotero's largely undocumented SQLite table structure, including column descriptions and types.
97
+
98
+ ```bash
99
+ # List available collections
100
+ zotlib show-collections
101
+
102
+ # Show database tables
103
+ zotlib show-tables
104
+ zotlib show-tables items
105
+ ```
106
+
107
+ ### Export
108
+
109
+ Export collection data in multiple formats. Supports linked attachments via `--pdfs-dir` for PDFs stored outside Zotero's default storage.
110
+
111
+ ```bash
112
+ # Export all tables as CSV
113
+ zotlib export-csv
114
+
115
+ # Export a collection as CSV
116
+ zotlib export-csv -c publications
117
+
118
+ # Format a collection as APA references
119
+ zotlib export-apa -c publications
120
+
121
+ # Generate cover images and thumbnails
122
+ zotlib export-covers -c publications
123
+
124
+ # Export annotated PDFs and markdown notes
125
+ zotlib export-annotations -c mycollection
126
+ ```
127
+
128
+ ### Backup
129
+
130
+ Archive the entire Zotero data directory as a compressed `.tar.bz2` file with a progress bar. Saves to `data/backups/zotero-YYYY-MM-DD.tar.bz2` by default. Use `-o` to specify a custom output path or `-d` to point to a different database.
131
+
132
+ ```bash
133
+ zotlib backup
134
+ zotlib backup -o ~/backups/zotero-2026-03-21.tar.bz2
135
+ ```
136
+
137
+ ### Output structure
138
+
139
+ ```
140
+ output/
141
+ ├── export-csv/ # Bibliographic metadata
142
+ │ └── publications.csv
143
+ ├── export-apa/ # APA-formatted references
144
+ │ └── publications.md
145
+ ├── export-covers/ # PDF cover images
146
+ │ └── publications/
147
+ │ ├── fullsize/
148
+ │ └── thumbnails/
149
+ └── export-annotations/ # Annotated PDFs + notes
150
+ └── mycollection/
151
+ └── author-year-title/
152
+ ├── paper.pdf
153
+ └── annotations.md
154
+ ```
155
+
156
+ ### Export annotations features
157
+
158
+ - Multi-attachment support: each PDF gets only its own annotations
159
+ - Standalone attachment support: PDFs added directly to a collection
160
+ - Linked attachment resolution via `--pdfs-dir`
161
+ - "REVIEW: " prefix stripping from titles
162
+
163
+ ### Python API
164
+
165
+ ```python
166
+ from zotlib import ZoteroDatabase, extract_cv_items, format_cv_as_apa
167
+
168
+ db = ZoteroDatabase("/path/to/zotero.sqlite")
169
+ items = extract_cv_items(db, collection_name="mypapers")
170
+ apa_output = format_cv_as_apa(items, output_path="output/apa.md")
171
+ ```
172
+
173
+ ## Zotero JavaScript Scripts
174
+
175
+ **WIP** — Utilities for Zotero's JavaScript console (Tools > Developer > Run JavaScript). The Zotero SQLite database should never be modified directly via Python — use these JS scripts (which run through Zotero's API) for any write operations.
176
+
177
+ ### create-parent-item.js
178
+
179
+ Creates parent document items for standalone PDF attachments in a collection. Useful when PDFs were added directly without metadata — creates a parent item using the filename as the title and re-parents the attachment.
180
+
181
+ ### extract-annotations.js
182
+
183
+ Extracts annotations from the selected item's PDFs as markdown. Auto-detects its context:
184
+
185
+ - **Interactive** (Tools > Developer > Run JavaScript): shows a file save dialog
186
+ - **Headless** (via HTTP debug API): writes to `~/Desktop/zotero-annotations/`
187
+
188
+ To run headlessly:
189
+
190
+ ```bash
191
+ ./scripts/run-extract.sh
192
+ ```
193
+
194
+ Requires: Settings > Advanced > "Allow other applications to communicate with Zotero"
195
+
196
+ The shell script should work on macOS where Zotero and the terminal share the same `localhost`. On WSL, the script calls Zotero's debug HTTP endpoint on `127.0.0.1:23119`, but `localhost` does not bridge to the Windows host by default. You may need to use the Windows host IP or run the curl command from PowerShell instead.
197
+
198
+ ## Annotation Format
199
+
200
+ ### Types
201
+
202
+ | Type | Extracted Data |
203
+ |------|----------------|
204
+ | Highlight | Text + comment + color |
205
+ | Note | Comment text |
206
+ | Underline | Text + comment |
207
+ | Image | Comment only (image not exported) |
208
+
209
+ ### Color Labels
210
+
211
+ | Hex Code | Label |
212
+ |----------|-------|
213
+ | `#ffd400` | yellow |
214
+ | `#ff6666` | red |
215
+ | `#5fb236` | green |
216
+ | `#2ea8e5` | blue |
217
+ | `#a28ae5` | purple |
218
+ | `#e56eee` | magenta |
219
+ | `#f19837` | orange |
zotlib-0.4.3/README.md ADDED
@@ -0,0 +1,207 @@
1
+ # zotlib
2
+
3
+ Tools for extracting and formatting bibliographic data from Zotero 8 databases.
4
+
5
+ Reads directly from Zotero's local SQLite database — no API key needed. Export collections as CSV or APA-formatted references, generate PDF cover images with thumbnails, and extract annotated PDFs with baked-in highlights and markdown notes. Includes a CLI for common workflows and a Python API for custom pipelines. Built for Zotero 8; older versions are untested and likely incompatible due to schema differences.
6
+
7
+ ## Project Structure
8
+
9
+ ```
10
+ zotlib/
11
+ ├── zotlib/ # Python library
12
+ │ ├── cli.py # CLI commands
13
+ │ ├── config.py # Database path discovery
14
+ │ ├── database.py # SQLite interface
15
+ │ ├── extractors.py # Data extraction functions
16
+ │ ├── exporters.py # Collection export (annotations + PDFs)
17
+ │ ├── backup.py # Zotero directory backup
18
+ │ ├── tables.py # Zotero database table definitions
19
+ │ ├── covers.py # PDF cover generation
20
+ │ ├── paths.py # Path resolution and filename utilities
21
+ │ └── formatters/apa.py # APA citation formatter
22
+ ├── scripts/ # Utility scripts
23
+ │ ├── extract-annotations.js # Annotation extractor (interactive + headless)
24
+ │ ├── create-parent-item.js # Create parents for standalone PDFs
25
+ │ └── run-extract.sh # Shell wrapper for headless extraction
26
+ ├── tests/ # Test suite
27
+ └── pyproject.toml # Project configuration
28
+ ```
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ uv add zotlib
34
+ ```
35
+
36
+ From source:
37
+
38
+ ```bash
39
+ git clone https://github.com/gitronald/zotlib.git
40
+ cd zotlib
41
+ uv sync
42
+ ```
43
+
44
+ From a specific branch:
45
+
46
+ ```bash
47
+ uv add git+https://github.com/gitronald/zotlib.git@dev
48
+ ```
49
+
50
+ ## Configuration
51
+
52
+ Run `zotlib init` to auto-discover Zotero paths and save them to `zotlib.toml`:
53
+
54
+ ```bash
55
+ zotlib init
56
+ ```
57
+
58
+ ```
59
+ database: /mnt/c/Users/rer/Zotero/zotero.sqlite
60
+ pdfs_dir: /mnt/i/My Drive/zotero-pdfs
61
+
62
+ Saved to zotlib.toml
63
+ ```
64
+
65
+ The config file stores the database and linked PDFs directory:
66
+
67
+ ```toml
68
+ [zotlib]
69
+ database = "/path/to/zotero.sqlite"
70
+ pdfs_dir = "/path/to/linked-pdfs"
71
+ ```
72
+
73
+ Path resolution priority (for both database and PDFs dir):
74
+
75
+ 1. **CLI flag**: `--database`, `--pdfs-dir`
76
+ 2. **Environment variable**: `ZOTERO_DATABASE`
77
+ 3. **Config file**: `zotlib.toml`
78
+ 4. **Auto-discovery**: Checks common locations (Linux, WSL, macOS)
79
+
80
+ ## CLI Commands
81
+
82
+ ### Explore
83
+
84
+ Browse collections and inspect database schema. The `show-tables` command documents Zotero's largely undocumented SQLite table structure, including column descriptions and types.
85
+
86
+ ```bash
87
+ # List available collections
88
+ zotlib show-collections
89
+
90
+ # Show database tables
91
+ zotlib show-tables
92
+ zotlib show-tables items
93
+ ```
94
+
95
+ ### Export
96
+
97
+ Export collection data in multiple formats. Supports linked attachments via `--pdfs-dir` for PDFs stored outside Zotero's default storage.
98
+
99
+ ```bash
100
+ # Export all tables as CSV
101
+ zotlib export-csv
102
+
103
+ # Export a collection as CSV
104
+ zotlib export-csv -c publications
105
+
106
+ # Format a collection as APA references
107
+ zotlib export-apa -c publications
108
+
109
+ # Generate cover images and thumbnails
110
+ zotlib export-covers -c publications
111
+
112
+ # Export annotated PDFs and markdown notes
113
+ zotlib export-annotations -c mycollection
114
+ ```
115
+
116
+ ### Backup
117
+
118
+ Archive the entire Zotero data directory as a compressed `.tar.bz2` file with a progress bar. Saves to `data/backups/zotero-YYYY-MM-DD.tar.bz2` by default. Use `-o` to specify a custom output path or `-d` to point to a different database.
119
+
120
+ ```bash
121
+ zotlib backup
122
+ zotlib backup -o ~/backups/zotero-2026-03-21.tar.bz2
123
+ ```
124
+
125
+ ### Output structure
126
+
127
+ ```
128
+ output/
129
+ ├── export-csv/ # Bibliographic metadata
130
+ │ └── publications.csv
131
+ ├── export-apa/ # APA-formatted references
132
+ │ └── publications.md
133
+ ├── export-covers/ # PDF cover images
134
+ │ └── publications/
135
+ │ ├── fullsize/
136
+ │ └── thumbnails/
137
+ └── export-annotations/ # Annotated PDFs + notes
138
+ └── mycollection/
139
+ └── author-year-title/
140
+ ├── paper.pdf
141
+ └── annotations.md
142
+ ```
143
+
144
+ ### Export annotations features
145
+
146
+ - Multi-attachment support: each PDF gets only its own annotations
147
+ - Standalone attachment support: PDFs added directly to a collection
148
+ - Linked attachment resolution via `--pdfs-dir`
149
+ - "REVIEW: " prefix stripping from titles
150
+
151
+ ### Python API
152
+
153
+ ```python
154
+ from zotlib import ZoteroDatabase, extract_cv_items, format_cv_as_apa
155
+
156
+ db = ZoteroDatabase("/path/to/zotero.sqlite")
157
+ items = extract_cv_items(db, collection_name="mypapers")
158
+ apa_output = format_cv_as_apa(items, output_path="output/apa.md")
159
+ ```
160
+
161
+ ## Zotero JavaScript Scripts
162
+
163
+ **WIP** — Utilities for Zotero's JavaScript console (Tools > Developer > Run JavaScript). The Zotero SQLite database should never be modified directly via Python — use these JS scripts (which run through Zotero's API) for any write operations.
164
+
165
+ ### create-parent-item.js
166
+
167
+ Creates parent document items for standalone PDF attachments in a collection. Useful when PDFs were added directly without metadata — creates a parent item using the filename as the title and re-parents the attachment.
168
+
169
+ ### extract-annotations.js
170
+
171
+ Extracts annotations from the selected item's PDFs as markdown. Auto-detects its context:
172
+
173
+ - **Interactive** (Tools > Developer > Run JavaScript): shows a file save dialog
174
+ - **Headless** (via HTTP debug API): writes to `~/Desktop/zotero-annotations/`
175
+
176
+ To run headlessly:
177
+
178
+ ```bash
179
+ ./scripts/run-extract.sh
180
+ ```
181
+
182
+ Requires: Settings > Advanced > "Allow other applications to communicate with Zotero"
183
+
184
+ The shell script should work on macOS where Zotero and the terminal share the same `localhost`. On WSL, the script calls Zotero's debug HTTP endpoint on `127.0.0.1:23119`, but `localhost` does not bridge to the Windows host by default. You may need to use the Windows host IP or run the curl command from PowerShell instead.
185
+
186
+ ## Annotation Format
187
+
188
+ ### Types
189
+
190
+ | Type | Extracted Data |
191
+ |------|----------------|
192
+ | Highlight | Text + comment + color |
193
+ | Note | Comment text |
194
+ | Underline | Text + comment |
195
+ | Image | Comment only (image not exported) |
196
+
197
+ ### Color Labels
198
+
199
+ | Hex Code | Label |
200
+ |----------|-------|
201
+ | `#ffd400` | yellow |
202
+ | `#ff6666` | red |
203
+ | `#5fb236` | green |
204
+ | `#2ea8e5` | blue |
205
+ | `#a28ae5` | purple |
206
+ | `#e56eee` | magenta |
207
+ | `#f19837` | orange |
zotlib-0.4.3/TODO.md ADDED
@@ -0,0 +1,19 @@
1
+ # TODO
2
+
3
+ - [ ] Test JS scripts across Windows/macOS and experiment with record modification (no plan)
4
+ - [x] Code review cleanup: naming, SQL params, dead code, tests ([plan](.claude/plans/011-code-review-cleanup.md))
5
+ - [x] Reorganize `zotero-js/` folder into `scripts/` ([plan](.claude/plans/010-reorganize-zotero-js.md))
6
+ - [x] Add descriptions and columns for remaining tables in `show-tables --all` (no plan)
7
+ - [x] Add `zotlib init` command with `zotlib.toml` config ([plan](.claude/plans/009-init-command.md))
8
+ - [x] Restructure CLI commands with export-* naming ([plan](.claude/plans/008-restructure-cli-commands.md))
9
+ - [x] Polish CLI schema command and standardize output dirs ([plan](.claude/plans/007-cli-polish.md))
10
+ - [x] Convert pandas to polars ([plan](.claude/plans/006-pandas-to-polars.md))
11
+ - [x] Export with `--base-dir` for linked attachments ([plan](.claude/plans/003-export-reviews.md))
12
+ - [x] Verify PDF annotation baking ([plan](.claude/plans/003-export-reviews.md))
13
+ - [x] Strip "REVIEW: " prefix from titles ([plan](.claude/plans/003-export-reviews.md))
14
+ - [x] Add `outputs/` to `.gitignore` (no plan)
15
+ - [x] Support standalone PDF attachments ([plan](.claude/plans/003-export-reviews.md))
16
+ - [x] Fix multi-attachment annotation baking ([plan](.claude/plans/003-export-reviews.md))
17
+ - [x] Rename `reviews.py` to `export.py` ([plan](.claude/plans/003-export-reviews.md))
18
+ - [x] Create parent items for standalone attachments via Zotero JS (no plan)
19
+ - [x] Extract titles from PDFs for untitled items ([plan](.claude/plans/003-export-reviews.md))
@@ -0,0 +1,35 @@
1
+ [project]
2
+ name = "zotlib"
3
+ version = "0.4.3"
4
+ description = "Extract and format bibliographic data from Zotero databases"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ dependencies = [
8
+ "typer>=0.12.0",
9
+ "rich>=13.0.0",
10
+ "pymupdf>=1.26.7,<2.0.0",
11
+ "pillow>=12.1.0,<13.0.0",
12
+ "polars>=1.39.2",
13
+ ]
14
+
15
+ [project.scripts]
16
+ zotlib = "zotlib.cli:app"
17
+
18
+ [dependency-groups]
19
+ dev = [
20
+ "pytest>=8.0.0",
21
+ "ipykernel>=6.29.0",
22
+ "ruff>=0.15.7",
23
+ "pytest-cov>=7.1.0",
24
+ ]
25
+
26
+ [build-system]
27
+ requires = ["hatchling"]
28
+ build-backend = "hatchling.build"
29
+
30
+ [tool.ruff]
31
+ target-version = "py310"
32
+ line-length = 100
33
+
34
+ [tool.ruff.lint]
35
+ select = ["E", "F", "I", "W", "UP"]
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Zotero Script: Create parent items for standalone PDF attachments
3
+ *
4
+ * Finds standalone PDF attachments in a collection and creates a parent
5
+ * document item for each, using the PDF filename (minus extension) as
6
+ * the title. The attachment is then re-parented under the new item.
7
+ *
8
+ * Usage: Run via Tools > Developer > Run JavaScript, or via the debug
9
+ * endpoint with run-create-parents.sh
10
+ */
11
+
12
+ var COLLECTION_NAME = 'reviews';
13
+ var output = [];
14
+
15
+ // reviews collection (id=7, subcollection of writing)
16
+ var collection = Zotero.Collections.get(7);
17
+
18
+ if (!collection) {
19
+ output.push("");
20
+ output.push("ERROR: Collection '" + COLLECTION_NAME + "' not found in any library.");
21
+ return output;
22
+ }
23
+
24
+ output.push("");
25
+ output.push("Found collection '" + COLLECTION_NAME + "' in library " + collection.libraryID);
26
+
27
+ var itemIDs = collection.getChildItems(true);
28
+ var items = Zotero.Items.get(itemIDs);
29
+ output.push("Items in collection: " + items.length);
30
+
31
+ var standalone = items.filter(function(item) {
32
+ return item.isAttachment()
33
+ && !item.parentItemID
34
+ && item.attachmentContentType === 'application/pdf';
35
+ });
36
+ output.push("Standalone PDFs found: " + standalone.length);
37
+
38
+ function cleanTitle(rawPath) {
39
+ // Strip "attachments:" or "storage:" prefix, then get filename without extension
40
+ var clean = rawPath.replace(/^(attachments|storage):/, '');
41
+ var filename = clean.split('/').pop().split('\\').pop();
42
+ return filename.replace(/\.pdf$/i, '').replace(/_/g, ' ') || null;
43
+ }
44
+
45
+ for (var i = 0; i < standalone.length; i++) {
46
+ var att = standalone[i];
47
+ var path = att.attachmentPath || att.attachmentFilename || '';
48
+ var title = cleanTitle(path) || ('Untitled attachment ' + att.id);
49
+
50
+ // Create parent item
51
+ var parent = new Zotero.Item('document');
52
+ parent.libraryID = att.libraryID;
53
+ parent.setField('title', title);
54
+ await parent.saveTx();
55
+
56
+ // Add parent to the same collection
57
+ collection.addItem(parent.id);
58
+ await collection.saveTx();
59
+
60
+ // Re-parent the attachment under the new item
61
+ att.parentItemID = parent.id;
62
+ await att.saveTx();
63
+
64
+ // Remove the now-child attachment from the collection
65
+ collection.removeItem(att.id);
66
+ await collection.saveTx();
67
+
68
+ output.push(" [" + (i+1) + "/" + standalone.length + "] Created parent: " + title);
69
+ }
70
+
71
+ output.push("");
72
+ output.push("Done. Created " + standalone.length + " parent items.");
73
+
74
+ return output;