├── .gitignore
├── LICENSE
├── README.md
├── icon.png
├── info.plist
├── package.json
├── query.js
└── screenshot.png
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | package-lock.json
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016-2022 Jongwook Choi
2 |
3 | Permission is hereby granted, free of charge, to any person
4 | obtaining a copy of this software and associated documentation
5 | files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use,
7 | copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the
9 | Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Alfred arXiv Workflow
2 | =====================
3 |
4 | Alfred 3-5 workflow to search via [arXiv search API](https://arxiv.org/help/api/index).
5 |
6 |
7 |
8 |
9 |
10 | Usage: `arxiv `
11 |
12 |
13 | Install
14 | -------
15 |
16 | From v2.0, the workflow is written in the [alfy](https://github.com/sindresorhus/alfy/) framework, so you can install via `npm`.
17 |
18 | ```
19 | npm install --global alfred-arxiv
20 | ```
21 |
22 | Requires Node.js >= 14.0 (ESM).
23 |
24 |
25 | Authors
26 | -------
27 |
28 | Jongwook Choi (@wookayin)
29 |
30 |
31 | License
32 | -------
33 |
34 | [The MIT License](LICENSE)
35 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wookayin/alfred-arxiv-workflow/4da9ff46b3f2426edf954c1cfb468bfaea1f818c/icon.png
--------------------------------------------------------------------------------
/info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | bundleid
6 | kr.wook.alfred-arxiv-workflow
7 | connections
8 |
9 | 04FAE5E3-4E3A-4C45-99DA-5A0A93AF188F
10 |
11 |
12 | destinationuid
13 | ABF44A93-47F8-4995-A8FB-8F972096FFAE
14 | modifiers
15 | 0
16 | modifiersubtext
17 |
18 | vitoclose
19 |
20 |
21 |
22 | destinationuid
23 | F0044CEE-2197-48CE-B31B-244C668438E9
24 | modifiers
25 | 0
26 | modifiersubtext
27 |
28 | vitoclose
29 |
30 |
31 |
32 | 684A5219-C08B-478D-9D1B-91771318AE81
33 |
34 |
35 | destinationuid
36 | 8BCEAD2E-4EC1-463D-A2AC-A2724E500902
37 | modifiers
38 | 0
39 | modifiersubtext
40 |
41 | vitoclose
42 |
43 |
44 |
45 | destinationuid
46 | B513E510-DDDB-4D68-A4DA-4AD0BED36DDD
47 | modifiers
48 | 524288
49 | modifiersubtext
50 |
51 | vitoclose
52 |
53 |
54 |
55 | destinationuid
56 | 04FAE5E3-4E3A-4C45-99DA-5A0A93AF188F
57 | modifiers
58 | 1048576
59 | modifiersubtext
60 |
61 | vitoclose
62 |
63 |
64 |
65 | destinationuid
66 | 79BC9E3D-DE43-457F-8DEA-8CCBE9993BC0
67 | modifiers
68 | 131072
69 | modifiersubtext
70 |
71 | vitoclose
72 |
73 |
74 |
75 | destinationuid
76 | CB0E287E-52F8-4B90-9F00-4DAA9175BCE9
77 | modifiers
78 | 262144
79 | modifiersubtext
80 |
81 | vitoclose
82 |
83 |
84 |
85 | 79BC9E3D-DE43-457F-8DEA-8CCBE9993BC0
86 |
87 |
88 | destinationuid
89 | ABF44A93-47F8-4995-A8FB-8F972096FFAE
90 | modifiers
91 | 0
92 | modifiersubtext
93 |
94 | vitoclose
95 |
96 |
97 |
98 | destinationuid
99 | F0044CEE-2197-48CE-B31B-244C668438E9
100 | modifiers
101 | 0
102 | modifiersubtext
103 |
104 | vitoclose
105 |
106 |
107 |
108 | CB0E287E-52F8-4B90-9F00-4DAA9175BCE9
109 |
110 |
111 | destinationuid
112 | ABF44A93-47F8-4995-A8FB-8F972096FFAE
113 | modifiers
114 | 0
115 | modifiersubtext
116 |
117 | vitoclose
118 |
119 |
120 |
121 | destinationuid
122 | 332C70B0-95E7-4908-93CA-F96D15A0E740
123 | modifiers
124 | 0
125 | modifiersubtext
126 |
127 | vitoclose
128 |
129 |
130 |
131 |
132 | createdby
133 | Jongwook Choi
134 | description
135 | Search arxiv.org
136 | disabled
137 |
138 | name
139 | arXiv Search
140 | objects
141 |
142 |
143 | config
144 |
145 | browser
146 |
147 | skipqueryencode
148 |
149 | skipvarencode
150 |
151 | spaces
152 |
153 | url
154 |
155 |
156 | type
157 | alfred.workflow.action.openurl
158 | uid
159 | 8BCEAD2E-4EC1-463D-A2AC-A2724E500902
160 | version
161 | 1
162 |
163 |
164 | config
165 |
166 | browser
167 |
168 | skipqueryencode
169 |
170 | skipvarencode
171 |
172 | spaces
173 |
174 | url
175 |
176 |
177 | type
178 | alfred.workflow.action.openurl
179 | uid
180 | B513E510-DDDB-4D68-A4DA-4AD0BED36DDD
181 | version
182 | 1
183 |
184 |
185 | config
186 |
187 | alfredfiltersresults
188 |
189 | alfredfiltersresultsmatchmode
190 | 0
191 | argumenttreatemptyqueryasnil
192 |
193 | argumenttrimmode
194 | 0
195 | argumenttype
196 | 0
197 | escaping
198 | 68
199 | keyword
200 | arxiv
201 | queuedelaycustom
202 | 3
203 | queuedelayimmediatelyinitially
204 |
205 | queuedelaymode
206 | 0
207 | queuemode
208 | 2
209 | runningsubtext
210 | Searching...
211 | script
212 | ./node_modules/.bin/run-node query.js "$1"
213 | scriptargtype
214 | 1
215 | scriptfile
216 |
217 | subtext
218 | Keyword: arxiv
219 | title
220 | Search
221 | type
222 | 0
223 | withspace
224 |
225 |
226 | type
227 | alfred.workflow.input.scriptfilter
228 | uid
229 | 684A5219-C08B-478D-9D1B-91771318AE81
230 | version
231 | 3
232 |
233 |
234 | config
235 |
236 | concurrently
237 |
238 | escaping
239 | 102
240 | script
241 | # Pass through the {query}
242 |
243 | query=$1
244 | echo -n $query
245 | scriptargtype
246 | 1
247 | scriptfile
248 |
249 | type
250 | 0
251 |
252 | type
253 | alfred.workflow.action.script
254 | uid
255 | 04FAE5E3-4E3A-4C45-99DA-5A0A93AF188F
256 | version
257 | 2
258 |
259 |
260 | config
261 |
262 | lastpathcomponent
263 |
264 | onlyshowifquerypopulated
265 |
266 | removeextension
267 |
268 | text
269 | {query}
270 | title
271 | Copied into the clipboard
272 |
273 | type
274 | alfred.workflow.output.notification
275 | uid
276 | ABF44A93-47F8-4995-A8FB-8F972096FFAE
277 | version
278 | 1
279 |
280 |
281 | config
282 |
283 | autopaste
284 |
285 | clipboardtext
286 | {query}
287 | ignoredynamicplaceholders
288 |
289 | transient
290 |
291 |
292 | type
293 | alfred.workflow.output.clipboard
294 | uid
295 | F0044CEE-2197-48CE-B31B-244C668438E9
296 | version
297 | 3
298 |
299 |
300 | config
301 |
302 | concurrently
303 |
304 | escaping
305 | 102
306 | script
307 | # Pass through the {query}
308 |
309 | query=$1
310 | echo -n $query
311 | scriptargtype
312 | 1
313 | scriptfile
314 |
315 | type
316 | 0
317 |
318 | type
319 | alfred.workflow.action.script
320 | uid
321 | 79BC9E3D-DE43-457F-8DEA-8CCBE9993BC0
322 | version
323 | 2
324 |
325 |
326 | config
327 |
328 | concurrently
329 |
330 | escaping
331 | 102
332 | script
333 | # Pass through the {query}
334 |
335 | echo -n "$1"
336 | scriptargtype
337 | 1
338 | scriptfile
339 |
340 | type
341 | 0
342 |
343 | type
344 | alfred.workflow.action.script
345 | uid
346 | CB0E287E-52F8-4B90-9F00-4DAA9175BCE9
347 | version
348 | 2
349 |
350 |
351 | config
352 |
353 | autopaste
354 |
355 | clipboardtext
356 | {query}
357 | ignoredynamicplaceholders
358 |
359 | transient
360 |
361 |
362 | type
363 | alfred.workflow.output.clipboard
364 | uid
365 | 332C70B0-95E7-4908-93CA-F96D15A0E740
366 | version
367 | 3
368 |
369 |
370 | readme
371 |
372 | uidata
373 |
374 | 04FAE5E3-4E3A-4C45-99DA-5A0A93AF188F
375 |
376 | note
377 | Copy arXiv abs URL
378 | xpos
379 | 400
380 | ypos
381 | 280
382 |
383 | 332C70B0-95E7-4908-93CA-F96D15A0E740
384 |
385 | xpos
386 | 645
387 | ypos
388 | 575
389 |
390 | 684A5219-C08B-478D-9D1B-91771318AE81
391 |
392 | xpos
393 | 120
394 | ypos
395 | 200
396 |
397 | 79BC9E3D-DE43-457F-8DEA-8CCBE9993BC0
398 |
399 | note
400 | Copy arXiv identifier
401 | xpos
402 | 400
403 | ypos
404 | 430
405 |
406 | 8BCEAD2E-4EC1-463D-A2AC-A2724E500902
407 |
408 | note
409 | Open abs page
410 | xpos
411 | 400
412 | ypos
413 | 15
414 |
415 | ABF44A93-47F8-4995-A8FB-8F972096FFAE
416 |
417 | xpos
418 | 640
419 | ypos
420 | 320
421 |
422 | B513E510-DDDB-4D68-A4DA-4AD0BED36DDD
423 |
424 | note
425 | Open PDF directly
426 | xpos
427 | 400
428 | ypos
429 | 145
430 |
431 | CB0E287E-52F8-4B90-9F00-4DAA9175BCE9
432 |
433 | note
434 | Copy BibTeX
435 | xpos
436 | 400
437 | ypos
438 | 570
439 |
440 | F0044CEE-2197-48CE-B31B-244C668438E9
441 |
442 | xpos
443 | 640
444 | ypos
445 | 430
446 |
447 |
448 | userconfigurationconfig
449 |
450 | version
451 | 2.1
452 | webaddress
453 |
454 |
455 |
456 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "alfred-arxiv",
3 | "version": "2.1.0",
4 | "description": "Alfred workflow to search arXiv.org",
5 | "author": {
6 | "name": "Jongwook Choi",
7 | "url": "https://github.com/wookayin",
8 | "email": "wookayin@gmail.com"
9 | },
10 | "homepage": "https://github.com/wookayin/alfred-arxiv-workflow",
11 | "main": "index.js",
12 | "type": "module",
13 | "license": "MIT",
14 | "dependencies": {
15 | "alfred-npms": "^2.1.0",
16 | "alfy": "^0.12.1",
17 | "arxiv-api": "^1.1.1"
18 | },
19 | "scripts": {
20 | "postinstall": "alfy-init",
21 | "preuninstall": "alfy-cleanup"
22 | },
23 | "keywords": [
24 | "Alfred",
25 | "Alfred workflow",
26 | "alfy",
27 | "arxiv"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/query.js:
--------------------------------------------------------------------------------
1 |
2 | import alfy from 'alfy';
3 | import arxiv from 'arxiv-api';
4 |
5 | const M = {};
6 |
7 | M.query_arxiv = async function(input) {
8 | // query arxiv API.
9 | input = input.replace('-', ' ');
10 | var papers = await arxiv.search({
11 | searchQueryParams: [
12 | {
13 | include: [{name: input}],
14 | },
15 | ],
16 | start: 0,
17 | maxResults: 25,
18 | });
19 |
20 | // Clean up the JSON entry.
21 | papers = papers.map(function(p) {
22 | p.title = p.title.replace(/\s*\n\s*/, ' ');
23 | p.authors = p.authors.map(x => x.flat()[0]);
24 | p.url = p.id; // "https://arxiv.org/abs/1234.56789v2"
25 |
26 | var match = p.url.match(/^https?:\/\/arxiv.org\/(pdf|abs)\/([\w.\-\/]+)(v\d+)(\.pdf)?$/);
27 | var id = (match || [])[2];
28 | var ver = (match || [])[3];
29 |
30 | p.id = id; // 1234.56789
31 | p.ver = ver; // v2
32 | p.canonical_url = `https://arxiv.org/abs/${id}`;
33 | p.year = p.published.substring(0, 4);
34 | return p;
35 | });
36 |
37 | // TODO: employ better ranking.
38 | return papers;
39 | };
40 |
41 | M.generate_bibtex = function(p) {
42 | // Use google scholar style bibitem identifier.
43 | // e.g., @article{lastname2021firstword ... }
44 | // TODO: Make it configurable.
45 | var repr_author = p.authors[0].split(' ');
46 | repr_author = repr_author[repr_author.length - 1].toLowerCase(); // last name
47 | var title_word = p.title.replace('-', ' ').split(' ')[0].toLowerCase();
48 |
49 | return `@article{${repr_author}${p.year}${title_word},
50 | title = {{${p.title}}},
51 | author = {${p.authors.join(' and ')}},
52 | journal = {arXiv preprint arXiv:${p.id}},
53 | year = {${p.year}},
54 | }
55 | `;
56 | };
57 |
58 | M.to_alfred_items = function(papers) {
59 | var paper_to_entry = function(p) {
60 | // p: arXiv paper entry.
61 | return {
62 | //title: p.title,
63 | title: p.id ? `[${p.id}] ${p.title}` : p.title,
64 | subtitle: p.authors.join(', '),
65 | arg: p.canonical_url,
66 | url: p.url,
67 | text: {
68 | // text to copy, cmd+C
69 | copy: `[${p.title}](${p.canonical_url})`,
70 | // largetype display text, cmd+L
71 | largetype: p.title + "\n\n" + p.authors.join(', ') + "\n" + p.canonical_url,
72 | },
73 | mods: {
74 | // alt(option): Open the PDF instead of abstract page.
75 | alt: {subtitle: `Open http://arxiv.org/pdf/${p.id}`,
76 | arg: `http://arxiv.org/pdf/${p.id}`},
77 | // cmd: Copy the url of the abstract page.
78 | cmd: {subtitle: `http://arxiv.org/abs/${p.id}`},
79 | // shift: Copy to the clipboard the identifier.
80 | shift: {subtitle: `Copy the identifier to the clipboard: ${p.id}`,
81 | arg: `${p.id}`},
82 | // ctrl: Copy to the clipboard a BibTex entry.
83 | ctrl: {subtitle: `Copy BibTex entry of ${p.id} to the clipboard`,
84 | arg: M.generate_bibtex(p)
85 | },
86 | }
87 | }
88 | };
89 |
90 | return papers.map(paper_to_entry);
91 | };
92 |
93 | M.query = async function(input) {
94 | var papers = await M.query_arxiv(input);
95 | return M.to_alfred_items(papers);
96 | };
97 |
98 | // Execute when running from Alfred.
99 | const argv = process.argv.slice(2);
100 | if (process.env.alfred_workflow_name) {
101 | alfy.output(await M.query(alfy.input));
102 | }
103 | else if (argv.length) {
104 | var result = await M.query(argv.join(' '));
105 | //var result = await M.query_arxiv(argv.join(' '));
106 | console.log(result);
107 | }
108 |
109 | const query = M;
110 | export default query;
111 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wookayin/alfred-arxiv-workflow/4da9ff46b3f2426edf954c1cfb468bfaea1f818c/screenshot.png
--------------------------------------------------------------------------------