├── .gitattributes ├── icon.png ├── icons ├── Back.png ├── Flag.png ├── Http.png ├── ID.png ├── Noty.png ├── Tag.png ├── anki.png ├── deck.png ├── del.png ├── B_Note.png ├── Extra.png ├── F_Note.png ├── Front.png ├── Model.png ├── Reset.png ├── Video.png ├── class.png ├── delete.png ├── ID_marked.png ├── Profile.png ├── refresh.png ├── settings.png ├── warning.png ├── Back_marked.png ├── Flag_marked.png ├── Http_marked.png ├── Noty_marked.png ├── Tag_marked.png ├── B_Note_marked.png ├── Extra_marked.png ├── F_Note_marked.png ├── Front_marked.png ├── Video_marked.png ├── class_marked.png ├── deck-settings.png ├── night_and_day.png ├── not-connected.png ├── for-dark-theme │ ├── Back.png │ ├── Flag.png │ ├── Http.png │ ├── ID.png │ ├── deck.png │ ├── noty.png │ ├── tag.png │ ├── B_Note.png │ ├── F_Note.png │ ├── Front.png │ ├── Model.png │ ├── class.png │ ├── delete.png │ ├── video.png │ ├── ID_marked.png │ ├── Profile.png │ ├── refresh.png │ ├── settings.png │ ├── Back_marked.png │ ├── Flag_marked.png │ ├── Http_marked.png │ ├── noty_marked.png │ ├── tag_marked.png │ ├── B_Note_marked.png │ ├── Extra_marked.png │ ├── F_Note_marked.png │ ├── Front_marked.png │ ├── class_marked.png │ ├── deck-settings.png │ ├── night_and_day.png │ └── video_marked.png └── for-light-theme │ ├── ID.png │ ├── Tag.png │ ├── Back.png │ ├── Extra.png │ ├── Flag.png │ ├── Front.png │ ├── Http.png │ ├── Model.png │ ├── Noty.png │ ├── class.png │ ├── deck.png │ ├── video.png │ ├── B_Note.png │ ├── F_Note.png │ ├── Profile.png │ ├── delete.png │ ├── refresh.png │ ├── settings.png │ ├── ID_marked.png │ ├── Tag_marked.png │ ├── B_Note_marked.png │ ├── Back_marked.png │ ├── Extra_marked.png │ ├── F_Note_marked.png │ ├── Flag_marked.png │ ├── Front_marked.png │ ├── Http_marked.png │ ├── Noty_marked.png │ ├── class_marked.png │ ├── deck-settings.png │ ├── night_and_day.png │ └── video_marked.png ├── readme ├── main-window.png ├── settings-window.png └── preview-of-card-field.png ├── .travis.yml ├── .yo-rc.json ├── test.js ├── src ├── anki │ ├── anki-tags.js │ ├── anki-delete-deck.js │ ├── anki-cards.js │ ├── anki-connect.js │ ├── anki-profiles.js │ ├── anki-decks.js │ ├── anki-models.js │ ├── anki-info.js │ └── anki-add-card.js ├── utils │ ├── index.js │ ├── engine.js │ └── error.js ├── input │ └── preview │ │ ├── main.css │ │ ├── monokai.css │ │ ├── preview.hbs │ │ └── normalize.css ├── wf │ ├── fields.js │ ├── header.js │ └── index.js ├── index.js ├── config │ └── index.js └── cmd │ ├── theme.js │ ├── del.js │ ├── profiles.js │ ├── reset.js │ ├── models.js │ ├── decks.js │ └── refresh.js ├── .vscode └── launch.json ├── LICENSE ├── .gitignore ├── package.json ├── index.js ├── update-config.js ├── readme.md └── info.plist /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text eol=lf 3 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icon.png -------------------------------------------------------------------------------- /icons/Back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Back.png -------------------------------------------------------------------------------- /icons/Flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Flag.png -------------------------------------------------------------------------------- /icons/Http.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Http.png -------------------------------------------------------------------------------- /icons/ID.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/ID.png -------------------------------------------------------------------------------- /icons/Noty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Noty.png -------------------------------------------------------------------------------- /icons/Tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Tag.png -------------------------------------------------------------------------------- /icons/anki.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/anki.png -------------------------------------------------------------------------------- /icons/deck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/deck.png -------------------------------------------------------------------------------- /icons/del.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/del.png -------------------------------------------------------------------------------- /icons/B_Note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/B_Note.png -------------------------------------------------------------------------------- /icons/Extra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Extra.png -------------------------------------------------------------------------------- /icons/F_Note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/F_Note.png -------------------------------------------------------------------------------- /icons/Front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Front.png -------------------------------------------------------------------------------- /icons/Model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Model.png -------------------------------------------------------------------------------- /icons/Reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Reset.png -------------------------------------------------------------------------------- /icons/Video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Video.png -------------------------------------------------------------------------------- /icons/class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/class.png -------------------------------------------------------------------------------- /icons/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/delete.png -------------------------------------------------------------------------------- /icons/ID_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/ID_marked.png -------------------------------------------------------------------------------- /icons/Profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Profile.png -------------------------------------------------------------------------------- /icons/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/refresh.png -------------------------------------------------------------------------------- /icons/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/settings.png -------------------------------------------------------------------------------- /icons/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/warning.png -------------------------------------------------------------------------------- /icons/Back_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Back_marked.png -------------------------------------------------------------------------------- /icons/Flag_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Flag_marked.png -------------------------------------------------------------------------------- /icons/Http_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Http_marked.png -------------------------------------------------------------------------------- /icons/Noty_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Noty_marked.png -------------------------------------------------------------------------------- /icons/Tag_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Tag_marked.png -------------------------------------------------------------------------------- /icons/B_Note_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/B_Note_marked.png -------------------------------------------------------------------------------- /icons/Extra_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Extra_marked.png -------------------------------------------------------------------------------- /icons/F_Note_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/F_Note_marked.png -------------------------------------------------------------------------------- /icons/Front_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Front_marked.png -------------------------------------------------------------------------------- /icons/Video_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/Video_marked.png -------------------------------------------------------------------------------- /icons/class_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/class_marked.png -------------------------------------------------------------------------------- /icons/deck-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/deck-settings.png -------------------------------------------------------------------------------- /icons/night_and_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/night_and_day.png -------------------------------------------------------------------------------- /icons/not-connected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/not-connected.png -------------------------------------------------------------------------------- /readme/main-window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/readme/main-window.png -------------------------------------------------------------------------------- /readme/settings-window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/readme/settings-window.png -------------------------------------------------------------------------------- /icons/for-dark-theme/Back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/Back.png -------------------------------------------------------------------------------- /icons/for-dark-theme/Flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/Flag.png -------------------------------------------------------------------------------- /icons/for-dark-theme/Http.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/Http.png -------------------------------------------------------------------------------- /icons/for-dark-theme/ID.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/ID.png -------------------------------------------------------------------------------- /icons/for-dark-theme/deck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/deck.png -------------------------------------------------------------------------------- /icons/for-dark-theme/noty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/noty.png -------------------------------------------------------------------------------- /icons/for-dark-theme/tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/tag.png -------------------------------------------------------------------------------- /icons/for-light-theme/ID.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/ID.png -------------------------------------------------------------------------------- /icons/for-light-theme/Tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/Tag.png -------------------------------------------------------------------------------- /icons/for-dark-theme/B_Note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/B_Note.png -------------------------------------------------------------------------------- /icons/for-dark-theme/F_Note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/F_Note.png -------------------------------------------------------------------------------- /icons/for-dark-theme/Front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/Front.png -------------------------------------------------------------------------------- /icons/for-dark-theme/Model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/Model.png -------------------------------------------------------------------------------- /icons/for-dark-theme/class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/class.png -------------------------------------------------------------------------------- /icons/for-dark-theme/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/delete.png -------------------------------------------------------------------------------- /icons/for-dark-theme/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/video.png -------------------------------------------------------------------------------- /icons/for-light-theme/Back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/Back.png -------------------------------------------------------------------------------- /icons/for-light-theme/Extra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/Extra.png -------------------------------------------------------------------------------- /icons/for-light-theme/Flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/Flag.png -------------------------------------------------------------------------------- /icons/for-light-theme/Front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/Front.png -------------------------------------------------------------------------------- /icons/for-light-theme/Http.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/Http.png -------------------------------------------------------------------------------- /icons/for-light-theme/Model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/Model.png -------------------------------------------------------------------------------- /icons/for-light-theme/Noty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/Noty.png -------------------------------------------------------------------------------- /icons/for-light-theme/class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/class.png -------------------------------------------------------------------------------- /icons/for-light-theme/deck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/deck.png -------------------------------------------------------------------------------- /icons/for-light-theme/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/video.png -------------------------------------------------------------------------------- /icons/for-dark-theme/ID_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/ID_marked.png -------------------------------------------------------------------------------- /icons/for-dark-theme/Profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/Profile.png -------------------------------------------------------------------------------- /icons/for-dark-theme/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/refresh.png -------------------------------------------------------------------------------- /icons/for-dark-theme/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/settings.png -------------------------------------------------------------------------------- /icons/for-light-theme/B_Note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/B_Note.png -------------------------------------------------------------------------------- /icons/for-light-theme/F_Note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/F_Note.png -------------------------------------------------------------------------------- /icons/for-light-theme/Profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/Profile.png -------------------------------------------------------------------------------- /icons/for-light-theme/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/delete.png -------------------------------------------------------------------------------- /icons/for-light-theme/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/refresh.png -------------------------------------------------------------------------------- /icons/for-light-theme/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/settings.png -------------------------------------------------------------------------------- /readme/preview-of-card-field.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/readme/preview-of-card-field.png -------------------------------------------------------------------------------- /icons/for-dark-theme/Back_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/Back_marked.png -------------------------------------------------------------------------------- /icons/for-dark-theme/Flag_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/Flag_marked.png -------------------------------------------------------------------------------- /icons/for-dark-theme/Http_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/Http_marked.png -------------------------------------------------------------------------------- /icons/for-dark-theme/noty_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/noty_marked.png -------------------------------------------------------------------------------- /icons/for-dark-theme/tag_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/tag_marked.png -------------------------------------------------------------------------------- /icons/for-light-theme/ID_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/ID_marked.png -------------------------------------------------------------------------------- /icons/for-light-theme/Tag_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/Tag_marked.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '7' 4 | 5 | # safelist 6 | branches: 7 | only: 8 | - master 9 | - stable -------------------------------------------------------------------------------- /icons/for-dark-theme/B_Note_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/B_Note_marked.png -------------------------------------------------------------------------------- /icons/for-dark-theme/Extra_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/Extra_marked.png -------------------------------------------------------------------------------- /icons/for-dark-theme/F_Note_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/F_Note_marked.png -------------------------------------------------------------------------------- /icons/for-dark-theme/Front_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/Front_marked.png -------------------------------------------------------------------------------- /icons/for-dark-theme/class_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/class_marked.png -------------------------------------------------------------------------------- /icons/for-dark-theme/deck-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/deck-settings.png -------------------------------------------------------------------------------- /icons/for-dark-theme/night_and_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/night_and_day.png -------------------------------------------------------------------------------- /icons/for-dark-theme/video_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-dark-theme/video_marked.png -------------------------------------------------------------------------------- /icons/for-light-theme/B_Note_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/B_Note_marked.png -------------------------------------------------------------------------------- /icons/for-light-theme/Back_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/Back_marked.png -------------------------------------------------------------------------------- /icons/for-light-theme/Extra_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/Extra_marked.png -------------------------------------------------------------------------------- /icons/for-light-theme/F_Note_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/F_Note_marked.png -------------------------------------------------------------------------------- /icons/for-light-theme/Flag_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/Flag_marked.png -------------------------------------------------------------------------------- /icons/for-light-theme/Front_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/Front_marked.png -------------------------------------------------------------------------------- /icons/for-light-theme/Http_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/Http_marked.png -------------------------------------------------------------------------------- /icons/for-light-theme/Noty_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/Noty_marked.png -------------------------------------------------------------------------------- /icons/for-light-theme/class_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/class_marked.png -------------------------------------------------------------------------------- /icons/for-light-theme/deck-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/deck-settings.png -------------------------------------------------------------------------------- /icons/for-light-theme/night_and_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/night_and_day.png -------------------------------------------------------------------------------- /icons/for-light-theme/video_marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikenik/alfred-anki/HEAD/icons/for-light-theme/video_marked.png -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-alfred": { 3 | "promptValues": { 4 | "githubUsername": "bikenik", 5 | "website": "http://bikenik.org" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | 3 | test('foo', t => { 4 | t.pass() 5 | }) 6 | 7 | test('bar', async t => { 8 | const bar = Promise.resolve('bar') 9 | 10 | t.is(await bar, 'bar') 11 | }) 12 | -------------------------------------------------------------------------------- /src/anki/anki-tags.js: -------------------------------------------------------------------------------- 1 | const ankiConnect = require('./anki-connect') 2 | 3 | module.exports.getTags = async () => { 4 | try { 5 | const getTags = await ankiConnect('getTags', 6) 6 | return getTags 7 | } catch (error) { 8 | return error 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/anki/anki-delete-deck.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | const ankiConnect = require('./anki-connect') 3 | 4 | const {config_value} = process.env 5 | const deletDeck = async () => { 6 | try { 7 | const result = await ankiConnect( 8 | 'deleteDecks', 6, 9 | { 10 | decks: [config_value], 11 | cardsToo: true 12 | } 13 | ) 14 | return result 15 | } catch (error) { 16 | return error 17 | } 18 | } 19 | 20 | deletDeck() 21 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "program": "${file}", 12 | "args": [ 13 | "${file}" 14 | ] 15 | }, 16 | { 17 | "type": "node", 18 | "request": "launch", 19 | "name": "Run AVA test", 20 | "program": "${workspaceRoot}/node_modules/ava/profile.js", 21 | "args": [ 22 | "${file}" 23 | ], 24 | "skipFiles": [ 25 | "/**/*.js" 26 | ] 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | const alfy = require('alfy') 2 | 3 | /* eslint-disable no-extend-native */ 4 | /* eslint-disable-next-line no-use-extend-native/no-use-extend-native */ 5 | String.prototype.replaceAll = function (search, replacement) { 6 | const target = this 7 | return target.replace(new RegExp(search, 'gi'), replacement) 8 | } 9 | 10 | /* eslint-disable-next-line no-use-extend-native/no-use-extend-native */ 11 | Array.prototype.last = function () { 12 | return this[this.length - 1] 13 | } 14 | /* eslint-enable no-extend-native */ 15 | 16 | module.exports = { 17 | wordOfURL: alfy.config.get('wordOfURL') 18 | } 19 | 20 | module.exports.capitalize = x => 21 | x.charAt(0).toUpperCase() + x.slice(1) 22 | 23 | module.exports.hasOwnProperty = (obj, prop) => 24 | Object.prototype.hasOwnProperty.call(obj, prop) 25 | -------------------------------------------------------------------------------- /src/anki/anki-cards.js: -------------------------------------------------------------------------------- 1 | const ankiConnect = require('./anki-connect') 2 | 3 | module.exports.cards = async decks => { 4 | const ankiCards = [] 5 | try { 6 | /* eslint-disable no-await-in-loop */ 7 | for (const deck of decks) { 8 | ankiCards.push(await ankiConnect('findCards', 6, 9 | {query: `"deck:${deck}"`}) 10 | ) 11 | } 12 | /* eslint-enable no-await-in-loop */ 13 | } catch (error) { 14 | return error 15 | } 16 | 17 | return ankiCards 18 | } 19 | 20 | module.exports.areDue = async cards => { 21 | try { 22 | const ankiCards = await ankiConnect('areDue', 6, 23 | {cards}) 24 | return ankiCards 25 | } catch (error) { 26 | return error 27 | } 28 | } 29 | 30 | module.exports.areSuspend = async cards => { 31 | try { 32 | const ankiCards = await ankiConnect('areSuspended', 6, 33 | {cards}) 34 | return ankiCards 35 | } catch (error) { 36 | return error 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/anki/anki-connect.js: -------------------------------------------------------------------------------- 1 | const {XMLHttpRequest} = require('xmlhttprequest') 2 | const Promise = require('promise') 3 | 4 | const WorkflowError = require('../utils/error') 5 | const {errorAction} = require('../utils/error') 6 | 7 | module.exports = function (action, version, params) { 8 | return new Promise((resolve, reject) => { 9 | const xhr = new XMLHttpRequest() 10 | xhr.addEventListener('error', () => 11 | reject(new WorkflowError(errorAction('main').title, errorAction('main'))) 12 | ) 13 | xhr.addEventListener('load', () => { 14 | try { 15 | const response = JSON.parse(xhr.responseText) 16 | if (response.error) { 17 | throw response.error 18 | } 19 | 20 | if (Object.prototype.hasOwnProperty.call(response, 'result')) { 21 | resolve(response.result) 22 | } 23 | 24 | reject(new Error('failed to get results from AnkiConnect')) 25 | } catch (error) { 26 | reject(error) 27 | } 28 | }) 29 | 30 | xhr.open('POST', 'http://127.0.0.1:8765') 31 | xhr.send( 32 | JSON.stringify({ 33 | action, 34 | version, 35 | params 36 | }) 37 | ) 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Bikeof Nikolay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/input/preview/main.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | -moz-box-sizing: border-box; 3 | -webkit-box-sizing: border-box; 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | font-family: 'Nunito', sans-serif; 9 | color: #384047; 10 | background-color: #5c1f87; 11 | } 12 | 13 | table { 14 | width: 95%; 15 | margin: 10px auto; 16 | } 17 | 18 | caption { 19 | font-size: 1.6em; 20 | font-weight: 400; 21 | padding: 10px 0; 22 | color: #FFF; 23 | } 24 | 25 | thead th { 26 | font-weight: 400; 27 | background: #8a97a0; 28 | color: #FFF; 29 | } 30 | 31 | tr { 32 | background: #f4f7f8; 33 | border-bottom: 1px solid #FFF; 34 | margin-bottom: 5px; 35 | } 36 | 37 | tr:nth-child(even) { 38 | background: #e8eeef; 39 | } 40 | 41 | th, td { 42 | text-align: left; 43 | padding: 20px; 44 | font-weight: 300; 45 | } 46 | 47 | tfoot tr { 48 | background: none; 49 | } 50 | 51 | tfoot td { 52 | padding: 10px 2px; 53 | font-size: 1em; 54 | font-style: italic; 55 | color: #a4b2bd; 56 | } 57 | 58 | p code { 59 | white-space: pre; 60 | white-space: pre-wrap; 61 | white-space: -moz-pre-wrap; 62 | white-space: -o-pre-wrap; 63 | background: #ddd; 64 | margin: 0; 65 | padding: 0.1em 0.3em; 66 | border: none; 67 | background: #ddd; 68 | } -------------------------------------------------------------------------------- /src/input/preview/monokai.css: -------------------------------------------------------------------------------- 1 | /* 2 | Monokai style - ported by Luigi Maselli - http://grigio.org 3 | */ 4 | 5 | .hljs { 6 | display: block; 7 | overflow-x: auto; 8 | padding: 0.5em; 9 | background: #272822; color: #ddd; 10 | } 11 | 12 | .hljs-tag, 13 | .hljs-keyword, 14 | .hljs-selector-tag, 15 | .hljs-literal, 16 | .hljs-strong, 17 | .hljs-name { 18 | color: #f92672; 19 | } 20 | 21 | .hljs-code { 22 | color: #66d9ef; 23 | } 24 | 25 | .hljs-class .hljs-title { 26 | color: white; 27 | } 28 | 29 | .hljs-attribute, 30 | .hljs-symbol, 31 | .hljs-regexp, 32 | .hljs-link { 33 | color: #bf79db; 34 | } 35 | 36 | .hljs-string, 37 | .hljs-bullet, 38 | .hljs-subst, 39 | .hljs-title, 40 | .hljs-section, 41 | .hljs-emphasis, 42 | .hljs-type, 43 | .hljs-built_in, 44 | .hljs-builtin-name, 45 | .hljs-selector-attr, 46 | .hljs-selector-pseudo, 47 | .hljs-addition, 48 | .hljs-variable, 49 | .hljs-template-tag, 50 | .hljs-template-variable { 51 | color: #a6e22e; 52 | } 53 | 54 | .hljs-comment, 55 | .hljs-quote, 56 | .hljs-deletion, 57 | .hljs-meta { 58 | color: #75715e; 59 | } 60 | 61 | .hljs-keyword, 62 | .hljs-selector-tag, 63 | .hljs-literal, 64 | .hljs-doctag, 65 | .hljs-title, 66 | .hljs-section, 67 | .hljs-type, 68 | .hljs-selector-id { 69 | font-weight: bold; 70 | } 71 | -------------------------------------------------------------------------------- /src/input/preview/preview.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Preview Anki Card 8 | 10 | 11 | 13 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | {{FIELD}} 39 | 40 |
Preview Of Card Fields
Field NameContent
Data is updated every time when you edit field.
41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/node 2 | 3 | ### Node ### 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (http://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # Typescript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | #other dev tools 64 | *.codekit3 65 | .editorconfig 66 | settings.json 67 | 68 | #temporary folder 69 | 70 | #other 71 | .DS_Store 72 | mytest/ 73 | anki-model-css.js 74 | 75 | # End of https://www.gitignore.io/api/node -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alfred-anki", 3 | "version": "1.3.1", 4 | "description": "anki card generator", 5 | "license": "MIT", 6 | "repository": "bikenik/alfred-anki", 7 | "author": { 8 | "name": "bikenik", 9 | "email": "bikeniks@gmail.com", 10 | "url": "https://bikenik.org" 11 | }, 12 | "engines": { 13 | "node": ">=4" 14 | }, 15 | "scripts": { 16 | "test": "xo && ava", 17 | "postinstall": "alfy-init", 18 | "preuninstall": "alfy-cleanup" 19 | }, 20 | "files": [ 21 | "index.js", 22 | "icon.png", 23 | "info.plist", 24 | "src", 25 | "icons", 26 | "update-config.js" 27 | ], 28 | "keywords": [ 29 | "alfred", 30 | "workflow", 31 | "alfy" 32 | ], 33 | "dependencies": { 34 | "alfy": "0.9.0", 35 | "fs-extra": "^8.1.0", 36 | "handlebars": "^4.1.2", 37 | "highlight.js": "^9.15.8", 38 | "html-entities": "^1.2.1", 39 | "jsonfile": "^5.0.0", 40 | "markdown-it": "^9.0.1", 41 | "markdown-it-abbr": "^1.0.4", 42 | "markdown-it-emoji": "^1.4.0", 43 | "markdown-it-ins": "^2.0.0", 44 | "markdown-it-mark": "^2.0.0", 45 | "p-map": "^3.0.0", 46 | "path-exists": "^4.0.0", 47 | "promise": "^8.0.3", 48 | "run-applescript": "^3.2.0", 49 | "xmlhttprequest": "^1.8.0" 50 | }, 51 | "devDependencies": { 52 | "ava": "^2.2.0", 53 | "xo": "^0.24.0" 54 | }, 55 | "xo": { 56 | "space": false, 57 | "semicolon": false, 58 | "capitalized-comments": [ 59 | "error", 60 | "always", 61 | { 62 | "block": { 63 | "ignorePattern": "blockignore" 64 | } 65 | } 66 | ] 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/anki/anki-profiles.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | const alfy = require('alfy') 3 | const jsonfile = require('jsonfile') 4 | 5 | const ankiConnect = require('./anki-connect') 6 | 7 | const fileAnkiProfiles = `${process.env.alfred_workflow_data}/anki-profiles.json` 8 | 9 | module.exports = async () => { 10 | try { 11 | const result = await ankiConnect('loadProfile', 6, 12 | {name: alfy.config.get('default-profile') ? alfy.config.get('default-profile') : 'Hello world'}) 13 | 14 | jsonfile.writeFile(fileAnkiProfiles, result === false ? [] : typeof (result) === 'boolean' ? [] : result, { 15 | spaces: 2 16 | }, error => { 17 | if (error !== null) { 18 | console.log(error) 19 | } 20 | }) 21 | return result 22 | } catch (error) { 23 | return error 24 | } 25 | } 26 | 27 | module.exports.render = async (pattern = '', autocomplete = () => undefined, ankiDecks, cmdIcon) => { 28 | const out = await alfy.matches(pattern, ankiDecks) 29 | .map(name => ({ 30 | title: name, 31 | subtitle: ankiDecks[name], 32 | autocomplete: autocomplete(name), 33 | valid: false, 34 | icon: { 35 | path: cmdIcon 36 | } 37 | })) 38 | if (out.length === 0) { 39 | out.push({ 40 | title: `Profile name is '${pattern}'`, 41 | subtitle: `Old value ⇒ ${alfy.config.get('default-profile')}`, 42 | valid: true, 43 | arg: JSON.stringify({ 44 | alfredworkflow: { 45 | variables: { 46 | action: 'set', 47 | config_variable_profile: 'default-profile', 48 | config_value: pattern 49 | } 50 | } 51 | }) 52 | }) 53 | return out 54 | } 55 | 56 | return out 57 | } 58 | -------------------------------------------------------------------------------- /src/anki/anki-decks.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | const alfy = require('alfy') 3 | 4 | const WorkflowError = require('../utils/error') 5 | const {errorAction} = require('../utils/error') 6 | const ankiConnect = require('./anki-connect') 7 | 8 | module.exports = () => { 9 | const outresult = async function () { 10 | try { 11 | alfy.cache.set('validOutput', 'true') 12 | const resultAll = await ankiConnect('deckNames', 6) 13 | return resultAll 14 | } catch (error) { 15 | alfy.cache.set('validOutput', 'false') 16 | throw new WorkflowError(error, error === 'failed to connect to AnkiConnect' ? errorAction('main') : error === 'collection is not available' ? errorAction('profile') : /model was not found/.test(error) ? errorAction('modelExist') : errorAction('main')) 17 | } 18 | } 19 | 20 | return outresult() 21 | } 22 | 23 | module.exports.render = async (pattern = '', autocomplete = () => undefined, ankiDecks, cmdIcon) => { 24 | const out = await alfy.matches(pattern, Object.getOwnPropertyNames(ankiDecks).sort()) 25 | .map(name => ({ 26 | title: name, 27 | subtitle: ankiDecks[name], 28 | autocomplete: autocomplete(name), 29 | valid: false, 30 | icon: { 31 | path: cmdIcon 32 | } 33 | })) 34 | if (out.length === 0) { 35 | out.push({ 36 | title: `Create new Deck as '${pattern}'`, 37 | subtitle: `Old value ⇒ ${alfy.config.get('default-deck')}`, 38 | valid: true, 39 | arg: JSON.stringify({ 40 | alfredworkflow: { 41 | variables: { 42 | action: 'set', 43 | config_variable_deck: 'default-deck', 44 | config_value: pattern 45 | } 46 | } 47 | }) 48 | }) 49 | return out 50 | } 51 | 52 | return out 53 | } 54 | -------------------------------------------------------------------------------- /src/wf/fields.js: -------------------------------------------------------------------------------- 1 | /* eslint no-extend-native: ["error", { "exceptions": ["String"] }] */ 2 | const fs = require('fs') 3 | const alfy = require('alfy') 4 | const {getTags} = require('../anki/anki-tags') 5 | const {Render} = require('../utils/engine') 6 | 7 | const modelFieldNames = require(`${process.env.alfred_workflow_data}/anki-model-fields.json`) 8 | 9 | /* eslint-disable-next-line no-use-extend-native/no-use-extend-native */ 10 | String.prototype.replaceAll = function (search, replacement) { 11 | const target = this 12 | return target.replace(new RegExp(search, 'gi'), replacement) 13 | } 14 | 15 | for (const field of modelFieldNames) { 16 | if (process.env.mode === field) { 17 | const input = alfy.input.replace(/\\n(\s|)/g, '\n') 18 | alfy.output([{ 19 | title: input, 20 | subtitle: `✏️ ${field}: ...`, 21 | text: {largetype: input}, 22 | icon: {path: fs.existsSync(`./icons/${field}.png`) ? `./icons/${field}.png` : './icons/Flag.png'}, 23 | arg: JSON.stringify({[field]: input}) 24 | }]) 25 | } 26 | } 27 | 28 | if (process.env.mode === 'Tags') { 29 | const showTags = async () => { 30 | const tags = await getTags() 31 | tags.unshift('') 32 | const items = [] 33 | for (const tag of tags) { 34 | const item = new Render('tags', 35 | 'title', 'subtitle', 'arg', 'icon') 36 | item.title = tag 37 | item.subtitle = 'some subtitle' 38 | item.arg = JSON.stringify({Tag: tag}) 39 | item.icon = './icons/tag.png' 40 | items.push(item.getProperties()) 41 | } 42 | 43 | if (alfy.inputMatches(items, 'title').length > 0) { 44 | alfy.output(alfy.inputMatches(items, 'title')) 45 | } else { 46 | const tag = alfy.input.replaceAll(/\s/, '_').replaceAll(/,/, ' ').replaceAll(/\s_/, ' ') 47 | alfy.output([{ 48 | title: `Add - [${tag}] - as your new tag`, 49 | subtitle: 'add <, > for several tags', 50 | arg: JSON.stringify({Tag: tag}) 51 | }]) 52 | } 53 | } 54 | 55 | showTags() 56 | } 57 | -------------------------------------------------------------------------------- /src/wf/header.js: -------------------------------------------------------------------------------- 1 | /* eslint camelcase: ["error", {properties: "never"}] */ 2 | /* eslint-disable guard-for-in */ 3 | /* eslint-env es6 */ 4 | 5 | const alfy = require('alfy') 6 | const jsonfile = require('jsonfile') 7 | 8 | const fileHeader = `${process.env.alfred_workflow_data}/header.json` 9 | const headerJson = require(fileHeader) 10 | 11 | const modelId = alfy.config.get('default-model') ? alfy.config.get('default-model')[Object.keys(alfy.config.get('default-model'))[0]] : null 12 | const currentConfig = alfy.config.get('fields') ? alfy.config.get('fields')[modelId] : null 13 | 14 | const resetHeader = header => { 15 | jsonfile.writeFile(fileHeader, header, { 16 | spaces: 2 17 | }, error => { 18 | if (error !== null) { 19 | console.error(error) 20 | } 21 | }) 22 | } 23 | 24 | if (process.env.action === 'reset-for-next-card' && modelId) { 25 | const header = {} 26 | 27 | for (const key2 in currentConfig) { 28 | for (const key in headerJson[modelId]) { 29 | if (key === key2 && currentConfig[key2] === 'rli') { 30 | header[key] = headerJson[modelId][key] 31 | } 32 | 33 | if (key === key2 && currentConfig[key2] === 'not_rli') { 34 | header[key] = '' 35 | } 36 | 37 | if (key === 'Tag' && alfy.config.get('Tag') === 'rli') { 38 | header[key] = headerJson[modelId].Tag 39 | } 40 | 41 | if (key === 'Tag' && alfy.config.get('Tag') === 'not_rli') { 42 | header[key] = '' 43 | } 44 | } 45 | } 46 | 47 | headerJson[modelId] = header 48 | resetHeader(headerJson) 49 | } else if (process.env.action === 'reset') { 50 | headerJson[modelId] = {} 51 | resetHeader(headerJson) 52 | } else if (process.argv[2]) { 53 | const header = headerJson ? headerJson : {[modelId]: {}} 54 | const currentData = JSON.parse(process.argv[2]) 55 | 56 | const currentProperty = Object.keys(currentData)[0] 57 | if (header[modelId]) { 58 | header[modelId][currentProperty] = currentData[currentProperty] 59 | } else { 60 | header[modelId] = {} 61 | 62 | header[modelId][currentProperty] = currentData[currentProperty] 63 | } 64 | 65 | jsonfile.writeFile(fileHeader, header, { 66 | spaces: 2 67 | }, error => { 68 | if (error !== null) { 69 | console.error(error) 70 | } 71 | }) 72 | } 73 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const alfy = require('alfy') 3 | const runApplescript = require('run-applescript') 4 | 5 | const set = require('./src/cmd/decks') 6 | const del = require('./src/cmd/del') 7 | const refresh = require('./src/cmd/refresh') 8 | const theme = require('./src/cmd/theme') 9 | const models = require('./src/cmd/models') 10 | const profiles = require('./src/cmd/profiles') 11 | const reset = require('./src/cmd/reset') 12 | const ankiInfo = require('./src/anki/anki-info') 13 | const wf = require('./src/wf') 14 | const WorkflowError = require('./src/utils/error') 15 | 16 | const commands = [set, models, profiles, del, refresh, theme, reset] 17 | const option = async input => { 18 | for (const command of commands) { 19 | if (command.match(input)) { 20 | return command(input) 21 | } 22 | } 23 | 24 | // No matches, show all commands 25 | if (/!.*/.test(input)) { 26 | const options = commands.map(command => ({ 27 | title: command.meta.name, 28 | subtitle: `${command.meta.help} | Usage: ${command.meta.usage}`, 29 | autocomplete: command.meta.autocomplete, 30 | text: { 31 | largetype: `${command.meta.help} | Usage: ${command.meta.usage}` 32 | }, 33 | icon: command.meta.icon, 34 | valid: false 35 | })) 36 | return alfy.inputMatches(options, 'title') 37 | } 38 | } 39 | 40 | if (!alfy.cache.get('start-PID')) { 41 | alfy.cache.set('start-PID', process.pid, {maxAge: 30000}) // 30 sec. 42 | } 43 | 44 | (async () => { 45 | if (alfy.config.get('theme') === undefined) { 46 | alfy.config.set('theme', 'dark') 47 | } 48 | 49 | try { 50 | if (alfy.cache.get('start-PID') === process.pid) { 51 | await runApplescript(` 52 | tell application "Alfred 4" 53 | run trigger ¬ 54 | "refresh" in workflow ¬ 55 | "org.bikenik.anki" 56 | end tell 57 | `) 58 | } 59 | 60 | if (/!.*/.test(alfy.input)) { 61 | const out = await option(alfy.input) 62 | alfy.output(out) 63 | } else { 64 | const out = await wf.fields() 65 | alfy.output(out) 66 | } 67 | } catch (error) { 68 | if (error.title === 'Working without AnkiConnect') { 69 | alfy.output([error]) 70 | } else if (error.message) { 71 | throw new WorkflowError(error.stack) 72 | } else { 73 | await ankiInfo() 74 | } 75 | } 76 | })() 77 | -------------------------------------------------------------------------------- /update-config.js: -------------------------------------------------------------------------------- 1 | const alfy = require('alfy') 2 | 3 | const {modelExist} = require('./src/anki/anki-models') 4 | 5 | const modelFieldNames = require(`${process.env.alfred_workflow_data}/anki-model-fields.json`) 6 | const {env} = process 7 | 8 | const toggle = (field, fields) => { 9 | switch (fields[field]) { 10 | case 'not_rli': fields[field] = 'rli' 11 | break 12 | default: fields[field] = 'not_rli' 13 | break 14 | } 15 | } 16 | 17 | const updateFieldsConfig = async newFields => { 18 | const fields = alfy.config.get('fields') 19 | for (const field of newFields) { 20 | const modelId = alfy.config.get('default-model') ? alfy.config.get('default-model')[Object.keys(alfy.config.get('default-model'))[0]] : null 21 | if (fields[modelId] === undefined) { 22 | fields[modelId] = {} 23 | } 24 | 25 | if (fields[modelId][field] === undefined) { 26 | fields[modelId][field] = 'not_rli' 27 | } 28 | 29 | if (env[field] === field) { 30 | toggle(field, fields[modelId]) 31 | } 32 | } 33 | 34 | alfy.config.set('fields', fields) 35 | } 36 | 37 | if (alfy.config.get('fields') === undefined) { 38 | alfy.config.set('fields', {}) 39 | } 40 | 41 | if (env.config_variable_deck) { 42 | alfy.config.set(env.config_variable_deck, env.config_value) 43 | } 44 | 45 | if (env.config_variable_profile) { 46 | alfy.config.set(env.config_variable_profile, env.config_value) 47 | alfy.cache.set('new-profile', true) 48 | alfy.cache.set('refresh-done', false) 49 | } 50 | 51 | if (env.config_variable_model) { 52 | (async () => { 53 | alfy.config.set(env.config_variable_model, JSON.parse(env.config_value)) 54 | alfy.cache.set('new-profile', false) 55 | const newFields = await modelExist(Object.keys(alfy.config.get('default-model'))[0]) 56 | await updateFieldsConfig(newFields) 57 | })() 58 | } else { 59 | updateFieldsConfig(modelFieldNames) 60 | } 61 | 62 | if (alfy.config.get('Tag') === undefined) { 63 | alfy.config.set('Tag', 'not_rli') 64 | } 65 | 66 | if (env.Tag === 'Tag') { 67 | switch (alfy.config.get('Tag')) { 68 | case 'not_rli': alfy.config.set('Tag', 'rli') 69 | break 70 | default: alfy.config.set('Tag', 'not_rli') 71 | break 72 | } 73 | } 74 | 75 | if (env.mode === 'getProfileName') { 76 | process.stdout.write(alfy.config.get('default-profile')) 77 | } 78 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* eslint camelcase: ["error", {properties: "never"}] */ 2 | /* eslint-parserOptions: {"ecmaVersion: 2017"} */ 3 | 4 | 'use strict' 5 | const fs = require('fs') 6 | const alfy = require('alfy') 7 | const jsonfile = require('jsonfile') 8 | const pMap = require('p-map') 9 | const {markdownIt} = require('./utils/engine') 10 | const ankiAddCard = require('./anki/anki-add-card') 11 | const config = require('./config').card 12 | 13 | const modelFieldNames = require(`${process.env.alfred_workflow_data}/anki-model-fields.json`) 14 | 15 | const modelId = alfy.config.get('default-model') ? alfy.config.get('default-model')[Object.keys(alfy.config.get('default-model'))[0]] : null 16 | 17 | const header = jsonfile.readFileSync(config.input) 18 | 19 | let firstField = [Object.keys(header)[0]] 20 | if (firstField[0] === undefined) { 21 | firstField = modelFieldNames[0] 22 | header[firstField] = '' 23 | } 24 | 25 | async function main(id) { 26 | setupDirStructure() 27 | const cleanedInput = cleanInput(header) 28 | const output = await processInput(cleanedInput, id) 29 | await ankiAddCard(output) 30 | } 31 | 32 | function setupDirStructure() { 33 | fs.existsSync(config.mediaDir) 34 | } 35 | 36 | function cleanInput(input) { 37 | const deUndefinedArray = [input].filter(card => { 38 | return card[firstField] !== undefined 39 | }) 40 | const deDupedArray = removeDuplicates(deUndefinedArray, config.fields[firstField]) 41 | return deDupedArray 42 | } 43 | 44 | async function processInput(input, id) { 45 | const mapper = async card => { 46 | const data = await getData(card[id]) 47 | const modifiedCard = card[id] 48 | Object.assign(modifiedCard, data) 49 | return modifiedCard 50 | } 51 | 52 | const result = await pMap(input, mapper, { 53 | concurrency: config.concurrency 54 | }) 55 | return result 56 | } 57 | 58 | async function getData(card) { 59 | markdownIt(card) 60 | const result = {} 61 | for (const field of modelFieldNames) { 62 | result[field] = card[field] ? card[field] : '' 63 | } 64 | 65 | result.Tag = card.Tag ? card.Tag : '' 66 | return result 67 | } 68 | 69 | function removeDuplicates(myArr, prop) { 70 | return myArr.filter((obj, pos, arr) => { 71 | return arr.map(mapObj => mapObj[prop]).indexOf(obj[prop]) === pos 72 | }) 73 | } 74 | 75 | main(modelId) 76 | -------------------------------------------------------------------------------- /src/anki/anki-models.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | const alfy = require('alfy') 3 | const jsonfile = require('jsonfile') 4 | 5 | const WorkflowError = require('../utils/error') 6 | const {errorAction} = require('../utils/error') 7 | const ankiConnect = require('./anki-connect') 8 | 9 | const note_type = alfy.config.get('default-model') ? Object.keys(alfy.config.get('default-model'))[0] : null 10 | 11 | const fileAnkiModelFields = `${process.env.alfred_workflow_data}/anki-model-fields.json` 12 | 13 | module.exports = async () => { 14 | try { 15 | const resultAll = await ankiConnect('modelNamesAndIds', 6) 16 | return resultAll 17 | } catch (error) { 18 | alfy.cache.set('validOutput', 'false') 19 | throw new WorkflowError(error, errorAction('modelExist')) 20 | } 21 | } 22 | 23 | module.exports.modelExist = async (model = note_type) => { 24 | try { 25 | alfy.cache.set('validOutput', 'true') 26 | const result = await ankiConnect('modelFieldNames', 6, {modelName: model}) 27 | jsonfile.writeFile(fileAnkiModelFields, result === null ? [] : result, { 28 | spaces: 2 29 | }, error => { 30 | if (error !== null) { 31 | console.log(error) 32 | } 33 | }) 34 | return result 35 | } catch (error) { 36 | alfy.cache.set('validOutput', 'false') 37 | return new WorkflowError(error, error === 'failed to connect to AnkiConnect' ? errorAction('main') : error === 'collection is not available' ? errorAction('profile') : /model was not found/.test(error) ? errorAction('modelExist') : errorAction('main')) 38 | } 39 | } 40 | 41 | module.exports.render = async (pattern = '', autocomplete = () => undefined, ankiDecks, cmdIcon) => { 42 | const out = await alfy.matches(pattern, Object.getOwnPropertyNames(ankiDecks).sort()) 43 | .map(name => ({ 44 | title: name, 45 | subtitle: ankiDecks[name], 46 | autocomplete: autocomplete(name), 47 | valid: false, 48 | icon: { 49 | path: cmdIcon 50 | } 51 | })) 52 | if (out.length === 0) { 53 | out.push({ 54 | title: `Not found '${pattern}' module`, 55 | subtitle: `Old value ⇒ ${alfy.config.get('default-deck')}`, 56 | valid: true, 57 | arg: JSON.stringify({ 58 | alfredworkflow: { 59 | variables: { 60 | action: 'model', 61 | config_variable_model: 'default-model', 62 | config_value: pattern 63 | } 64 | } 65 | }) 66 | }) 67 | return out 68 | } 69 | 70 | return out 71 | } 72 | -------------------------------------------------------------------------------- /src/anki/anki-info.js: -------------------------------------------------------------------------------- 1 | const alfy = require('alfy') 2 | const jsonfile = require('jsonfile') 3 | const WorkflowError = require('../utils/error') 4 | const decks = require('../anki/anki-decks') 5 | const {errorAction} = require('../utils/error') 6 | const config = require('../config').card 7 | const {modelExist} = require('./anki-models') 8 | const ankiConnect = require('./anki-connect') 9 | 10 | const fileAnkiDecks = `${process.env.alfred_workflow_data}/anki-decks.json` 11 | const model = alfy.config.get('default-model') ? Object.keys(alfy.config.get('default-model'))[0] : null 12 | 13 | module.exports = async () => { 14 | const introMessage = [{ 15 | name: 'intro', 16 | title: alfy.config.get('default-deck') ? 'Create new card (⌘ + ↵)' : 'press ↵ or ↹ to select default deck', 17 | subtitle: alfy.config.get('default-deck') ? `🧰 ${alfy.config.get('default-deck')} ⚒ ${model} 👤 ${alfy.config.get('default-profile')}` : '', 18 | icon: {path: './icons/anki.png'}, 19 | autocomplete: '!deck default-deck ', 20 | valid: false, 21 | arg: '', 22 | quicklookurl: `${config.mediaDir}/_preview.html`, 23 | mods: { 24 | alt: { 25 | subtitle: '❌ RESET', 26 | valid: true, 27 | variables: { 28 | action: 'reset' 29 | }, 30 | arg: '' 31 | }, 32 | cmd: { 33 | valid: true, 34 | subtitle: '\t Add New Card \t🎉', 35 | variables: { 36 | action: 'make-new-card' 37 | } 38 | } 39 | } 40 | }] 41 | const ankiModelExist = await modelExist() 42 | if (alfy.config.get('default-profile') === false) { 43 | await ankiConnect('version', 6) 44 | return [errorAction('alfred-settings')] 45 | } 46 | 47 | if (alfy.cache.get('new-profile') === true) { 48 | if (alfy.cache.get('refresh-done') === false) { 49 | return [errorAction('waiting-for-refresh')] 50 | } 51 | 52 | return [errorAction('new-profile')] 53 | } 54 | 55 | if (ankiModelExist && ankiModelExist.message) { 56 | return [ankiModelExist] 57 | } 58 | 59 | if (!ankiModelExist) { 60 | return [errorAction('modelExist')] 61 | } 62 | 63 | const ankiDecks = await decks() 64 | if (ankiDecks === null) { 65 | throw new WorkflowError('Decks was not found, check your Anki profile', errorAction('profile')) 66 | } 67 | 68 | jsonfile.writeFile(fileAnkiDecks, ankiDecks, { 69 | spaces: 2 70 | }, error => { 71 | if (error !== null) { 72 | console.log(error) 73 | } 74 | }) 75 | return introMessage 76 | } 77 | -------------------------------------------------------------------------------- /src/config/index.js: -------------------------------------------------------------------------------- 1 | /* eslint camelcase: ["error", {properties: "never"}] */ 2 | /* eslint-disable camelcase */ 3 | const os = require('os') 4 | const fs = require('fs-extra') 5 | 6 | const alfy = require('alfy') 7 | const WorkflowError = require('../utils/error') 8 | const {errorAction} = require('../utils/error') 9 | const ankiConnect = require('../anki/anki-connect') 10 | 11 | const createJsonFile = async db => { 12 | try { 13 | await fs.readJson(db) 14 | } catch { 15 | try { 16 | await fs.outputJson(db, []) 17 | } catch (error) { 18 | throw new WorkflowError(error.stack) 19 | } 20 | } 21 | } 22 | 23 | const envOfWF = process.env.alfred_workflow_data 24 | createJsonFile(`${envOfWF}/anki-cards.json`) 25 | createJsonFile(`${envOfWF}/anki-decks.json`) 26 | createJsonFile(`${envOfWF}/anki-model-fields.json`) 27 | createJsonFile(`${envOfWF}/anki-models.json`) 28 | createJsonFile(`${envOfWF}/anki-profiles.json`) 29 | createJsonFile(`${envOfWF}/header.json`) 30 | 31 | const modelFieldNames = require(`${process.env.alfred_workflow_data}/anki-model-fields.json`) 32 | 33 | const user = os.userInfo() 34 | 35 | const getProfileName = async () => { 36 | const getProfileName = fs.existsSync(`${user.homedir}/Library/Application Support/Anki2/${alfy.config.get('default-profile')}/collection.media`) ? alfy.config.get('default-profile') : false 37 | if (typeof (getProfileName) === 'string') { 38 | try { 39 | await ankiConnect('loadProfile', 6, {name: alfy.config.get('default-profile')}) 40 | } catch (error) { 41 | throw new WorkflowError(error, errorAction('main')) 42 | } 43 | } else { 44 | alfy.config.set('default-profile', getProfileName) 45 | } 46 | } 47 | 48 | const path_to_ankiMedia = () => alfy.config.get('default-profile') ? `/Library/Application Support/Anki2/${alfy.config.get('default-profile')}/collection.media/` : '' 49 | 50 | const fields = () => { 51 | const fields = {} 52 | modelFieldNames.forEach(x => { 53 | fields[x] = x 54 | }) 55 | return fields 56 | } 57 | 58 | const card = { 59 | concurrency: 10, 60 | input: `${process.env.alfred_workflow_data}/header.json`, 61 | fields: fields(), 62 | get mediaDir() { 63 | return user.homedir + path_to_ankiMedia() 64 | } 65 | } 66 | 67 | const cmd = { 68 | defaults: { 69 | 'default-deck': 'Default' 70 | }, 71 | delete: { 72 | 'delete-deck': 'choose ...' 73 | }, 74 | refresh: { 75 | 'refreshing...': 'refreshings ...' 76 | }, 77 | theme: { 78 | 'change theme': 'toogle ...' 79 | }, 80 | models: { 81 | 'default-model': 'choose ...' 82 | }, 83 | profiles: { 84 | 'default-profile': 'choose ...' 85 | }, 86 | reset: { 87 | 'reseting...': 'reset it ...' 88 | } 89 | } 90 | 91 | module.exports = {getProfileName, card, cmd} 92 | -------------------------------------------------------------------------------- /src/anki/anki-add-card.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | /* eslint-disable camelcase */ 3 | const alfy = require('alfy') 4 | const ankiConnect = require('./anki-connect') 5 | const decks = require('./anki-decks') 6 | 7 | const nameOfDeck = alfy.config.get('default-deck') 8 | const note_type = Object.keys(alfy.config.get('default-model'))[0] 9 | 10 | const logResult = { 11 | error: [], 12 | result: [] 13 | } 14 | 15 | module.exports = async function (output) { 16 | /* eslint-disable no-await-in-loop */ 17 | for (let i = 0; i < output.length; i++) { 18 | if (output[i].Homnum) { 19 | output[i].Headword = `${output[i].Headword}${ 20 | output[i].Homnum.toString()}` 21 | } 22 | 23 | delete output[i].Inflections // Can't understood the reason of error without delete 24 | 25 | if (output[i].Definition !== 'notfound' && output[i].Definition !== '') { 26 | try { 27 | const result1 = await ankiConnect( 28 | 'createDeck', 6, 29 | { 30 | deck: nameOfDeck 31 | }) 32 | } catch (error) { 33 | logResult.error.push(error) 34 | } 35 | 36 | try { 37 | const result2 = await ankiConnect( 38 | 'addNote', 6, 39 | { 40 | note: { 41 | deckName: nameOfDeck, 42 | modelName: note_type, 43 | fields: output[i], 44 | tags: [output[i].Tag] 45 | } 46 | }) 47 | logResult.result.push(`\n${nameOfDeck}: ${result2}`) 48 | } catch (error) { 49 | logResult.error.push(error) 50 | } 51 | } 52 | } 53 | 54 | /* eslint-enable no-await-in-loop */ 55 | if (logResult.error.length > 0) { 56 | logResult.error.forEach(error => { 57 | process.stdout.write(`!err: ${error}`) 58 | }) 59 | } else { 60 | process.stdout.write(logResult.result.length > 1 ? `in the number of: ${logResult.result.length.toString()} items` : logResult.result[0]) 61 | } 62 | } 63 | 64 | module.exports.canAddNotes = async function (check) { 65 | const ankiDecks = await decks() 66 | /* eslint-disable no-await-in-loop */ 67 | for (let i = 0; i < check.length; i++) { 68 | const currentFields = { 69 | Headword: `${check[i].Headword}${check[i].Homnum ? `${ 70 | check[i].Homnum.toString()}` : ''}`, 71 | Audio: '', 72 | Translation: '', 73 | Example: '', 74 | Image: '', 75 | Verb_table: '', 76 | Tag: [check[i].Part_of_speech] 77 | } 78 | try { 79 | const result = await ankiConnect( 80 | 'canAddNotes', 6, 81 | { 82 | notes: [{ 83 | deckName: ankiDecks[0], 84 | modelName: note_type, 85 | fields: currentFields, 86 | tags: [currentFields.Tag] 87 | }] 88 | }) 89 | return result 90 | } catch (error) { 91 | return error 92 | } 93 | } 94 | /* eslint-enable no-await-in-loop */ 95 | } 96 | -------------------------------------------------------------------------------- /src/utils/engine.js: -------------------------------------------------------------------------------- 1 | /* eslint max-params: ["error", 9] */ 2 | 'use strict' 3 | const hljs = require('highlight.js') 4 | const md = require('markdown-it')({ 5 | breaks: true, 6 | highlight: (str, lang) => { 7 | if (lang && hljs.getLanguage(lang)) { 8 | try { 9 | return '
' +
 10 | 					hljs.highlight(lang, str, true).value +
 11 | 					'
' 12 | } catch (error) { 13 | console.log(error) 14 | } 15 | } 16 | 17 | return '
' + md.utils.escapeHtml(str) + '
' 18 | } 19 | }) 20 | .use(require('markdown-it-mark')) 21 | .use(require('markdown-it-ins')) 22 | .use(require('markdown-it-emoji')) 23 | .use(require('markdown-it-abbr')) 24 | 25 | const clearSentences = argSentence => argSentence.replace(/\s(\.|\?|!)/g, '$1') 26 | const largetypeFunc = (title, subtitle) => { 27 | return `${title ? title : ''}\n\n${subtitle ? subtitle : ''}` 28 | } 29 | 30 | const clearSentencesInArg = arg => { 31 | if (arg && arg.examples) { 32 | for (const example of arg.examples) { 33 | if (example.text) { 34 | example.text = clearSentences(example.text) 35 | } 36 | } 37 | } 38 | } 39 | 40 | const keyOperations = (item, key) => { 41 | switch (key) { 42 | case 'title': 43 | item.autocomplete = item.title 44 | break 45 | case 'icon': 46 | item.icon = {path: item.icon} 47 | break 48 | case 'arg': 49 | clearSentencesInArg(item.arg) 50 | break 51 | 52 | default: 53 | break 54 | } 55 | } 56 | 57 | module.exports.Render = class { 58 | constructor(name, ...itemKeys) { 59 | const item = {} 60 | item.name = name 61 | item.valid = true 62 | item.sentence = '' 63 | item.mods = { 64 | ctrl: { 65 | valid: false 66 | } 67 | } 68 | for (const key of itemKeys) { 69 | this.itemKey = null 70 | Object.defineProperty(this, key, { 71 | get: () => key, 72 | set: value => { 73 | item[key] = value 74 | keyOperations(item, key) 75 | if (key === 'title') { 76 | item.autocomplete = item.title 77 | } 78 | 79 | if (Object.keys(item).length - 3 === itemKeys.length) { 80 | clearSentencesInArg(item.arg) 81 | if (!item.text) { 82 | const largetype = largetypeFunc(item.title, item.subtitle) 83 | item.text = { 84 | copy: largetype, 85 | largetype 86 | } 87 | } 88 | } 89 | } 90 | }) 91 | } 92 | 93 | this.getProperties = () => item 94 | } 95 | } 96 | 97 | module.exports.markdownIt = obj => { 98 | for (const key in obj) { 99 | if (key !== 'Tag') { 100 | let element = md.render(obj[key]) 101 | const clozeDeletion = /\[\[(.*?)\]\]/gm 102 | const removePtag = /^

|<\/p>$/gm 103 | element = element.replace(clozeDeletion, '{{c1::$1}}') 104 | element = element.replace(removePtag, '') 105 | obj[key] = element 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # alfred-anki [![Build Status](https://travis-ci.org/bikenik/alfred-anki.svg?branch=master)](https://travis-ci.org/bikenik/alfred-anki) 2 | 3 | > anki card generator: Create New Cards into any Decks with any "Note Type". 4 | 5 | ![main window](./readme/main-window.png) 6 | ![main window](./readme/settings-window.png) 7 | ![main window](./readme/preview-of-card-field.png) 8 | 9 | ## Install 10 | 11 | ``` 12 | $ npm install --global alfred-anki 13 | ``` 14 | or as [ `anki.alfredworkflow`](https://github.com/bikenik/alfred-anki/releases) 15 | 16 | *Requires: [Node.js](https://nodejs.org) 7.6+, Alfred [Powerpack](https://www.alfredapp.com/powerpack/), [Anki](https://apps.ankiweb.net) intelligent flash cards, [AnkiConnect](https://ankiweb.net/shared/info/2055492159) plugin for Anki* 17 | 18 | >Also you can consider [DrLulz/Anki-Templates](https://github.com/DrLulz/Anki-Templates) 19 | 20 | 21 | ## Usage 22 | 23 | - In Alfred, type `:anki`, 24 | - Enter/Click on the any field to fill it in 25 | - Enter/Click to save current value 26 | - the same staps to edit. 27 | - type `:anki!`, to setting menu. 28 | 29 | #### This workflow uses Markdown syntax: 30 | - [markdow-it](https://markdown-it.github.io) library with [Syntax highlighting (highlighjs.org)](https://highlightjs.org/), 31 | - [\ tag plugin for markdown-it](https://github.com/markdown-it/markdown-it-ins) `++inserted++ => inserted`, 32 | - [\ tag plugin for markdown-it](https://github.com/markdown-it/markdown-it-mark) `==marked== => inserted` 33 | - [Emojies](https://github.com/markdown-it/markdown-it-emoji) Shortcuts (emoticons)`: :-) :-( 8-) ;)` 34 | - [\ tag plugin for markdown-it](https://github.com/markdown-it/markdown-it-abbr) : 35 | ``` 36 | *[HTML]: Hyper Text Markup Language 37 | The HTML specification 38 | ``` 39 | 40 | --- 41 | On the card fields: 42 | 43 | - hit ⌃ + ↩ to remeber last input after created card. 44 | - hit ⌃ + ↩ one more time to toggle switch this option 45 | - hit ⌥ + ↩ to reset text from all fields 46 | - hit (shift) to preview parsed fields of card 47 | - for line break type '\n' (with space or without), also should be handy to use 'Alfred's snippets'. 48 | - to add imag from clipboard use Alfred snippet >>img needs install [`pngpaste`](https://github.com/jcsalterego/pngpaste) 49 | - check out another snippets 50 | --- 51 | - To use your own icons for your Model's fields just you should call the `png` files the same as fields `[name-of-field].png` and put it into `icons` folder of this Workflow directory. Recommended use two instances of each icon where second icon (with some mark) will be called as `[name-of-field]_marked.png` for "remember last import" option. 52 | 53 | Buy Me A Coffee 54 | 55 | ## License 56 | 57 | MIT © [bikenik](http://bikenik.org) 58 | -------------------------------------------------------------------------------- /src/cmd/theme.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const fs = require('fs-extra') 3 | const alfy = require('alfy') 4 | 5 | const config = require('../config').cmd 6 | const WorkflowError = require('../utils/error') 7 | const {hasOwnProperty} = require('../utils') 8 | 9 | const variables = { 10 | 'toogle...': { 11 | theme: 'toogle...' 12 | } 13 | } 14 | 15 | const copyFiles = async () => { 16 | try { 17 | switch (alfy.config.get('theme')) { 18 | case 'dark': 19 | alfy.config.set('theme', 'light') 20 | await fs.copy(`${process.env.PWD}/icons/for-light-theme/`, `${process.env.PWD}/icons/`) 21 | break 22 | case 'light': 23 | alfy.config.set('theme', 'dark') 24 | await fs.copy(`${process.env.PWD}/icons/for-dark-theme/`, `${process.env.PWD}/icons/`) 25 | break 26 | 27 | default: 28 | alfy.config.set('theme', 'dark') 29 | await fs.copy(`${process.env.PWD}/icons/for-dark-theme/`, `${process.env.PWD}/icons/`) 30 | break 31 | } 32 | } catch (error) { 33 | process.stderr.write(error) 34 | } 35 | } 36 | 37 | // Output matching for config variables 38 | const outputVariables = pattern => { 39 | if (!pattern) { 40 | pattern = '' 41 | } 42 | 43 | const vars = Object.keys(config.refresh) 44 | 45 | const mapper = key => ({ 46 | title: key, 47 | subtitle: pattern === '' ? 'Hit ↵ to toogle current theme dark / light' : 'hold on...', 48 | valid: false, 49 | autocomplete: '!theme toogle... ', 50 | icon: {path: './icons/night_and_day.png'} 51 | }) 52 | 53 | const out = alfy.matches(pattern, Object.keys(config.theme)).map(mapper) 54 | 55 | return out.length === 0 ? vars.map(mapper) : out 56 | } 57 | 58 | module.exports = async input => { 59 | // !refresh command value 60 | 61 | if (typeof input !== 'string') { 62 | throw new TypeError('input should be a string') 63 | } 64 | 65 | const chunks = input.split(' ') 66 | 67 | if (chunks.length === 1) { 68 | return outputVariables() 69 | } 70 | 71 | if (chunks.length === 2) { 72 | return outputVariables(chunks[1]) 73 | } 74 | 75 | const variableName = chunks[1] 76 | 77 | // Throw if variable is invalid 78 | if (!hasOwnProperty(variables, variableName)) { 79 | throw new WorkflowError(`Variable '${variableName}' does not exist`, { 80 | autocomplete: '!toogle' 81 | }) 82 | } 83 | 84 | if (chunks.length >= 3) { 85 | return [{ 86 | title: 'Icons have been overwritten', 87 | subtitle: 'hit ↵ to go home', 88 | valid: true, 89 | autocomplete: '', 90 | arg: JSON.stringify({ 91 | alfredworkflow: { 92 | variables: { 93 | action: 'change-theme-icons' 94 | }, 95 | arg: await copyFiles() 96 | } 97 | }), 98 | icon: { 99 | path: './icons/warning.png' 100 | } 101 | }] 102 | } 103 | } 104 | 105 | module.exports.meta = { 106 | name: '!theme', 107 | usage: '!toogle to another theme', 108 | help: '!choose dark or night theme', 109 | autocomplete: '!theme ', 110 | icon: {path: './icons/night_and_day.png'} 111 | } 112 | 113 | module.exports.match = input => { 114 | return input.indexOf('!theme') === 0 115 | } 116 | -------------------------------------------------------------------------------- /src/cmd/del.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | 3 | const alfy = require('alfy') 4 | const WorkflowError = require('../utils/error') 5 | const {errorAction} = require('../utils/error') 6 | const {hasOwnProperty} = require('../utils') 7 | const config = require('../config').cmd 8 | const decks = require('../anki/anki-decks') 9 | 10 | const ankiCards = require(`${process.env.alfred_workflow_data}/anki-cards.json`) 11 | 12 | const variables = { 13 | 'delete-deck': { 14 | delete: 'choose ...', 15 | outputOptions: decks 16 | } 17 | } 18 | 19 | // Output matching for config variables 20 | const outputVariables = pattern => { 21 | if (!pattern) { 22 | pattern = '' 23 | } 24 | 25 | const vars = Object.keys(config.delete) 26 | 27 | const mapper = key => ({ 28 | title: key, 29 | subtitle: `current deck is ⇒ ${alfy.config.get('default-deck')} | ↵ choose this or pick out another`, 30 | valid: false, 31 | autocomplete: `!del ${key} `, 32 | icon: {path: './icons/delete.png'} 33 | }) 34 | 35 | const out = alfy.matches(pattern, Object.keys(config.delete)).map(mapper) 36 | 37 | return out.length === 0 ? vars.map(mapper) : out 38 | } 39 | 40 | module.exports = input => { 41 | // !del command value 42 | 43 | if (typeof input !== 'string') { 44 | throw new TypeError('input should be a string') 45 | } 46 | 47 | const chunks = input.split(' ') 48 | 49 | if (chunks.length === 1) { 50 | return outputVariables() 51 | } 52 | 53 | if (chunks.length === 2) { 54 | return outputVariables(chunks[1]) 55 | } 56 | 57 | const variableName = chunks[1] 58 | 59 | // Throw if variable is invalid 60 | if (!hasOwnProperty(variables, variableName)) { 61 | throw new WorkflowError(`Variable '${variableName}' does not exist`, { 62 | autocomplete: '!del ' 63 | }) 64 | } 65 | 66 | const variable = variables[variableName] 67 | const value = chunks.slice(2).join(' ') 68 | const arrayOfDecks = ankiCards 69 | 70 | if (chunks.length >= 3) { 71 | return (async () => { 72 | if (await decks() === null) { 73 | throw new WorkflowError('Decks was not found, check your Anki profile', errorAction('!del decks')) 74 | } 75 | 76 | if (Object.getOwnPropertyNames(arrayOfDecks).indexOf(value) === -1) { 77 | return variable.outputOptions.render( 78 | value, 79 | name => `!del ${variableName} ${name}`, 80 | arrayOfDecks, 81 | './icons/del.png' 82 | ) 83 | } 84 | 85 | return [{ 86 | title: `The deck [${value}] will be deleted`, 87 | subtitle: 'All cards in this deck will be deleted. Are you sure?', 88 | valid: true, 89 | arg: JSON.stringify({ 90 | alfredworkflow: { 91 | variables: { 92 | action: 'del', 93 | config_variable: variableName, 94 | config_value: value 95 | } 96 | } 97 | }), 98 | icon: { 99 | path: './icons/warning.png' 100 | } 101 | }] 102 | })() 103 | } 104 | } 105 | 106 | module.exports.meta = { 107 | name: '!del', 108 | usage: '!delete any your deck', 109 | help: 'Delete deck by the given value.', 110 | autocomplete: '!del ', 111 | icon: {path: './icons/delete.png'} 112 | } 113 | 114 | module.exports.match = input => { 115 | return input.indexOf('!del') === 0 116 | } 117 | -------------------------------------------------------------------------------- /src/cmd/profiles.js: -------------------------------------------------------------------------------- 1 | const alfy = require('alfy') 2 | const WorkflowError = require('../utils/error') 3 | const {errorAction} = require('../utils/error') 4 | const {hasOwnProperty} = require('../utils') 5 | const config = require('../config').cmd 6 | const profiles = require('../anki/anki-profiles') 7 | 8 | const ankiProfiles = require(`${process.env.alfred_workflow_data}/anki-profiles.json`) 9 | 10 | const variables = { 11 | 'default-profile': { 12 | profiles: 'User 1', 13 | outputOptions: profiles 14 | } 15 | } 16 | 17 | // Output matching for config variables 18 | const outputVariables = pattern => { 19 | if (!pattern) { 20 | pattern = '' 21 | } 22 | 23 | const vars = Object.keys(config.profiles) 24 | 25 | const mapper = key => ({ 26 | title: `${key} ⇒ ${ 27 | alfy.config.get(key) === undefined ? config.profiles['default-profile'] : alfy.config.get(key)}`, 28 | subtitle: '↵ pick out another profile...', 29 | valid: false, 30 | autocomplete: `!profile ${key} `, 31 | icon: {path: './icons/Profile.png'} 32 | }) 33 | 34 | const out = alfy.matches(pattern, Object.keys(config.profiles)).map(mapper) 35 | 36 | return out.length === 0 ? vars.map(mapper) : out 37 | } 38 | 39 | module.exports = input => { 40 | if (typeof input !== 'string') { 41 | throw new TypeError('input should be a string') 42 | } 43 | 44 | const chunks = input.split(' ') 45 | 46 | if (chunks.length === 1) { 47 | return outputVariables() 48 | } 49 | 50 | if (chunks.length === 2) { 51 | return outputVariables(chunks[1]) 52 | } 53 | 54 | const variableName = chunks[1] 55 | 56 | // Throw if variable is invalid 57 | if (!hasOwnProperty(variables, variableName)) { 58 | throw new WorkflowError(`Variable '${variableName}' does not exist`, { 59 | autocomplete: '!profile ' 60 | }) 61 | } 62 | 63 | const variable = variables[variableName] 64 | const value = chunks.slice(2).join(' ') 65 | const arrayOfProfiles = ankiProfiles 66 | 67 | if (chunks.length >= 3) { 68 | variable.outputOptions() 69 | return (async () => { 70 | if (await profiles() === null) { 71 | throw new WorkflowError('Profiles was not found, type excisting name', errorAction('!profile profiles')) 72 | } 73 | 74 | if (Object.values(arrayOfProfiles).indexOf(value) === -1) { 75 | return variable.outputOptions.render( 76 | value, 77 | name => `!profile ${variableName} ${name}`, 78 | arrayOfProfiles, 79 | './icons/Profile.png' 80 | ) 81 | } 82 | 83 | return [{ 84 | title: `Set ${variableName} to '${value}'`, 85 | subtitle: `Old value ⇒ ${alfy.config.get(variableName)}`, 86 | valid: true, 87 | arg: JSON.stringify({ 88 | alfredworkflow: { 89 | variables: { 90 | action: 'set', 91 | /* eslint-disable camelcase */ 92 | config_variable_profile: variableName, 93 | config_value: value 94 | /* eslint-enable camelcase */ 95 | } 96 | } 97 | }) 98 | }] 99 | })() 100 | } 101 | } 102 | 103 | module.exports.meta = { 104 | name: '!profile', 105 | usage: '!profile to another Profile', 106 | help: 'Pick out the profile by the given value.', 107 | autocomplete: '!profile ', 108 | icon: {path: './icons/Profile.png'} 109 | } 110 | 111 | module.exports.match = input => { 112 | return input.indexOf('!profile') === 0 113 | } 114 | -------------------------------------------------------------------------------- /src/cmd/reset.js: -------------------------------------------------------------------------------- 1 | /* eslint curly: ["error", "multi-line"] */ 2 | 'use strict' 3 | const alfy = require('alfy') 4 | const jsonfile = require('jsonfile') 5 | 6 | const config = require('../config').cmd 7 | const WorkflowError = require('../utils/error') 8 | const {hasOwnProperty} = require('../utils') 9 | 10 | const envOfWF = process.env.alfred_workflow_data 11 | const fileHeader = `${envOfWF}/header.json` 12 | const fileDecks = `${envOfWF}/anki-decks.json` 13 | const fileCards = `${envOfWF}/anki-cards.json` 14 | const fileModelFields = `${envOfWF}/anki-model-fields.json` 15 | const fileModels = `${envOfWF}/anki-models.json` 16 | const fileProfiles = `${envOfWF}/anki-profiles.json` 17 | 18 | const variables = { 19 | 'reseting...': { 20 | refresh: 'reset it ...' 21 | } 22 | } 23 | 24 | // Output matching for config variables 25 | const outputVariables = pattern => { 26 | if (!pattern) pattern = '' 27 | 28 | const vars = Object.keys(config.reset) 29 | 30 | const mapper = key => ({ 31 | title: key, 32 | subtitle: pattern === '' ? 'Hit ↵ to reset all previous settings' : 'hold on...', 33 | valid: false, 34 | autocomplete: '!reset reseting... ', 35 | icon: {path: './icons/Reset.png'} 36 | }) 37 | 38 | const out = alfy.matches(pattern, Object.keys(config.reset)).map(mapper) 39 | 40 | return out.length === 0 ? vars.map(mapper) : out 41 | } 42 | 43 | module.exports = async (input = '!reset reseting... ') => { 44 | // !refresh command value 45 | 46 | if (typeof input !== 'string') { 47 | throw new TypeError('input should be a string') 48 | } 49 | 50 | const chunks = input.split(' ') 51 | 52 | if (chunks.length === 1) { 53 | return outputVariables() 54 | } 55 | 56 | if (chunks.length === 2) { 57 | return outputVariables(chunks[1]) 58 | } 59 | 60 | const variableName = chunks[1] 61 | 62 | // Throw if variable is invalid 63 | if (!hasOwnProperty(variables, variableName)) { 64 | throw new WorkflowError(`Variable '${variableName}' does not exist`, { 65 | autocomplete: '!reset ' 66 | }) 67 | } 68 | 69 | if (chunks.length >= 3) { 70 | const reset = () => { 71 | alfy.config.clear() 72 | alfy.cache.clear() 73 | jsonfile.writeFile(fileHeader, {}, {spaces: 2}, error => { 74 | if (error) throw new WorkflowError(error) 75 | }) 76 | jsonfile.writeFile(fileDecks, ['Default'], {spaces: 2}, error => { 77 | if (error) throw new WorkflowError(error) 78 | }) 79 | jsonfile.writeFile(fileCards, {}, {spaces: 2}, error => { 80 | if (error) throw new WorkflowError(error) 81 | }) 82 | jsonfile.writeFile(fileModelFields, [], {spaces: 2}, error => { 83 | if (error) throw new WorkflowError(error) 84 | }) 85 | jsonfile.writeFile(fileModels, {}, {spaces: 2}, error => { 86 | if (error) throw new WorkflowError(error) 87 | }) 88 | jsonfile.writeFile(fileProfiles, {}, {spaces: 2}, error => { 89 | if (error) throw new WorkflowError(error) 90 | }) 91 | } 92 | 93 | return [{ 94 | title: 'WF config was reset', 95 | subtitle: 'hit ↵ to go settings', 96 | valid: false, 97 | autocomplete: '!', 98 | arg: JSON.stringify({ 99 | alfredworkflow: { 100 | autocomplete: '!', 101 | arg: reset() 102 | } 103 | }), 104 | icon: { 105 | path: './icons/Reset.png' 106 | } 107 | }] 108 | } 109 | } 110 | 111 | module.exports.meta = { 112 | name: '!reset', 113 | usage: '!reset all config', 114 | help: 'Reseting all settings to default', 115 | autocomplete: '!reset ', 116 | icon: {path: './icons/Reset.png'} 117 | } 118 | 119 | module.exports.match = input => { 120 | return input.indexOf('!reset') === 0 121 | } 122 | -------------------------------------------------------------------------------- /src/cmd/models.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable capitalized-comments */ 2 | 3 | const alfy = require('alfy') 4 | const WorkflowError = require('../utils/error') 5 | const {errorAction} = require('../utils/error') 6 | const {hasOwnProperty} = require('../utils') 7 | const config = require('../config').cmd 8 | const models = require('../anki/anki-models') 9 | 10 | const ankiModels = require(`${process.env.alfred_workflow_data}/anki-models.json`) 11 | 12 | const variables = { 13 | 'default-model': { 14 | models: 'Basic', 15 | outputOptions: models 16 | } 17 | } 18 | 19 | // Output matching for config variables 20 | const outputVariables = pattern => { 21 | if (!pattern) { 22 | pattern = '' 23 | } 24 | 25 | const vars = Object.keys(config.models) 26 | 27 | const mapper = key => ({ 28 | title: `${key} ⇒ ${ 29 | Object.keys(alfy.config.get(key))[0] === undefined ? config.models['default-model'] : Object.keys(alfy.config.get(key))[0]}`, 30 | subtitle: '↵ pick out another model...', 31 | valid: false, 32 | autocomplete: `!model ${key} `, 33 | icon: {path: './icons/Model.png'} 34 | }) 35 | 36 | const out = alfy.matches(pattern, Object.keys(config.models)).map(mapper) 37 | 38 | return out.length === 0 ? vars.map(mapper) : out 39 | } 40 | 41 | module.exports = input => { 42 | if (typeof input !== 'string') { 43 | throw new TypeError('input should be a string') 44 | } 45 | 46 | const chunks = input.split(' ') 47 | 48 | if (chunks.length === 1) { 49 | return outputVariables() 50 | } 51 | 52 | if (chunks.length === 2) { 53 | return outputVariables(chunks[1]) 54 | } 55 | 56 | const variableName = chunks[1] 57 | 58 | // Throw if variable is invalid 59 | if (!hasOwnProperty(variables, variableName)) { 60 | throw new WorkflowError(`Variable '${variableName}' does not exist`, { 61 | autocomplete: '!model ' 62 | }) 63 | } 64 | // for (let i = 2; i < chunks.length; i++) { 65 | // chunks[i] = chunks[i].charAt(0).toUpperCase() + chunks[i].slice(1) 66 | // } 67 | 68 | // value.split('-').forEach(x => {x.charAt(0).toUpperCase() + x.slice(1)}).join('-'), 69 | const variable = variables[variableName] 70 | const value = chunks.slice(2).join(' ') 71 | const arrayOfDecks = ankiModels 72 | 73 | if (chunks.length >= 3) { 74 | return (async () => { 75 | if (await models() === null) { 76 | throw new WorkflowError('Models was not found, check your Anki profile', errorAction('!model models')) 77 | } 78 | 79 | if (Object.getOwnPropertyNames(arrayOfDecks).indexOf(value) === -1) { 80 | return variable.outputOptions.render( 81 | value, 82 | name => `!model ${variableName} ${name}`, 83 | arrayOfDecks, 84 | './icons/Model.png' 85 | ) 86 | } 87 | 88 | return [{ 89 | title: `Set ${variableName} to '${value}'`, 90 | subtitle: `Old value ⇒ ${alfy.config.get(variableName)}`, 91 | valid: true, 92 | arg: JSON.stringify({ 93 | alfredworkflow: { 94 | variables: { 95 | action: 'set', 96 | /* eslint-disable camelcase */ 97 | config_variable_model: variableName, 98 | config_value: JSON.stringify({ 99 | [value]: arrayOfDecks[value].toString() 100 | }) 101 | /* eslint-enable camelcase */ 102 | } 103 | } 104 | }) 105 | }] 106 | })() 107 | } 108 | } 109 | 110 | module.exports.meta = { 111 | name: '!model', 112 | usage: '!model to another Model', 113 | help: 'Pick out the model by the given value.', 114 | autocomplete: '!model ', 115 | icon: {path: './icons/Model.png'} 116 | } 117 | 118 | module.exports.match = input => { 119 | return input.indexOf('!model') === 0 120 | } 121 | -------------------------------------------------------------------------------- /src/cmd/decks.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable capitalized-comments */ 2 | 3 | const alfy = require('alfy') 4 | 5 | const WorkflowError = require('../utils/error') 6 | const {errorAction} = require('../utils/error') 7 | const {hasOwnProperty} = require('../utils') 8 | const config = require('../config').cmd 9 | const decks = require('../anki/anki-decks') 10 | 11 | const ankiCards = require(`${process.env.alfred_workflow_data}/anki-cards.json`) 12 | 13 | const variables = { 14 | 'default-deck': { 15 | default: 'Default', 16 | outputOptions: decks 17 | } 18 | } 19 | 20 | // Output matching for config variables 21 | const outputVariables = pattern => { 22 | if (!pattern) { 23 | pattern = '' 24 | } 25 | 26 | const vars = Object.keys(config.defaults) 27 | 28 | const mapper = key => ({ 29 | title: `${key} ⇒ ${ 30 | alfy.config.get(key) === undefined ? config.defaults['default-deck'] : alfy.config.get(key)}`, 31 | subtitle: '↵ pick out another ...', 32 | valid: false, 33 | autocomplete: `!deck ${key} `, 34 | icon: {path: './icons/deck-settings.png'} 35 | }) 36 | 37 | const out = alfy.matches(pattern, Object.keys(config.defaults)).map(mapper) 38 | 39 | return out.length === 0 ? vars.map(mapper) : out 40 | } 41 | 42 | module.exports = input => { 43 | if (typeof input !== 'string') { 44 | throw new TypeError('input should be a string') 45 | } 46 | 47 | const chunks = input.split(' ') 48 | 49 | if (chunks.length === 1) { 50 | return outputVariables() 51 | } 52 | 53 | if (chunks.length === 2) { 54 | return outputVariables(chunks[1]) 55 | } 56 | 57 | const variableName = chunks[1] 58 | 59 | // Throw if variable is invalid 60 | if (!hasOwnProperty(variables, variableName)) { 61 | throw new WorkflowError(`Variable '${variableName}' does not exist`, { 62 | autocomplete: '!deck ' 63 | }) 64 | } 65 | // for (let i = 2; i < chunks.length; i++) { 66 | // chunks[i] = chunks[i].charAt(0).toUpperCase() + chunks[i].slice(1) 67 | // } 68 | 69 | // value.split('-').forEach(x => {x.charAt(0).toUpperCase() + x.slice(1)}).join('-'), 70 | const variable = variables[variableName] 71 | let value = chunks.slice(2).join(' ') 72 | const arrayOfDecks = ankiCards 73 | 74 | if (chunks.length >= 3) { 75 | return (async () => { 76 | if (await decks() === null) { 77 | throw new WorkflowError('Decks was not found, check your Anki profile', errorAction('!deck decks')) 78 | } 79 | 80 | if (Object.getOwnPropertyNames(arrayOfDecks).indexOf(value) === -1) { 81 | value = value.split(' ') 82 | .map(x => x.charAt(0).toUpperCase() + x.slice(1)) 83 | .join('-') 84 | return variable.outputOptions.render( 85 | value, 86 | name => `!deck ${variableName} ${name}`, 87 | arrayOfDecks, 88 | './icons/deck.png' 89 | ) 90 | } 91 | 92 | return [{ 93 | title: `Set ${variableName} to '${value}'`, 94 | subtitle: `Old value ⇒ ${alfy.config.get(variableName)}`, 95 | valid: true, 96 | arg: JSON.stringify({ 97 | alfredworkflow: { 98 | variables: { 99 | action: 'set', 100 | /* eslint-disable camelcase */ 101 | config_variable_deck: variableName, 102 | config_value: value 103 | /* eslint-enable camelcase */ 104 | } 105 | } 106 | }) 107 | }] 108 | })() 109 | } 110 | } 111 | 112 | module.exports.meta = { 113 | name: '!deck', 114 | usage: '!deck to another deck', 115 | help: 'Create deck by the given value or new value.', 116 | autocomplete: '!deck ', 117 | icon: {path: './icons/deck-settings.png'} 118 | } 119 | 120 | module.exports.match = input => { 121 | return input.indexOf('!deck') === 0 122 | } 123 | -------------------------------------------------------------------------------- /src/wf/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const Handlebars = require('handlebars') 3 | const Entities = require('html-entities').AllHtmlEntities 4 | const alfy = require('alfy') 5 | const jsonfile = require('jsonfile') 6 | const config = require('../config').card 7 | const {getProfileName} = require('../config') 8 | const ankiInfo = require('../anki/anki-info') 9 | const {Render} = require('../utils/engine') 10 | const {markdownIt} = require('../utils/engine') 11 | 12 | const header = require(`${process.env.alfred_workflow_data}/header.json`) 13 | const modelFieldNames = require(`${process.env.alfred_workflow_data}/anki-model-fields.json`) 14 | 15 | const entities = new Entities() 16 | 17 | const inFile = './src/input/preview/preview.hbs' 18 | const outFile = `${config.mediaDir}_preview.html` 19 | const source = fs.readFileSync(inFile, 'utf8') 20 | const modelId = alfy.config.get('default-model') ? alfy.config.get('default-model')[Object.keys(alfy.config.get('default-model'))[0]] : null 21 | 22 | const handleFields = async () => { 23 | const items = [] 24 | const subtitle = 'toggle the option: remember last input' 25 | 26 | const mods = field => { 27 | return { 28 | ctrl: { 29 | subtitle, 30 | variables: {[field]: field} 31 | }, 32 | cmd: { 33 | valid: true, 34 | subtitle: '\t Add New Card \t🎉', 35 | variables: { 36 | action: 'make-new-card' 37 | } 38 | } 39 | } 40 | } 41 | 42 | const variables = field => { 43 | return { 44 | mode: field, 45 | action: 'work', 46 | currentText: header[modelId] && header[modelId][field] ? header[modelId][field] : '' 47 | } 48 | } 49 | 50 | for (const field of modelFieldNames) { 51 | const item = new Render(field, 52 | 'title', 'quicklookurl', 'variables', 'subtitle', 'arg', 'icon', 'mods') 53 | item.title = header[modelId] && header[modelId][field] ? header[modelId][field] : '...' 54 | item.subtitle = field 55 | item.arg = '' 56 | item.quicklookurl = `${config.mediaDir}/_preview.html` 57 | item.icon = alfy.config.get('fields') && alfy.config.get('fields')[modelId][field] === 'rli' ? fs.existsSync(`./icons/${field}.png`) ? `./icons/${field}_marked.png` : './icons/flag_marked.png' : fs.existsSync(`./icons/${field}.png`) ? `./icons/${field}.png` : './icons/flag.png' 58 | item.variables = variables(field) 59 | item.mods = mods(field) 60 | items.push(item.getProperties()) 61 | } 62 | 63 | const tags = new Render('Tags', 64 | 'title', 'variables', 'quicklookurl', 'subtitle', 'arg', 'icon', 'mods') 65 | tags.title = header[modelId] && header[modelId].Tag ? header[modelId].Tag : '...' 66 | tags.subtitle = 'Choose your tags' 67 | tags.arg = '' 68 | tags.quicklookurl = `${config.mediaDir}/_preview.html` 69 | tags.icon = alfy.config.get('Tag') === 'rli' ? './icons/tag_marked.png' : './icons/tag.png' 70 | tags.variables = { 71 | mode: 'Tags', 72 | action: 'work' 73 | } 74 | tags.mods = mods('Tag') 75 | items.push(tags.getProperties()) 76 | return items 77 | } 78 | 79 | module.exports.fields = async () => { 80 | let result 81 | await getProfileName() 82 | const ankiInfoRes = await ankiInfo() 83 | const fields = await handleFields() 84 | fields.unshift(ankiInfoRes[0] ? ankiInfoRes[0] : ankiInfoRes) 85 | if (fields && fields[0] && fields[0].subtitle && fields[0].name === 'intro') { 86 | result = alfy.inputMatches(fields, 'subtitle') 87 | } else { 88 | result = await ankiInfo() 89 | } 90 | 91 | return result 92 | } 93 | 94 | const template = Handlebars.compile(source) 95 | 96 | let toRender = '' 97 | const currentValueOfHeader = jsonfile.readFileSync(`${process.env.alfred_workflow_data}/header.json`) 98 | if (currentValueOfHeader[modelId]) { 99 | markdownIt(currentValueOfHeader[modelId]) 100 | 101 | for (const fieldName of modelFieldNames) { 102 | toRender += ` 103 | 104 | ${fieldName} 105 | ${currentValueOfHeader[modelId][fieldName] ? currentValueOfHeader[modelId][fieldName] : '...'} 106 | 107 | ` 108 | } 109 | } 110 | 111 | const result = template({ 112 | FIELD: toRender 113 | }) 114 | try { 115 | fs.writeFileSync(outFile, entities.decode(result)) 116 | } catch (error) { 117 | } 118 | -------------------------------------------------------------------------------- /src/utils/error.js: -------------------------------------------------------------------------------- 1 | /* eslint one-var: [2, { var: "always", let: "always" }] */ 2 | /* eslint quotes: ["error", "single", { "allowTemplateLiterals": true }] */ 3 | 4 | const alfy = require('alfy') 5 | 6 | module.exports = class WorkflowError extends Error { 7 | constructor(message, data) { 8 | // `data` is an object with the following optional props: 9 | // .tip - message to show so the user can fix the error 10 | // .autocomplete - self-explanatory 11 | // if (message === 'failed to connect to AnkiConnect') { 12 | // data.icon.path = './icons/not-connected.png' 13 | // } 14 | super(message) 15 | this.name = 'Workflow' 16 | 17 | Object.assign(this, data) 18 | } 19 | } 20 | module.exports.errorAction = reason => { 21 | let title, subtitle, autocomplete, text 22 | switch (reason) { 23 | case 'waiting-for-refresh': 24 | title = 'waiting-for-refresh' 25 | break 26 | case 'main': 27 | title = 'Working without AnkiConnect' 28 | subtitle = '⇧↵ to open Anki.' 29 | text = {largetype: subtitle} 30 | autocomplete = '' 31 | break 32 | case 'profile': 33 | title = 'Collection is not available' 34 | subtitle = '⇧↵ to open Anki and choose profile. | ⌘L to see the stack trace' 35 | text = {largetype: subtitle} 36 | break 37 | case 'modelExist': 38 | if (alfy.config.get('default-model')) { 39 | title = `model "${Object.keys(alfy.config.get('default-model'))[0]}" was not found` 40 | subtitle = `Pres ⇤ (tab) to pick out exist note type. Or ⇧↵ to open Anki & choose another profile which has current note type.` 41 | text = {largetype: subtitle} 42 | autocomplete = '!models default-model ' 43 | } else { 44 | title = `no model selected` 45 | subtitle = `Pres ⇤ (tab) to pick out exist note type. Or ⇧↵ to open Anki & choose another profile which has current note type.` 46 | text = {largetype: subtitle} 47 | autocomplete = '!models default-model ' 48 | } 49 | 50 | break 51 | case 'new-profile': 52 | title = `Select model for [👤${alfy.config.get('default-profile')}] profile` 53 | subtitle = `Pres ⇤ (tab) to pick out exist profile. Or ⇧↵ to open Anki & choose another profile which has current note type.` 54 | text = {largetype: subtitle} 55 | autocomplete = '!models default-model ' 56 | break 57 | case '!deck decks': 58 | title = null 59 | subtitle = '⇧↵ to open Anki. | ⌘L to see the stack trace' 60 | text = {largetype: subtitle} 61 | autocomplete = '!deck ' 62 | break 63 | case '!model models': 64 | title = null 65 | subtitle = '⇧↵ to open Anki. | ⌘L to see the stack trace' 66 | text = {largetype: subtitle} 67 | autocomplete = '!model ' 68 | break 69 | case '!del decks': 70 | title = null 71 | subtitle = '⇧↵ to open Anki. | ⌘L to see the stack trace' 72 | text = {largetype: subtitle} 73 | autocomplete = '!del ' 74 | break 75 | case 'alfred-settings': 76 | title = 'set some Profile for Workflow' 77 | subtitle = `Pres ⇤ (tab) to pick out the profile by the given value.. Or ⇧↵ to open Anki & choose another` 78 | autocomplete = '!profile default-profile ' 79 | break 80 | default: 81 | break 82 | } 83 | 84 | return { 85 | title, 86 | subtitle, 87 | autocomplete, 88 | text, 89 | mods: { 90 | shift: { 91 | variables: { 92 | run: 'anki' 93 | }, 94 | valid: true, 95 | subtitle: 'Anki will be run' 96 | } 97 | }, 98 | valid: false, 99 | icon: { 100 | path: './icons/not-connected.png' 101 | } 102 | } 103 | } 104 | 105 | module.exports.errorOut = err => { 106 | const messages = [] 107 | 108 | if (err.tip) { 109 | messages.push(err.tip) 110 | } 111 | 112 | messages.push('Activate this item to try again.') 113 | messages.push('⌘L to see the stack trace') 114 | return [{ 115 | title: err.title ? err.title : `${err.message} `, 116 | subtitle: err.subtitle ? err.subtitle : messages.join(' | '), 117 | autocomplete: err.autocomplete ? err.autocomplete : '', 118 | icon: err.icon ? err.icon : { 119 | path: alfy.icon.error 120 | }, 121 | valid: err.valid ? err.valid : true, 122 | variables: err.variables ? err.variables : {}, 123 | text: { 124 | largetype: `${err.subtitle} \n\n${err.stack} `, 125 | copy: err.stack 126 | }, 127 | mods: err.mods ? err.mods : { 128 | mods: {} 129 | } 130 | }] 131 | } 132 | -------------------------------------------------------------------------------- /src/cmd/refresh.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | 'use strict' 3 | const jsonfile = require('jsonfile') 4 | const alfy = require('alfy') 5 | 6 | const config = require('../config').cmd 7 | const decks = require('../anki/anki-decks') 8 | const models = require('../anki/anki-models') 9 | const profiles = require('../anki/anki-profiles') 10 | const WorkflowError = require('../utils/error') 11 | const {hasOwnProperty} = require('../utils') 12 | const {cards, areDue, areSuspend} = require('../anki/anki-cards') 13 | 14 | const envOfWF = process.env.alfred_workflow_data 15 | const fileAnkiCards = `${envOfWF}/anki-cards.json` 16 | const fileAnkiModels = `${envOfWF}/anki-models.json` 17 | const cards2Json = {} 18 | 19 | const variables = { 20 | 'refreshing...': { 21 | refresh: 'choose ...', 22 | outputOptions: decks 23 | } 24 | } 25 | 26 | const runUpdate = async () => { 27 | const ankiDecks = await decks() 28 | const ankiModels = await models() 29 | const ankiCardsCount = await cards(ankiDecks) 30 | await profiles() 31 | /* eslint-disable no-await-in-loop */ 32 | for (let i = 0; i < ankiCardsCount.length; i++) { 33 | const elem = await areDue(ankiCardsCount[i]) 34 | const elem2 = await areSuspend(ankiCardsCount[i]) 35 | let countFormat = elem.length.toString() 36 | let countNew = elem.filter(x => x).length.toString() 37 | while (countFormat.length <= 10) { 38 | countFormat += ' ' 39 | } 40 | 41 | while (countNew.length <= 10) { 42 | countNew += ' ' 43 | } 44 | 45 | cards2Json[`${ankiDecks[i]}`] = `\tCards: ${countFormat}New: ${countNew}Due: ${ankiCardsCount[i].length - elem 46 | .filter(x => x).length}\t\t${elem2.filter(x => x).length > 0 ? ` Suspend: ${elem2.filter(x => x).length}` : ''}` 47 | } 48 | 49 | /* eslint-enable no-await-in-loop */ 50 | jsonfile.writeFile(fileAnkiCards, cards2Json, { 51 | spaces: 2 52 | }, error => { 53 | if (error !== null) { 54 | process.stderr.write(error) 55 | } 56 | }) 57 | 58 | jsonfile.writeFile(fileAnkiModels, ankiModels, { 59 | spaces: 2 60 | }, error => { 61 | if (error !== null) { 62 | process.stderr.write(error) 63 | } 64 | }) 65 | alfy.cache.set('refresh-done', true) 66 | } 67 | 68 | if (process.argv[2] === 'runref') { 69 | runUpdate() 70 | } 71 | 72 | // Output matching for config variables 73 | const outputVariables = pattern => { 74 | if (!pattern) { 75 | pattern = '' 76 | } 77 | 78 | const vars = Object.keys(config.refresh) 79 | 80 | const mapper = key => ({ 81 | title: key, 82 | subtitle: pattern === '' ? 'Hit ↵ to refresh cards in decks | It will take a few seconds' : 'hold on, your collection will be update', 83 | valid: false, 84 | autocomplete: '!refresh refreshing... ', 85 | icon: {path: './icons/refresh.png'} 86 | }) 87 | 88 | const out = alfy.matches(pattern, Object.keys(config.refresh)).map(mapper) 89 | 90 | return out.length === 0 ? vars.map(mapper) : out 91 | } 92 | 93 | module.exports = async input => { 94 | // !refresh command value 95 | 96 | if (typeof input !== 'string') { 97 | throw new TypeError('input should be a string') 98 | } 99 | 100 | const chunks = input.split(' ') 101 | 102 | if (chunks.length === 1) { 103 | return outputVariables() 104 | } 105 | 106 | if (chunks.length === 2) { 107 | return outputVariables(chunks[1]) 108 | } 109 | 110 | const variableName = chunks[1] 111 | 112 | // Throw if variable is invalid 113 | if (!hasOwnProperty(variables, variableName)) { 114 | throw new WorkflowError(`Variable '${variableName}' does not exist`, { 115 | autocomplete: '!refresh ' 116 | }) 117 | } 118 | 119 | const value = chunks.slice(2).join(' ') 120 | if (chunks.length >= 3) { 121 | return [{ 122 | title: 'Collection updated', 123 | subtitle: 'hit ↵ to go settings', 124 | valid: false, 125 | autocomplete: '!', 126 | arg: JSON.stringify({ 127 | alfredworkflow: { 128 | autocomplete: '!', 129 | variables: { 130 | action: 'refresh', 131 | config_variable: variableName, 132 | config_value: value 133 | }, 134 | arg: await runUpdate() 135 | } 136 | }), 137 | icon: { 138 | path: './icons/warning.png' 139 | } 140 | }] 141 | } 142 | } 143 | 144 | module.exports.meta = { 145 | name: '!refresh', 146 | usage: '!refresh to another deck', 147 | help: 'Refreshing decks by Anki-Connect.', 148 | autocomplete: '!refresh ', 149 | icon: {path: './icons/refresh.png'} 150 | } 151 | 152 | module.exports.match = input => { 153 | return input.indexOf('!refresh') === 0 154 | } 155 | 156 | module.exports.update = runUpdate 157 | -------------------------------------------------------------------------------- /src/input/preview/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.1 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; 11 | /* 1 */ 12 | -ms-text-size-adjust: 100%; 13 | /* 2 */ 14 | -webkit-text-size-adjust: 100%; 15 | /* 2 */ 16 | } 17 | 18 | /** 19 | * Remove default margin. 20 | */ 21 | 22 | body { 23 | margin: 0; 24 | } 25 | 26 | /* HTML5 display definitions 27 | ========================================================================== */ 28 | 29 | /** 30 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 31 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox. 32 | * Correct `block` display not defined for `main` in IE 11. 33 | */ 34 | 35 | article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { 36 | display: block; 37 | } 38 | 39 | /** 40 | * 1. Correct `inline-block` display not defined in IE 8/9. 41 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 42 | */ 43 | 44 | audio, canvas, progress, video { 45 | display: inline-block; 46 | /* 1 */ 47 | vertical-align: baseline; 48 | /* 2 */ 49 | } 50 | 51 | /** 52 | * Prevent modern browsers from displaying `audio` without controls. 53 | * Remove excess height in iOS 5 devices. 54 | */ 55 | 56 | audio:not([controls]) { 57 | display: none; 58 | height: 0; 59 | } 60 | 61 | /** 62 | * Address `[hidden]` styling not present in IE 8/9/10. 63 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 64 | */ 65 | 66 | [hidden], template { 67 | display: none; 68 | } 69 | 70 | /* Links 71 | ========================================================================== */ 72 | 73 | /** 74 | * Remove the gray background color from active links in IE 10. 75 | */ 76 | 77 | a { 78 | background: transparent; 79 | } 80 | 81 | /** 82 | * Improve readability when focused and also mouse hovered in all browsers. 83 | */ 84 | 85 | a:active, a:hover { 86 | outline: 0; 87 | } 88 | 89 | /* Text-level semantics 90 | ========================================================================== */ 91 | 92 | /** 93 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 94 | */ 95 | 96 | abbr[title] { 97 | border-bottom: 1px dotted; 98 | } 99 | 100 | /** 101 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 102 | */ 103 | 104 | b, strong { 105 | font-weight: bold; 106 | } 107 | 108 | /** 109 | * Address styling not present in Safari and Chrome. 110 | */ 111 | 112 | dfn { 113 | font-style: italic; 114 | } 115 | 116 | /** 117 | * Address variable `h1` font-size and margin within `section` and `article` 118 | * contexts in Firefox 4+, Safari, and Chrome. 119 | */ 120 | 121 | h1 { 122 | font-size: 2em; 123 | margin: 0.67em 0; 124 | } 125 | 126 | /** 127 | * Address styling not present in IE 8/9. 128 | */ 129 | 130 | mark { 131 | background: #ff0; 132 | color: #000; 133 | } 134 | 135 | /** 136 | * Address inconsistent and variable font size in all browsers. 137 | */ 138 | 139 | small { 140 | font-size: 80%; 141 | } 142 | 143 | /** 144 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 145 | */ 146 | 147 | sub, sup { 148 | font-size: 75%; 149 | line-height: 0; 150 | position: relative; 151 | vertical-align: baseline; 152 | } 153 | 154 | sup { 155 | top: -0.5em; 156 | } 157 | 158 | sub { 159 | bottom: -0.25em; 160 | } 161 | 162 | /* Embedded content 163 | ========================================================================== */ 164 | 165 | /** 166 | * Remove border when inside `a` element in IE 8/9/10. 167 | */ 168 | 169 | img { 170 | border: 0; 171 | } 172 | 173 | /** 174 | * Correct overflow not hidden in IE 9/10/11. 175 | */ 176 | 177 | svg:not(:root) { 178 | overflow: hidden; 179 | } 180 | 181 | /* Grouping content 182 | ========================================================================== */ 183 | 184 | /** 185 | * Address margin not present in IE 8/9 and Safari. 186 | */ 187 | 188 | figure { 189 | margin: 1em 40px; 190 | } 191 | 192 | /** 193 | * Address differences between Firefox and other browsers. 194 | */ 195 | 196 | hr { 197 | -moz-box-sizing: content-box; 198 | box-sizing: content-box; 199 | height: 0; 200 | } 201 | 202 | /** 203 | * Contain overflow in all browsers. 204 | */ 205 | 206 | pre { 207 | overflow: auto; 208 | } 209 | 210 | /** 211 | * Address odd `em`-unit font size rendering in all browsers. 212 | */ 213 | 214 | code, kbd, pre, samp { 215 | font-family: monospace, monospace; 216 | font-size: 1em; 217 | } 218 | 219 | /* Forms 220 | ========================================================================== */ 221 | 222 | /** 223 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 224 | * styling of `select`, unless a `border` property is set. 225 | */ 226 | 227 | /** 228 | * 1. Correct color not being inherited. 229 | * Known issue: affects color of disabled elements. 230 | * 2. Correct font properties not being inherited. 231 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 232 | */ 233 | 234 | button, input, optgroup, select, textarea { 235 | color: inherit; 236 | /* 1 */ 237 | font: inherit; 238 | /* 2 */ 239 | margin: 0; 240 | /* 3 */ 241 | } 242 | 243 | /** 244 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 245 | */ 246 | 247 | button { 248 | overflow: visible; 249 | } 250 | 251 | /** 252 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 253 | * All other form control elements do not inherit `text-transform` values. 254 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 255 | * Correct `select` style inheritance in Firefox. 256 | */ 257 | 258 | button, select { 259 | text-transform: none; 260 | } 261 | 262 | /** 263 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 264 | * and `video` controls. 265 | * 2. Correct inability to style clickable `input` types in iOS. 266 | * 3. Improve usability and consistency of cursor style between image-type 267 | * `input` and others. 268 | */ 269 | 270 | button, html input[type="button"], 271 | /* 1 */ 272 | 273 | input[type="reset"], input[type="submit"] { 274 | -webkit-appearance: button; 275 | /* 2 */ 276 | cursor: pointer; 277 | /* 3 */ 278 | } 279 | 280 | /** 281 | * Re-set default cursor for disabled elements. 282 | */ 283 | 284 | button[disabled], html input[disabled] { 285 | cursor: default; 286 | } 287 | 288 | /** 289 | * Remove inner padding and border in Firefox 4+. 290 | */ 291 | 292 | button::-moz-focus-inner, input::-moz-focus-inner { 293 | border: 0; 294 | padding: 0; 295 | } 296 | 297 | /** 298 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 299 | * the UA stylesheet. 300 | */ 301 | 302 | input { 303 | line-height: normal; 304 | } 305 | 306 | /** 307 | * It's recommended that you don't attempt to style these elements. 308 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 309 | * 310 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 311 | * 2. Remove excess padding in IE 8/9/10. 312 | */ 313 | 314 | input[type="checkbox"], input[type="radio"] { 315 | box-sizing: border-box; 316 | /* 1 */ 317 | padding: 0; 318 | /* 2 */ 319 | } 320 | 321 | /** 322 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 323 | * `font-size` values of the `input`, it causes the cursor style of the 324 | * decrement button to change from `default` to `text`. 325 | */ 326 | 327 | input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { 328 | height: auto; 329 | } 330 | 331 | /** 332 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 333 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 334 | * (include `-moz` to future-proof). 335 | */ 336 | 337 | input[type="search"] { 338 | -webkit-appearance: textfield; 339 | /* 1 */ 340 | -moz-box-sizing: content-box; 341 | -webkit-box-sizing: content-box; 342 | /* 2 */ 343 | box-sizing: content-box; 344 | } 345 | 346 | /** 347 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 348 | * Safari (but not Chrome) clips the cancel button when the search input has 349 | * padding (and `textfield` appearance). 350 | */ 351 | 352 | input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { 353 | -webkit-appearance: none; 354 | } 355 | 356 | /** 357 | * Define consistent border, margin, and padding. 358 | */ 359 | 360 | fieldset { 361 | border: 1px solid #c0c0c0; 362 | margin: 0 2px; 363 | padding: 0.35em 0.625em 0.75em; 364 | } 365 | 366 | /** 367 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 368 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 369 | */ 370 | 371 | legend { 372 | border: 0; 373 | /* 1 */ 374 | padding: 0; 375 | /* 2 */ 376 | } 377 | 378 | /** 379 | * Remove default vertical scrollbar in IE 8/9/10/11. 380 | */ 381 | 382 | textarea { 383 | overflow: auto; 384 | } 385 | 386 | /** 387 | * Don't inherit the `font-weight` (applied by a rule above). 388 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 389 | */ 390 | 391 | optgroup { 392 | font-weight: bold; 393 | } 394 | 395 | /* Tables 396 | ========================================================================== */ 397 | 398 | /** 399 | * Remove most spacing between table cells. 400 | */ 401 | 402 | table { 403 | /* border-collapse: collapse; */ 404 | border-collapse: separate; 405 | border-spacing: 1px; 406 | } 407 | 408 | td, th { 409 | padding: 0; 410 | } -------------------------------------------------------------------------------- /info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bundleid 6 | org.bikenik.anki 7 | category 8 | Productivity 9 | connections 10 | 11 | 0157B150-6312-4618-9078-CB436756CFEE 12 | 13 | 14 | destinationuid 15 | D2305FF6-F132-45FF-AA14-A5A706CB746E 16 | modifiers 17 | 0 18 | modifiersubtext 19 | 20 | vitoclose 21 | 22 | 23 | 24 | 0171E88F-3E0C-4C78-9843-1CD82699A511 25 | 26 | 27 | destinationuid 28 | 5871888C-AD47-4C28-91F0-F3B2B4AEAE5D 29 | modifiers 30 | 0 31 | modifiersubtext 32 | 33 | vitoclose 34 | 35 | 36 | 37 | 16E77EDF-F1CB-4F48-BB70-A5EBC420693C 38 | 39 | 2C87ECB2-70AF-429F-90EA-C89E93DCB148 40 | 41 | 42 | destinationuid 43 | 65611F88-01F2-4A24-A10E-4192327061B4 44 | modifiers 45 | 0 46 | modifiersubtext 47 | 48 | vitoclose 49 | 50 | 51 | 52 | 2D76F2C6-18C2-4E97-9849-7CFF4801C9EE 53 | 54 | 55 | destinationuid 56 | 46444138-C3AC-4805-A574-C1F1B70D7EDB 57 | modifiers 58 | 0 59 | modifiersubtext 60 | 61 | vitoclose 62 | 63 | 64 | 65 | 2DD4369C-2758-4547-9744-48EF75136302 66 | 67 | 68 | destinationuid 69 | 95BE8DC6-2A86-4AEC-BCC4-4E9D459D4076 70 | modifiers 71 | 0 72 | modifiersubtext 73 | 74 | vitoclose 75 | 76 | 77 | 78 | 3533497B-3633-4BB9-94DF-FB47E1DB26EA 79 | 80 | 81 | destinationuid 82 | CFC28381-4945-4F56-B327-968C1DA67A3B 83 | modifiers 84 | 0 85 | modifiersubtext 86 | 87 | vitoclose 88 | 89 | 90 | 91 | 38CC2093-2FE4-47ED-9199-5EBB31ED6F14 92 | 93 | 94 | destinationuid 95 | D00E50E5-A246-4577-9EA4-1D8272EBE61A 96 | modifiers 97 | 0 98 | modifiersubtext 99 | 100 | vitoclose 101 | 102 | 103 | 104 | 41AD6E48-2472-43AD-B5DD-2AA26C5941EE 105 | 106 | 107 | destinationuid 108 | 9C7FC23C-28CE-4D31-B999-DA0989811A89 109 | modifiers 110 | 0 111 | modifiersubtext 112 | 113 | vitoclose 114 | 115 | 116 | 117 | 46444138-C3AC-4805-A574-C1F1B70D7EDB 118 | 119 | 120 | destinationuid 121 | 9F0DF1B9-583D-4458-850A-82C5852CB2AD 122 | modifiers 123 | 0 124 | modifiersubtext 125 | 126 | vitoclose 127 | 128 | 129 | 130 | destinationuid 131 | 332408A1-AB50-438A-B81C-53977A92FC49 132 | modifiers 133 | 0 134 | modifiersubtext 135 | 136 | vitoclose 137 | 138 | 139 | 140 | 47764D4F-265E-4871-90AB-0D174DD3159A 141 | 142 | 143 | destinationuid 144 | 487DCA31-FA43-4421-B268-93183B4FC353 145 | modifiers 146 | 0 147 | modifiersubtext 148 | 149 | vitoclose 150 | 151 | 152 | 153 | 487DCA31-FA43-4421-B268-93183B4FC353 154 | 155 | 156 | destinationuid 157 | CF402DBC-4552-4802-9382-6A2D51DEC2A5 158 | modifiers 159 | 0 160 | modifiersubtext 161 | 162 | vitoclose 163 | 164 | 165 | 166 | destinationuid 167 | 41AD6E48-2472-43AD-B5DD-2AA26C5941EE 168 | modifiers 169 | 524288 170 | modifiersubtext 171 | 172 | vitoclose 173 | 174 | 175 | 176 | destinationuid 177 | 4CA1F45A-A969-40E0-89C9-7AD7BE097FE7 178 | modifiers 179 | 0 180 | modifiersubtext 181 | 182 | vitoclose 183 | 184 | 185 | 186 | destinationuid 187 | ADACAE15-F887-454C-99CD-CBEC20560AB6 188 | modifiers 189 | 0 190 | modifiersubtext 191 | 192 | vitoclose 193 | 194 | 195 | 196 | destinationuid 197 | 5CC8BE37-69CC-43B2-90D0-C1D3B03FCB5F 198 | modifiers 199 | 0 200 | modifiersubtext 201 | 202 | vitoclose 203 | 204 | 205 | 206 | destinationuid 207 | 94AAD358-0F71-4EC8-8C0B-71C1409F85A2 208 | modifiers 209 | 0 210 | modifiersubtext 211 | 212 | vitoclose 213 | 214 | 215 | 216 | destinationuid 217 | 73BE45C1-3769-4975-88B4-2E26E0BFCE9A 218 | modifiers 219 | 0 220 | modifiersubtext 221 | 222 | vitoclose 223 | 224 | 225 | 226 | destinationuid 227 | EEB1004E-BFEB-4865-B8D1-21868B7BF8E7 228 | modifiers 229 | 0 230 | modifiersubtext 231 | 232 | vitoclose 233 | 234 | 235 | 236 | destinationuid 237 | E2770E96-6CF7-4A85-A009-F3C836A40E54 238 | modifiers 239 | 0 240 | modifiersubtext 241 | 242 | vitoclose 243 | 244 | 245 | 246 | destinationuid 247 | 0171E88F-3E0C-4C78-9843-1CD82699A511 248 | modifiers 249 | 0 250 | modifiersubtext 251 | 252 | vitoclose 253 | 254 | 255 | 256 | destinationuid 257 | 38CC2093-2FE4-47ED-9199-5EBB31ED6F14 258 | modifiers 259 | 0 260 | modifiersubtext 261 | 262 | vitoclose 263 | 264 | 265 | 266 | destinationuid 267 | A64BC663-F93F-4363-8F54-5A92B480F3FD 268 | modifiers 269 | 262144 270 | modifiersubtext 271 | 272 | vitoclose 273 | 274 | 275 | 276 | 4CA1F45A-A969-40E0-89C9-7AD7BE097FE7 277 | 278 | 279 | destinationuid 280 | 75FCBB30-CC79-454C-A038-B63F930D9BC8 281 | modifiers 282 | 0 283 | modifiersubtext 284 | 285 | vitoclose 286 | 287 | 288 | 289 | 4D444E9D-FDA8-4546-9527-75E091D79A30 290 | 291 | 292 | destinationuid 293 | D4169A9B-5FF4-40F2-8528-AAA440E3CD78 294 | modifiers 295 | 0 296 | modifiersubtext 297 | 298 | vitoclose 299 | 300 | 301 | 302 | 5871888C-AD47-4C28-91F0-F3B2B4AEAE5D 303 | 304 | 305 | destinationuid 306 | 72B24521-F77E-4B2A-9CB7-C96E9A0BD14D 307 | modifiers 308 | 0 309 | modifiersubtext 310 | 311 | vitoclose 312 | 313 | 314 | 315 | 5B3CCCFE-2E4F-45D4-ADFF-7D36030CB26D 316 | 317 | 318 | destinationuid 319 | FD14A9BC-87AB-4BCB-9D99-536259E7C614 320 | modifiers 321 | 0 322 | modifiersubtext 323 | 324 | vitoclose 325 | 326 | 327 | 328 | 5C10A737-34D1-46EA-958E-C3B53C8B7E48 329 | 330 | 331 | destinationuid 332 | 2DD4369C-2758-4547-9744-48EF75136302 333 | modifiers 334 | 0 335 | modifiersubtext 336 | 337 | vitoclose 338 | 339 | 340 | 341 | 5CC8BE37-69CC-43B2-90D0-C1D3B03FCB5F 342 | 343 | 344 | destinationuid 345 | B7625B29-64C5-4BA2-B97B-83BE2F4B6192 346 | modifiers 347 | 0 348 | modifiersubtext 349 | 350 | vitoclose 351 | 352 | 353 | 354 | 63BFDB58-03D2-44A3-9C5B-F3C9BD0ED4A2 355 | 356 | 357 | destinationuid 358 | D6A58C8E-76B1-4B21-A512-27424CF8E50B 359 | modifiers 360 | 0 361 | modifiersubtext 362 | 363 | vitoclose 364 | 365 | 366 | 367 | 72B24521-F77E-4B2A-9CB7-C96E9A0BD14D 368 | 369 | 370 | destinationuid 371 | 6A1870C0-B298-4F2B-AE7C-43BDBC5609F7 372 | modifiers 373 | 0 374 | modifiersubtext 375 | 376 | vitoclose 377 | 378 | 379 | 380 | destinationuid 381 | 4B7B78BA-8332-4539-8C65-4673A7A2E94E 382 | modifiers 383 | 0 384 | modifiersubtext 385 | 386 | vitoclose 387 | 388 | 389 | 390 | 73BE45C1-3769-4975-88B4-2E26E0BFCE9A 391 | 392 | 75FCBB30-CC79-454C-A038-B63F930D9BC8 393 | 394 | 395 | destinationuid 396 | 9F0DF1B9-583D-4458-850A-82C5852CB2AD 397 | modifiers 398 | 0 399 | modifiersubtext 400 | 401 | vitoclose 402 | 403 | 404 | 405 | destinationuid 406 | 332408A1-AB50-438A-B81C-53977A92FC49 407 | modifiers 408 | 0 409 | modifiersubtext 410 | 411 | vitoclose 412 | 413 | 414 | 415 | 7602FF28-6A79-4BF2-8643-FBEF6A22EFB4 416 | 417 | 418 | destinationuid 419 | 9C7FC23C-28CE-4D31-B999-DA0989811A89 420 | modifiers 421 | 0 422 | modifiersubtext 423 | 424 | vitoclose 425 | 426 | 427 | 428 | 8786E11F-9169-4F88-863F-15A30263717C 429 | 430 | 431 | destinationuid 432 | FB1372F3-E68F-43E3-B6CA-1C112CB95F12 433 | modifiers 434 | 0 435 | modifiersubtext 436 | 437 | vitoclose 438 | 439 | 440 | 441 | 94AAD358-0F71-4EC8-8C0B-71C1409F85A2 442 | 443 | 444 | destinationuid 445 | A63E0832-37B4-49C3-A8CF-8D0B02D75DE6 446 | modifiers 447 | 0 448 | modifiersubtext 449 | 450 | vitoclose 451 | 452 | 453 | 454 | 95BE8DC6-2A86-4AEC-BCC4-4E9D459D4076 455 | 456 | 457 | destinationuid 458 | D00E50E5-A246-4577-9EA4-1D8272EBE61A 459 | modifiers 460 | 0 461 | modifiersubtext 462 | 463 | vitoclose 464 | 465 | 466 | 467 | 9C59315A-EBF3-4624-A015-3CBD7CD88BE2 468 | 469 | 470 | destinationuid 471 | 487DCA31-FA43-4421-B268-93183B4FC353 472 | modifiers 473 | 0 474 | modifiersubtext 475 | 476 | vitoclose 477 | 478 | 479 | 480 | 9C7FC23C-28CE-4D31-B999-DA0989811A89 481 | 482 | 483 | destinationuid 484 | 0157B150-6312-4618-9078-CB436756CFEE 485 | modifiers 486 | 0 487 | modifiersubtext 488 | 489 | vitoclose 490 | 491 | 492 | 493 | 9F0DF1B9-583D-4458-850A-82C5852CB2AD 494 | 495 | A64BC663-F93F-4363-8F54-5A92B480F3FD 496 | 497 | 498 | destinationuid 499 | D00E50E5-A246-4577-9EA4-1D8272EBE61A 500 | modifiers 501 | 0 502 | modifiersubtext 503 | 504 | vitoclose 505 | 506 | 507 | 508 | destinationuid 509 | 4D444E9D-FDA8-4546-9527-75E091D79A30 510 | modifiers 511 | 0 512 | modifiersubtext 513 | 514 | vitoclose 515 | 516 | 517 | 518 | ADACAE15-F887-454C-99CD-CBEC20560AB6 519 | 520 | 521 | destinationuid 522 | 2D76F2C6-18C2-4E97-9849-7CFF4801C9EE 523 | modifiers 524 | 0 525 | modifiersubtext 526 | 527 | vitoclose 528 | 529 | 530 | 531 | B7625B29-64C5-4BA2-B97B-83BE2F4B6192 532 | 533 | 534 | destinationuid 535 | F21B5144-DC68-4065-8B4B-A399D4AD79E8 536 | modifiers 537 | 0 538 | modifiersubtext 539 | 540 | vitoclose 541 | 542 | 543 | 544 | destinationuid 545 | F9B7F4E7-A5EA-40AF-8C7A-90FD40FFBF9F 546 | modifiers 547 | 0 548 | modifiersubtext 549 | 550 | vitoclose 551 | 552 | 553 | 554 | destinationuid 555 | 2C87ECB2-70AF-429F-90EA-C89E93DCB148 556 | modifiers 557 | 0 558 | modifiersubtext 559 | 560 | vitoclose 561 | 562 | 563 | 564 | destinationuid 565 | C38EC994-75BA-4C5F-A461-441C63F573CB 566 | modifiers 567 | 0 568 | modifiersubtext 569 | 570 | vitoclose 571 | 572 | 573 | 574 | C38EC994-75BA-4C5F-A461-441C63F573CB 575 | 576 | 577 | destinationuid 578 | 924BB709-C6AF-41F4-86D4-FE8E4D00E6B1 579 | modifiers 580 | 0 581 | modifiersubtext 582 | 583 | vitoclose 584 | 585 | 586 | 587 | C3AC3CFC-E4C8-4B87-A6CD-9BD13BF9E3F7 588 | 589 | 590 | destinationuid 591 | D911C0B6-8115-460C-89BA-9C9A7C0CB9D6 592 | modifiers 593 | 0 594 | modifiersubtext 595 | 596 | vitoclose 597 | 598 | 599 | 600 | destinationuid 601 | 191E831E-71B9-4553-9340-6D248884CAEC 602 | modifiers 603 | 0 604 | modifiersubtext 605 | 606 | vitoclose 607 | 608 | 609 | 610 | CF402DBC-4552-4802-9382-6A2D51DEC2A5 611 | 612 | 613 | destinationuid 614 | D4618291-BECE-42B6-8C73-378A5A52D4F4 615 | modifiers 616 | 0 617 | modifiersubtext 618 | 619 | vitoclose 620 | 621 | 622 | 623 | CFC28381-4945-4F56-B327-968C1DA67A3B 624 | 625 | 626 | destinationuid 627 | 6A1870C0-B298-4F2B-AE7C-43BDBC5609F7 628 | modifiers 629 | 0 630 | modifiersubtext 631 | 632 | vitoclose 633 | 634 | 635 | 636 | destinationuid 637 | 4B7B78BA-8332-4539-8C65-4673A7A2E94E 638 | modifiers 639 | 0 640 | modifiersubtext 641 | 642 | vitoclose 643 | 644 | 645 | 646 | D00E50E5-A246-4577-9EA4-1D8272EBE61A 647 | 648 | 649 | destinationuid 650 | 3533497B-3633-4BB9-94DF-FB47E1DB26EA 651 | modifiers 652 | 0 653 | modifiersubtext 654 | 655 | vitoclose 656 | 657 | 658 | 659 | destinationuid 660 | 5B3CCCFE-2E4F-45D4-ADFF-7D36030CB26D 661 | modifiers 662 | 0 663 | modifiersubtext 664 | 665 | vitoclose 666 | 667 | 668 | 669 | D4618291-BECE-42B6-8C73-378A5A52D4F4 670 | 671 | 672 | destinationuid 673 | D2305FF6-F132-45FF-AA14-A5A706CB746E 674 | modifiers 675 | 0 676 | modifiersubtext 677 | 678 | vitoclose 679 | 680 | 681 | 682 | D6A58C8E-76B1-4B21-A512-27424CF8E50B 683 | 684 | D911C0B6-8115-460C-89BA-9C9A7C0CB9D6 685 | 686 | 687 | destinationuid 688 | 0E9A7DA0-E3E9-4F4F-96BE-032F1CB4FE95 689 | modifiers 690 | 0 691 | modifiersubtext 692 | 693 | vitoclose 694 | 695 | 696 | 697 | E2770E96-6CF7-4A85-A009-F3C836A40E54 698 | 699 | 700 | destinationuid 701 | 752836EF-6B95-4274-9D66-D7E3F3B02588 702 | modifiers 703 | 0 704 | modifiersubtext 705 | 706 | vitoclose 707 | 708 | 709 | 710 | EEB1004E-BFEB-4865-B8D1-21868B7BF8E7 711 | 712 | 713 | destinationuid 714 | 16E77EDF-F1CB-4F48-BB70-A5EBC420693C 715 | modifiers 716 | 0 717 | modifiersubtext 718 | 719 | vitoclose 720 | 721 | 722 | 723 | F21B5144-DC68-4065-8B4B-A399D4AD79E8 724 | 725 | 726 | destinationuid 727 | C3AC3CFC-E4C8-4B87-A6CD-9BD13BF9E3F7 728 | modifiers 729 | 0 730 | modifiersubtext 731 | 732 | vitoclose 733 | 734 | 735 | 736 | F9B7F4E7-A5EA-40AF-8C7A-90FD40FFBF9F 737 | 738 | 739 | destinationuid 740 | 8786E11F-9169-4F88-863F-15A30263717C 741 | modifiers 742 | 0 743 | modifiersubtext 744 | 745 | vitoclose 746 | 747 | 748 | 749 | FAB4AF74-7BA2-4AB4-9ACE-FE846E7EECE0 750 | 751 | 752 | destinationuid 753 | 73BE45C1-3769-4975-88B4-2E26E0BFCE9A 754 | modifiers 755 | 0 756 | modifiersubtext 757 | 758 | vitoclose 759 | 760 | 761 | 762 | FD14A9BC-87AB-4BCB-9D99-536259E7C614 763 | 764 | 765 | destinationuid 766 | B8286172-11CE-47C6-9815-BC7492B2A31B 767 | modifiers 768 | 0 769 | modifiersubtext 770 | 771 | vitoclose 772 | 773 | 774 | 775 | 776 | createdby 777 | bikenik 778 | description 779 | anki card generator 780 | disabled 781 | 782 | name 783 | anki 784 | objects 785 | 786 | 787 | config 788 | 789 | lastpathcomponent 790 | 791 | onlyshowifquerypopulated 792 | 793 | removeextension 794 | 795 | text 796 | You need fill "Front" & "Back" fields to create new card! 797 | title 798 | ⛔ Requirements: 799 | 800 | type 801 | alfred.workflow.output.notification 802 | uid 803 | D4618291-BECE-42B6-8C73-378A5A52D4F4 804 | version 805 | 1 806 | 807 | 808 | config 809 | 810 | inputstring 811 | {var:action} 812 | matchcasesensitive 813 | 814 | matchmode 815 | 0 816 | matchstring 817 | 0 818 | 819 | type 820 | alfred.workflow.utility.filter 821 | uid 822 | CF402DBC-4552-4802-9382-6A2D51DEC2A5 823 | version 824 | 1 825 | 826 | 827 | config 828 | 829 | externaltriggerid 830 | reset 831 | passinputasargument 832 | 833 | passvariables 834 | 835 | workflowbundleid 836 | self 837 | 838 | type 839 | alfred.workflow.output.callexternaltrigger 840 | uid 841 | 0E9A7DA0-E3E9-4F4F-96BE-032F1CB4FE95 842 | version 843 | 1 844 | 845 | 846 | config 847 | 848 | concurrently 849 | 850 | escaping 851 | 102 852 | script 853 | ./node_modules/.bin/run-node src/wf/header.js "$1" 854 | scriptargtype 855 | 1 856 | scriptfile 857 | 858 | type 859 | 0 860 | 861 | type 862 | alfred.workflow.action.script 863 | uid 864 | 9C7FC23C-28CE-4D31-B999-DA0989811A89 865 | version 866 | 2 867 | 868 | 869 | config 870 | 871 | externaltriggerid 872 | anki 873 | passinputasargument 874 | 875 | passvariables 876 | 877 | workflowbundleid 878 | self 879 | 880 | type 881 | alfred.workflow.output.callexternaltrigger 882 | uid 883 | D2305FF6-F132-45FF-AA14-A5A706CB746E 884 | version 885 | 1 886 | 887 | 888 | config 889 | 890 | triggerid 891 | reset 892 | 893 | type 894 | alfred.workflow.trigger.external 895 | uid 896 | 7602FF28-6A79-4BF2-8643-FBEF6A22EFB4 897 | version 898 | 1 899 | 900 | 901 | config 902 | 903 | argument 904 | 905 | variables 906 | 907 | action 908 | reset-for-next-card 909 | 910 | 911 | type 912 | alfred.workflow.utility.argument 913 | uid 914 | D911C0B6-8115-460C-89BA-9C9A7C0CB9D6 915 | version 916 | 1 917 | 918 | 919 | config 920 | 921 | inputstring 922 | {var:action} 923 | matchcasesensitive 924 | 925 | matchmode 926 | 0 927 | matchstring 928 | reset 929 | 930 | type 931 | alfred.workflow.utility.filter 932 | uid 933 | 41AD6E48-2472-43AD-B5DD-2AA26C5941EE 934 | version 935 | 1 936 | 937 | 938 | config 939 | 940 | inputstring 941 | {var:action} 942 | matchcasesensitive 943 | 944 | matchmode 945 | 0 946 | matchstring 947 | reset 948 | 949 | type 950 | alfred.workflow.utility.filter 951 | uid 952 | 0157B150-6312-4618-9078-CB436756CFEE 953 | version 954 | 1 955 | 956 | 957 | config 958 | 959 | lastpathcomponent 960 | 961 | onlyshowifquerypopulated 962 | 963 | removeextension 964 | 965 | text 966 | {query} 967 | title 968 | ✌️ Anki Card Created 969 | 970 | type 971 | alfred.workflow.output.notification 972 | uid 973 | 191E831E-71B9-4553-9340-6D248884CAEC 974 | version 975 | 1 976 | 977 | 978 | config 979 | 980 | inputstring 981 | {query} 982 | matchcasesensitive 983 | 984 | matchmode 985 | 2 986 | matchstring 987 | \:\s[0-9]{13} 988 | 989 | type 990 | alfred.workflow.utility.filter 991 | uid 992 | C3AC3CFC-E4C8-4B87-A6CD-9BD13BF9E3F7 993 | version 994 | 1 995 | 996 | 997 | config 998 | 999 | inputstring 1000 | {query} 1001 | matchcasesensitive 1002 | 1003 | matchmode 1004 | 2 1005 | matchstring 1006 | ^[^!] 1007 | 1008 | type 1009 | alfred.workflow.utility.filter 1010 | uid 1011 | F21B5144-DC68-4065-8B4B-A399D4AD79E8 1012 | version 1013 | 1 1014 | 1015 | 1016 | config 1017 | 1018 | alfredfiltersresults 1019 | 1020 | alfredfiltersresultsmatchmode 1021 | 0 1022 | argumenttreatemptyqueryasnil 1023 | 1024 | argumenttrimmode 1025 | 0 1026 | argumenttype 1027 | 1 1028 | escaping 1029 | 102 1030 | queuedelaycustom 1031 | 3 1032 | queuedelayimmediatelyinitially 1033 | 1034 | queuedelaymode 1035 | 0 1036 | queuemode 1037 | 2 1038 | runningsubtext 1039 | choose your tag 1040 | script 1041 | ./node_modules/.bin/run-node ./src/wf/fields.js "$1" 1042 | scriptargtype 1043 | 1 1044 | scriptfile 1045 | index.js 1046 | subtext 1047 | Anki generator 1048 | title 1049 | TAGS 1050 | type 1051 | 0 1052 | withspace 1053 | 1054 | 1055 | type 1056 | alfred.workflow.input.scriptfilter 1057 | uid 1058 | 75FCBB30-CC79-454C-A038-B63F930D9BC8 1059 | version 1060 | 3 1061 | 1062 | 1063 | config 1064 | 1065 | action 1066 | 0 1067 | argument 1068 | 0 1069 | focusedappvariable 1070 | 1071 | focusedappvariablename 1072 | 1073 | hotkey 1074 | 0 1075 | hotmod 1076 | 1572864 1077 | hotstring 1078 | A 1079 | leftcursor 1080 | 1081 | modsmode 1082 | 0 1083 | relatedAppsMode 1084 | 0 1085 | 1086 | type 1087 | alfred.workflow.trigger.hotkey 1088 | uid 1089 | 47764D4F-265E-4871-90AB-0D174DD3159A 1090 | version 1091 | 2 1092 | 1093 | 1094 | config 1095 | 1096 | concurrently 1097 | 1098 | escaping 1099 | 102 1100 | script 1101 | ./node_modules/.bin/run-node src/wf/header.js "$1" 1102 | scriptargtype 1103 | 1 1104 | scriptfile 1105 | 1106 | type 1107 | 0 1108 | 1109 | type 1110 | alfred.workflow.action.script 1111 | uid 1112 | 9F0DF1B9-583D-4458-850A-82C5852CB2AD 1113 | version 1114 | 2 1115 | 1116 | 1117 | config 1118 | 1119 | inputstring 1120 | {var:mode} 1121 | matchcasesensitive 1122 | 1123 | matchmode 1124 | 0 1125 | matchstring 1126 | tags 1127 | 1128 | type 1129 | alfred.workflow.utility.filter 1130 | uid 1131 | 4CA1F45A-A969-40E0-89C9-7AD7BE097FE7 1132 | version 1133 | 1 1134 | 1135 | 1136 | config 1137 | 1138 | lastpathcomponent 1139 | 1140 | onlyshowifquerypopulated 1141 | 1142 | removeextension 1143 | 1144 | text 1145 | ⛔ {query} 1146 | title 1147 | NOT YET: {var:word} 1148 | 1149 | type 1150 | alfred.workflow.output.notification 1151 | uid 1152 | FB1372F3-E68F-43E3-B6CA-1C112CB95F12 1153 | version 1154 | 1 1155 | 1156 | 1157 | config 1158 | 1159 | inputstring 1160 | {query} 1161 | matchcasesensitive 1162 | 1163 | matchmode 1164 | 2 1165 | matchstring 1166 | ^!err 1167 | 1168 | type 1169 | alfred.workflow.utility.filter 1170 | uid 1171 | F9B7F4E7-A5EA-40AF-8C7A-90FD40FFBF9F 1172 | version 1173 | 1 1174 | 1175 | 1176 | config 1177 | 1178 | matchmode 1179 | 1 1180 | matchstring 1181 | !err:(.*?\.)\s.* 1182 | regexcaseinsensitive 1183 | 1184 | regexmultiline 1185 | 1186 | replacestring 1187 | $1 1188 | 1189 | type 1190 | alfred.workflow.utility.replace 1191 | uid 1192 | 8786E11F-9169-4F88-863F-15A30263717C 1193 | version 1194 | 2 1195 | 1196 | 1197 | config 1198 | 1199 | alfredfiltersresults 1200 | 1201 | alfredfiltersresultsmatchmode 1202 | 0 1203 | argumenttreatemptyqueryasnil 1204 | 1205 | argumenttrimmode 1206 | 0 1207 | argumenttype 1208 | 1 1209 | escaping 1210 | 102 1211 | keyword 1212 | :anki 1213 | queuedelaycustom 1214 | 3 1215 | queuedelayimmediatelyinitially 1216 | 1217 | queuedelaymode 1218 | 0 1219 | queuemode 1220 | 2 1221 | runningsubtext 1222 | Type to start anki card 1223 | script 1224 | ./node_modules/.bin/run-node index.js "$1" 1225 | scriptargtype 1226 | 1 1227 | scriptfile 1228 | index.js 1229 | subtext 1230 | Anki generator 1231 | title 1232 | Getting info from Anki ... 1233 | type 1234 | 0 1235 | withspace 1236 | 1237 | 1238 | type 1239 | alfred.workflow.input.scriptfilter 1240 | uid 1241 | 487DCA31-FA43-4421-B268-93183B4FC353 1242 | version 1243 | 3 1244 | 1245 | 1246 | config 1247 | 1248 | alfredfiltersresults 1249 | 1250 | alfredfiltersresultsmatchmode 1251 | 0 1252 | argumenttreatemptyqueryasnil 1253 | 1254 | argumenttrimmode 1255 | 0 1256 | argumenttype 1257 | 1 1258 | escaping 1259 | 102 1260 | queuedelaycustom 1261 | 3 1262 | queuedelayimmediatelyinitially 1263 | 1264 | queuedelaymode 1265 | 0 1266 | queuemode 1267 | 2 1268 | runningsubtext 1269 | type to front side 1270 | script 1271 | ./node_modules/.bin/run-node ./src/wf/fields.js "$1" 1272 | scriptargtype 1273 | 1 1274 | scriptfile 1275 | index.js 1276 | subtext 1277 | Anki generator 1278 | title 1279 | FIELDS 1280 | type 1281 | 0 1282 | withspace 1283 | 1284 | 1285 | type 1286 | alfred.workflow.input.scriptfilter 1287 | uid 1288 | 46444138-C3AC-4805-A574-C1F1B70D7EDB 1289 | version 1290 | 3 1291 | 1292 | 1293 | config 1294 | 1295 | triggerid 1296 | anki 1297 | 1298 | type 1299 | alfred.workflow.trigger.external 1300 | uid 1301 | 9C59315A-EBF3-4624-A015-3CBD7CD88BE2 1302 | version 1303 | 1 1304 | 1305 | 1306 | config 1307 | 1308 | concurrently 1309 | 1310 | escaping 1311 | 102 1312 | script 1313 | ./node_modules/.bin/run-node src/index.js "$1" 1314 | scriptargtype 1315 | 1 1316 | scriptfile 1317 | 1318 | type 1319 | 0 1320 | 1321 | type 1322 | alfred.workflow.action.script 1323 | uid 1324 | B7625B29-64C5-4BA2-B97B-83BE2F4B6192 1325 | version 1326 | 2 1327 | 1328 | 1329 | config 1330 | 1331 | inputstring 1332 | {var:action} 1333 | matchcasesensitive 1334 | 1335 | matchmode 1336 | 0 1337 | matchstring 1338 | make-new-card 1339 | 1340 | type 1341 | alfred.workflow.utility.filter 1342 | uid 1343 | 5CC8BE37-69CC-43B2-90D0-C1D3B03FCB5F 1344 | version 1345 | 1 1346 | 1347 | 1348 | config 1349 | 1350 | json 1351 | { 1352 | "alfredworkflow" : { 1353 | "arg" : "{var:currentText}" 1354 | } 1355 | } 1356 | 1357 | type 1358 | alfred.workflow.utility.json 1359 | uid 1360 | 2D76F2C6-18C2-4E97-9849-7CFF4801C9EE 1361 | version 1362 | 1 1363 | 1364 | 1365 | config 1366 | 1367 | inputstring 1368 | {var:action} 1369 | matchcasesensitive 1370 | 1371 | matchmode 1372 | 0 1373 | matchstring 1374 | work 1375 | 1376 | type 1377 | alfred.workflow.utility.filter 1378 | uid 1379 | ADACAE15-F887-454C-99CD-CBEC20560AB6 1380 | version 1381 | 1 1382 | 1383 | 1384 | config 1385 | 1386 | externaltriggerid 1387 | anki 1388 | passinputasargument 1389 | 1390 | passvariables 1391 | 1392 | workflowbundleid 1393 | self 1394 | 1395 | type 1396 | alfred.workflow.output.callexternaltrigger 1397 | uid 1398 | 332408A1-AB50-438A-B81C-53977A92FC49 1399 | version 1400 | 1 1401 | 1402 | 1403 | config 1404 | 1405 | lastpathcomponent 1406 | 1407 | onlyshowifquerypopulated 1408 | 1409 | removeextension 1410 | 1411 | text 1412 | ⛔ See logs 1413 | title 1414 | Something went wrong with: {var:word} 1415 | 1416 | type 1417 | alfred.workflow.output.notification 1418 | uid 1419 | 65611F88-01F2-4A24-A10E-4192327061B4 1420 | version 1421 | 1 1422 | 1423 | 1424 | config 1425 | 1426 | inputstring 1427 | {query} 1428 | matchcasesensitive 1429 | 1430 | matchmode 1431 | 2 1432 | matchstring 1433 | \}$ 1434 | 1435 | type 1436 | alfred.workflow.utility.filter 1437 | uid 1438 | 2C87ECB2-70AF-429F-90EA-C89E93DCB148 1439 | version 1440 | 1 1441 | 1442 | 1443 | config 1444 | 1445 | inputstring 1446 | {var:action} 1447 | matchcasesensitive 1448 | 1449 | matchmode 1450 | 0 1451 | matchstring 1452 | change-theme-icons 1453 | 1454 | type 1455 | alfred.workflow.utility.filter 1456 | uid 1457 | 94AAD358-0F71-4EC8-8C0B-71C1409F85A2 1458 | version 1459 | 1 1460 | 1461 | 1462 | config 1463 | 1464 | lastpathcomponent 1465 | 1466 | onlyshowifquerypopulated 1467 | 1468 | removeextension 1469 | 1470 | text 1471 | ⛔ result is {query} 1472 | title 1473 | Something went wrong with: {var:word} 1474 | 1475 | type 1476 | alfred.workflow.output.notification 1477 | uid 1478 | 924BB709-C6AF-41F4-86D4-FE8E4D00E6B1 1479 | version 1480 | 1 1481 | 1482 | 1483 | type 1484 | alfred.workflow.utility.hidealfred 1485 | uid 1486 | A63E0832-37B4-49C3-A8CF-8D0B02D75DE6 1487 | version 1488 | 1 1489 | 1490 | 1491 | config 1492 | 1493 | inputstring 1494 | {query} 1495 | matchcasesensitive 1496 | 1497 | matchmode 1498 | 2 1499 | matchstring 1500 | null 1501 | 1502 | type 1503 | alfred.workflow.utility.filter 1504 | uid 1505 | C38EC994-75BA-4C5F-A461-441C63F573CB 1506 | version 1507 | 1 1508 | 1509 | 1510 | config 1511 | 1512 | concurrently 1513 | 1514 | escaping 1515 | 102 1516 | script 1517 | ./node_modules/.bin/run-node src/cmd/refresh.js "runref" 1518 | scriptargtype 1519 | 1 1520 | scriptfile 1521 | 1522 | type 1523 | 0 1524 | 1525 | type 1526 | alfred.workflow.action.script 1527 | uid 1528 | 73BE45C1-3769-4975-88B4-2E26E0BFCE9A 1529 | version 1530 | 2 1531 | 1532 | 1533 | config 1534 | 1535 | triggerid 1536 | refresh 1537 | 1538 | type 1539 | alfred.workflow.trigger.external 1540 | uid 1541 | FAB4AF74-7BA2-4AB4-9ACE-FE846E7EECE0 1542 | version 1543 | 1 1544 | 1545 | 1546 | config 1547 | 1548 | lastpathcomponent 1549 | 1550 | onlyshowifquerypopulated 1551 | 1552 | removeextension 1553 | 1554 | text 1555 | 1556 | title 1557 | Was updated 1558 | 1559 | type 1560 | alfred.workflow.output.notification 1561 | uid 1562 | 16E77EDF-F1CB-4F48-BB70-A5EBC420693C 1563 | version 1564 | 1 1565 | 1566 | 1567 | config 1568 | 1569 | inputstring 1570 | {var:action} 1571 | matchcasesensitive 1572 | 1573 | matchmode 1574 | 0 1575 | matchstring 1576 | refresh 1577 | 1578 | type 1579 | alfred.workflow.utility.filter 1580 | uid 1581 | EEB1004E-BFEB-4865-B8D1-21868B7BF8E7 1582 | version 1583 | 1 1584 | 1585 | 1586 | config 1587 | 1588 | paths 1589 | 1590 | /Applications/Anki.app 1591 | 1592 | toggle 1593 | 1594 | 1595 | type 1596 | alfred.workflow.action.launchfiles 1597 | uid 1598 | 752836EF-6B95-4274-9D66-D7E3F3B02588 1599 | version 1600 | 1 1601 | 1602 | 1603 | config 1604 | 1605 | inputstring 1606 | {var:run} 1607 | matchcasesensitive 1608 | 1609 | matchmode 1610 | 0 1611 | matchstring 1612 | anki 1613 | 1614 | type 1615 | alfred.workflow.utility.filter 1616 | uid 1617 | E2770E96-6CF7-4A85-A009-F3C836A40E54 1618 | version 1619 | 1 1620 | 1621 | 1622 | config 1623 | 1624 | externaltriggerid 1625 | anki 1626 | passinputasargument 1627 | 1628 | passvariables 1629 | 1630 | workflowbundleid 1631 | self 1632 | 1633 | type 1634 | alfred.workflow.output.callexternaltrigger 1635 | uid 1636 | 6A1870C0-B298-4F2B-AE7C-43BDBC5609F7 1637 | version 1638 | 1 1639 | 1640 | 1641 | config 1642 | 1643 | lastpathcomponent 1644 | 1645 | onlyshowifquerypopulated 1646 | 1647 | removeextension 1648 | 1649 | text 1650 | {var:config_value} 1651 | title 1652 | Delete deck 1653 | 1654 | type 1655 | alfred.workflow.output.notification 1656 | uid 1657 | 72B24521-F77E-4B2A-9CB7-C96E9A0BD14D 1658 | version 1659 | 1 1660 | 1661 | 1662 | config 1663 | 1664 | concurrently 1665 | 1666 | escaping 1667 | 102 1668 | script 1669 | ./node_modules/.bin/run-node src/anki/anki-delete-deck.js 1670 | scriptargtype 1671 | 1 1672 | scriptfile 1673 | 1674 | type 1675 | 0 1676 | 1677 | type 1678 | alfred.workflow.action.script 1679 | uid 1680 | 5871888C-AD47-4C28-91F0-F3B2B4AEAE5D 1681 | version 1682 | 2 1683 | 1684 | 1685 | config 1686 | 1687 | inputstring 1688 | {var:action} 1689 | matchcasesensitive 1690 | 1691 | matchmode 1692 | 0 1693 | matchstring 1694 | del 1695 | 1696 | type 1697 | alfred.workflow.utility.filter 1698 | uid 1699 | 0171E88F-3E0C-4C78-9843-1CD82699A511 1700 | version 1701 | 1 1702 | 1703 | 1704 | config 1705 | 1706 | lastpathcomponent 1707 | 1708 | onlyshowifquerypopulated 1709 | 1710 | removeextension 1711 | 1712 | text 1713 | {var:config_value} 1714 | title 1715 | Current is 1716 | 1717 | type 1718 | alfred.workflow.output.notification 1719 | uid 1720 | CFC28381-4945-4F56-B327-968C1DA67A3B 1721 | version 1722 | 1 1723 | 1724 | 1725 | config 1726 | 1727 | concurrently 1728 | 1729 | escaping 1730 | 102 1731 | script 1732 | ./node_modules/.bin/run-node update-config.js 1733 | scriptargtype 1734 | 1 1735 | scriptfile 1736 | 1737 | type 1738 | 0 1739 | 1740 | type 1741 | alfred.workflow.action.script 1742 | uid 1743 | D00E50E5-A246-4577-9EA4-1D8272EBE61A 1744 | version 1745 | 2 1746 | 1747 | 1748 | config 1749 | 1750 | externaltriggerid 1751 | refresh 1752 | passinputasargument 1753 | 1754 | passvariables 1755 | 1756 | workflowbundleid 1757 | self 1758 | 1759 | type 1760 | alfred.workflow.output.callexternaltrigger 1761 | uid 1762 | 4B7B78BA-8332-4539-8C65-4673A7A2E94E 1763 | version 1764 | 1 1765 | 1766 | 1767 | config 1768 | 1769 | inputstring 1770 | {var:config_value} 1771 | matchcasesensitive 1772 | 1773 | matchmode 1774 | 2 1775 | matchstring 1776 | [a-zA-Z] 1777 | 1778 | type 1779 | alfred.workflow.utility.filter 1780 | uid 1781 | 3533497B-3633-4BB9-94DF-FB47E1DB26EA 1782 | version 1783 | 1 1784 | 1785 | 1786 | config 1787 | 1788 | inputstring 1789 | {var:action} 1790 | matchcasesensitive 1791 | 1792 | matchmode 1793 | 0 1794 | matchstring 1795 | set 1796 | 1797 | type 1798 | alfred.workflow.utility.filter 1799 | uid 1800 | 38CC2093-2FE4-47ED-9199-5EBB31ED6F14 1801 | version 1802 | 1 1803 | 1804 | 1805 | config 1806 | 1807 | concurrently 1808 | 1809 | escaping 1810 | 103 1811 | script 1812 | profileName=$1 1813 | tmp=$RANDOM 1814 | path=/Users/bikenik/Library/Application\ Support/Anki2/"$profileName"/collection.media/$tmp.png 1815 | /usr/local/bin/pngpaste "$path" 1816 | printf $tmp 1817 | scriptargtype 1818 | 1 1819 | scriptfile 1820 | 1821 | type 1822 | 0 1823 | 1824 | type 1825 | alfred.workflow.action.script 1826 | uid 1827 | FD14A9BC-87AB-4BCB-9D99-536259E7C614 1828 | version 1829 | 2 1830 | 1831 | 1832 | config 1833 | 1834 | focusedappvariable 1835 | 1836 | focusedappvariablename 1837 | 1838 | keyword 1839 | img 1840 | 1841 | type 1842 | alfred.workflow.trigger.snippet 1843 | uid 1844 | 5C10A737-34D1-46EA-958E-C3B53C8B7E48 1845 | version 1846 | 1 1847 | 1848 | 1849 | config 1850 | 1851 | autopaste 1852 | 1853 | clipboardtext 1854 | ![img]({query}.png) 1855 | transient 1856 | 1857 | 1858 | type 1859 | alfred.workflow.output.clipboard 1860 | uid 1861 | B8286172-11CE-47C6-9815-BC7492B2A31B 1862 | version 1863 | 3 1864 | 1865 | 1866 | config 1867 | 1868 | externaltriggerid 1869 | anki 1870 | passinputasargument 1871 | 1872 | passvariables 1873 | 1874 | workflowbundleid 1875 | self 1876 | 1877 | type 1878 | alfred.workflow.output.callexternaltrigger 1879 | uid 1880 | D4169A9B-5FF4-40F2-8528-AAA440E3CD78 1881 | version 1882 | 1 1883 | 1884 | 1885 | config 1886 | 1887 | json 1888 | { 1889 | "alfredworkflow" : { 1890 | "arg" : "{var:currentText}", 1891 | "variables" : { 1892 | } 1893 | } 1894 | } 1895 | 1896 | type 1897 | alfred.workflow.utility.json 1898 | uid 1899 | 4D444E9D-FDA8-4546-9527-75E091D79A30 1900 | version 1901 | 1 1902 | 1903 | 1904 | config 1905 | 1906 | inputstring 1907 | {var:focusedapp} 1908 | matchcasesensitive 1909 | 1910 | matchmode 1911 | 0 1912 | matchstring 1913 | com.runningwithcrayons.Alfred 1914 | 1915 | type 1916 | alfred.workflow.utility.filter 1917 | uid 1918 | 2DD4369C-2758-4547-9744-48EF75136302 1919 | version 1920 | 1 1921 | 1922 | 1923 | type 1924 | alfred.workflow.utility.junction 1925 | uid 1926 | A64BC663-F93F-4363-8F54-5A92B480F3FD 1927 | version 1928 | 1 1929 | 1930 | 1931 | config 1932 | 1933 | argument 1934 | 1935 | variables 1936 | 1937 | mode 1938 | getProfileName 1939 | 1940 | 1941 | type 1942 | alfred.workflow.utility.argument 1943 | uid 1944 | 95BE8DC6-2A86-4AEC-BCC4-4E9D459D4076 1945 | version 1946 | 1 1947 | 1948 | 1949 | config 1950 | 1951 | inputstring 1952 | {var:mode} 1953 | matchcasesensitive 1954 | 1955 | matchmode 1956 | 0 1957 | matchstring 1958 | getProfileName 1959 | 1960 | type 1961 | alfred.workflow.utility.filter 1962 | uid 1963 | 5B3CCCFE-2E4F-45D4-ADFF-7D36030CB26D 1964 | version 1965 | 1 1966 | 1967 | 1968 | config 1969 | 1970 | focusedappvariable 1971 | 1972 | focusedappvariablename 1973 | 1974 | keyword 1975 | cz 1976 | 1977 | type 1978 | alfred.workflow.trigger.snippet 1979 | uid 1980 | 63BFDB58-03D2-44A3-9C5B-F3C9BD0ED4A2 1981 | version 1982 | 1 1983 | 1984 | 1985 | config 1986 | 1987 | autopaste 1988 | 1989 | clipboardtext 1990 | {{c1::{cursor}}} 1991 | transient 1992 | 1993 | 1994 | type 1995 | alfred.workflow.output.clipboard 1996 | uid 1997 | D6A58C8E-76B1-4B21-A512-27424CF8E50B 1998 | version 1999 | 3 2000 | 2001 | 2002 | readme 2003 | 2004 | uidata 2005 | 2006 | 0157B150-6312-4618-9078-CB436756CFEE 2007 | 2008 | colorindex 2009 | 3 2010 | xpos 2011 | 1250 2012 | ypos 2013 | 260 2014 | 2015 | 0171E88F-3E0C-4C78-9843-1CD82699A511 2016 | 2017 | colorindex 2018 | 3 2019 | note 2020 | var:action == "del" 2021 | xpos 2022 | 470 2023 | ypos 2024 | 1250 2025 | 2026 | 0E9A7DA0-E3E9-4F4F-96BE-032F1CB4FE95 2027 | 2028 | colorindex 2029 | 2 2030 | xpos 2031 | 1860 2032 | ypos 2033 | 210 2034 | 2035 | 16E77EDF-F1CB-4F48-BB70-A5EBC420693C 2036 | 2037 | xpos 2038 | 650 2039 | ypos 2040 | 970 2041 | 2042 | 191E831E-71B9-4553-9340-6D248884CAEC 2043 | 2044 | colorindex 2045 | 5 2046 | xpos 2047 | 1730 2048 | ypos 2049 | 340 2050 | 2051 | 2C87ECB2-70AF-429F-90EA-C89E93DCB148 2052 | 2053 | colorindex 2054 | 1 2055 | xpos 2056 | 1580 2057 | ypos 2058 | 670 2059 | 2060 | 2D76F2C6-18C2-4E97-9849-7CFF4801C9EE 2061 | 2062 | colorindex 2063 | 3 2064 | xpos 2065 | 580 2066 | ypos 2067 | 590 2068 | 2069 | 2DD4369C-2758-4547-9744-48EF75136302 2070 | 2071 | colorindex 2072 | 3 2073 | xpos 2074 | 330 2075 | ypos 2076 | 1560 2077 | 2078 | 332408A1-AB50-438A-B81C-53977A92FC49 2079 | 2080 | colorindex 2081 | 9 2082 | xpos 2083 | 1095 2084 | ypos 2085 | 600 2086 | 2087 | 3533497B-3633-4BB9-94DF-FB47E1DB26EA 2088 | 2089 | xpos 2090 | 790 2091 | ypos 2092 | 1400 2093 | 2094 | 38CC2093-2FE4-47ED-9199-5EBB31ED6F14 2095 | 2096 | colorindex 2097 | 3 2098 | note 2099 | var:action == "set" 2100 | xpos 2101 | 470 2102 | ypos 2103 | 1400 2104 | 2105 | 41AD6E48-2472-43AD-B5DD-2AA26C5941EE 2106 | 2107 | colorindex 2108 | 3 2109 | xpos 2110 | 480 2111 | ypos 2112 | 260 2113 | 2114 | 46444138-C3AC-4805-A574-C1F1B70D7EDB 2115 | 2116 | xpos 2117 | 650 2118 | ypos 2119 | 560 2120 | 2121 | 47764D4F-265E-4871-90AB-0D174DD3159A 2122 | 2123 | xpos 2124 | 50 2125 | ypos 2126 | 400 2127 | 2128 | 487DCA31-FA43-4421-B268-93183B4FC353 2129 | 2130 | xpos 2131 | 260 2132 | ypos 2133 | 560 2134 | 2135 | 4B7B78BA-8332-4539-8C65-4673A7A2E94E 2136 | 2137 | colorindex 2138 | 2 2139 | xpos 2140 | 1040 2141 | ypos 2142 | 1370 2143 | 2144 | 4CA1F45A-A969-40E0-89C9-7AD7BE097FE7 2145 | 2146 | colorindex 2147 | 3 2148 | xpos 2149 | 580 2150 | ypos 2151 | 430 2152 | 2153 | 4D444E9D-FDA8-4546-9527-75E091D79A30 2154 | 2155 | colorindex 2156 | 3 2157 | xpos 2158 | 570 2159 | ypos 2160 | 1560 2161 | 2162 | 5871888C-AD47-4C28-91F0-F3B2B4AEAE5D 2163 | 2164 | note 2165 | Delete deck 2166 | xpos 2167 | 650 2168 | ypos 2169 | 1220 2170 | 2171 | 5B3CCCFE-2E4F-45D4-ADFF-7D36030CB26D 2172 | 2173 | xpos 2174 | 860 2175 | ypos 2176 | 1560 2177 | 2178 | 5C10A737-34D1-46EA-958E-C3B53C8B7E48 2179 | 2180 | xpos 2181 | 170 2182 | ypos 2183 | 1530 2184 | 2185 | 5CC8BE37-69CC-43B2-90D0-C1D3B03FCB5F 2186 | 2187 | xpos 2188 | 1250 2189 | ypos 2190 | 590 2191 | 2192 | 63BFDB58-03D2-44A3-9C5B-F3C9BD0ED4A2 2193 | 2194 | xpos 2195 | 170 2196 | ypos 2197 | 1680 2198 | 2199 | 65611F88-01F2-4A24-A10E-4192327061B4 2200 | 2201 | colorindex 2202 | 1 2203 | xpos 2204 | 1730 2205 | ypos 2206 | 640 2207 | 2208 | 6A1870C0-B298-4F2B-AE7C-43BDBC5609F7 2209 | 2210 | colorindex 2211 | 9 2212 | xpos 2213 | 1040 2214 | ypos 2215 | 1220 2216 | 2217 | 72B24521-F77E-4B2A-9CB7-C96E9A0BD14D 2218 | 2219 | xpos 2220 | 860 2221 | ypos 2222 | 1220 2223 | 2224 | 73BE45C1-3769-4975-88B4-2E26E0BFCE9A 2225 | 2226 | note 2227 | ... 2228 | ANKI-REFRESH 2229 | xpos 2230 | 650 2231 | ypos 2232 | 820 2233 | 2234 | 752836EF-6B95-4274-9D66-D7E3F3B02588 2235 | 2236 | xpos 2237 | 650 2238 | ypos 2239 | 1090 2240 | 2241 | 75FCBB30-CC79-454C-A038-B63F930D9BC8 2242 | 2243 | xpos 2244 | 650 2245 | ypos 2246 | 400 2247 | 2248 | 7602FF28-6A79-4BF2-8643-FBEF6A22EFB4 2249 | 2250 | colorindex 2251 | 2 2252 | xpos 2253 | 650 2254 | ypos 2255 | 230 2256 | 2257 | 8786E11F-9169-4F88-863F-15A30263717C 2258 | 2259 | colorindex 2260 | 1 2261 | xpos 2262 | 1650 2263 | ypos 2264 | 510 2265 | 2266 | 924BB709-C6AF-41F4-86D4-FE8E4D00E6B1 2267 | 2268 | colorindex 2269 | 1 2270 | xpos 2271 | 1730 2272 | ypos 2273 | 780 2274 | 2275 | 94AAD358-0F71-4EC8-8C0B-71C1409F85A2 2276 | 2277 | xpos 2278 | 700 2279 | ypos 2280 | 700 2281 | 2282 | 95BE8DC6-2A86-4AEC-BCC4-4E9D459D4076 2283 | 2284 | xpos 2285 | 410 2286 | ypos 2287 | 1560 2288 | 2289 | 9C59315A-EBF3-4624-A015-3CBD7CD88BE2 2290 | 2291 | colorindex 2292 | 9 2293 | xpos 2294 | 50 2295 | ypos 2296 | 560 2297 | 2298 | 9C7FC23C-28CE-4D31-B999-DA0989811A89 2299 | 2300 | note 2301 | ... 2302 | RESET FIELDS 2303 | xpos 2304 | 1080 2305 | ypos 2306 | 230 2307 | 2308 | 9F0DF1B9-583D-4458-850A-82C5852CB2AD 2309 | 2310 | xpos 2311 | 1080 2312 | ypos 2313 | 400 2314 | 2315 | A63E0832-37B4-49C3-A8CF-8D0B02D75DE6 2316 | 2317 | xpos 2318 | 1060 2319 | ypos 2320 | 790 2321 | 2322 | A64BC663-F93F-4363-8F54-5A92B480F3FD 2323 | 2324 | colorindex 2325 | 3 2326 | xpos 2327 | 480 2328 | ypos 2329 | 1560 2330 | 2331 | ADACAE15-F887-454C-99CD-CBEC20560AB6 2332 | 2333 | colorindex 2334 | 3 2335 | xpos 2336 | 480 2337 | ypos 2338 | 590 2339 | 2340 | B7625B29-64C5-4BA2-B97B-83BE2F4B6192 2341 | 2342 | note 2343 | ... 2344 | MAKE CARD 2345 | xpos 2346 | 1360 2347 | ypos 2348 | 560 2349 | 2350 | B8286172-11CE-47C6-9815-BC7492B2A31B 2351 | 2352 | xpos 2353 | 1280 2354 | ypos 2355 | 1530 2356 | 2357 | C38EC994-75BA-4C5F-A461-441C63F573CB 2358 | 2359 | colorindex 2360 | 1 2361 | xpos 2362 | 1580 2363 | ypos 2364 | 810 2365 | 2366 | C3AC3CFC-E4C8-4B87-A6CD-9BD13BF9E3F7 2367 | 2368 | colorindex 2369 | 5 2370 | xpos 2371 | 1650 2372 | ypos 2373 | 370 2374 | 2375 | CF402DBC-4552-4802-9382-6A2D51DEC2A5 2376 | 2377 | colorindex 2378 | 1 2379 | xpos 2380 | 480 2381 | ypos 2382 | 130 2383 | 2384 | CFC28381-4945-4F56-B327-968C1DA67A3B 2385 | 2386 | xpos 2387 | 860 2388 | ypos 2389 | 1370 2390 | 2391 | D00E50E5-A246-4577-9EA4-1D8272EBE61A 2392 | 2393 | note 2394 | Update config 2395 | xpos 2396 | 650 2397 | ypos 2398 | 1370 2399 | 2400 | D2305FF6-F132-45FF-AA14-A5A706CB746E 2401 | 2402 | colorindex 2403 | 2 2404 | xpos 2405 | 1360 2406 | ypos 2407 | 230 2408 | 2409 | D4169A9B-5FF4-40F2-8528-AAA440E3CD78 2410 | 2411 | colorindex 2412 | 2 2413 | xpos 2414 | 650 2415 | ypos 2416 | 1530 2417 | 2418 | D4618291-BECE-42B6-8C73-378A5A52D4F4 2419 | 2420 | xpos 2421 | 1080 2422 | ypos 2423 | 100 2424 | 2425 | D6A58C8E-76B1-4B21-A512-27424CF8E50B 2426 | 2427 | xpos 2428 | 390 2429 | ypos 2430 | 1680 2431 | 2432 | D911C0B6-8115-460C-89BA-9C9A7C0CB9D6 2433 | 2434 | colorindex 2435 | 3 2436 | note 2437 | ... 2438 | To RESET 2439 | xpos 2440 | 1770 2441 | ypos 2442 | 240 2443 | 2444 | E2770E96-6CF7-4A85-A009-F3C836A40E54 2445 | 2446 | colorindex 2447 | 3 2448 | xpos 2449 | 470 2450 | ypos 2451 | 1120 2452 | 2453 | EEB1004E-BFEB-4865-B8D1-21868B7BF8E7 2454 | 2455 | colorindex 2456 | 3 2457 | note 2458 | var:action == "refresh" 2459 | xpos 2460 | 470 2461 | ypos 2462 | 1000 2463 | 2464 | F21B5144-DC68-4065-8B4B-A399D4AD79E8 2465 | 2466 | colorindex 2467 | 5 2468 | xpos 2469 | 1580 2470 | ypos 2471 | 370 2472 | 2473 | F9B7F4E7-A5EA-40AF-8C7A-90FD40FFBF9F 2474 | 2475 | colorindex 2476 | 1 2477 | xpos 2478 | 1580 2479 | ypos 2480 | 510 2481 | 2482 | FAB4AF74-7BA2-4AB4-9ACE-FE846E7EECE0 2483 | 2484 | colorindex 2485 | 2 2486 | xpos 2487 | 440 2488 | ypos 2489 | 820 2490 | 2491 | FB1372F3-E68F-43E3-B6CA-1C112CB95F12 2492 | 2493 | colorindex 2494 | 1 2495 | note 2496 | Errors from anki-add-card.js 2497 | xpos 2498 | 1730 2499 | ypos 2500 | 480 2501 | 2502 | FD14A9BC-87AB-4BCB-9D99-536259E7C614 2503 | 2504 | xpos 2505 | 1040 2506 | ypos 2507 | 1530 2508 | 2509 | 2510 | version 2511 | 1.3.1 2512 | webaddress 2513 | https://github.com/bikenik/alfred-anki#readme 2514 | 2515 | 2516 | --------------------------------------------------------------------------------