├── .editorconfig ├── .gitattributes ├── .github ├── SECURITY.md └── workflows │ ├── bad-link-reporter.yml │ ├── markdown-linter.yml │ └── yaml-linter.yml ├── .gitignore ├── .jsonlintrc.json ├── .markdownlint.json ├── .yamllint.yml ├── CITATION.cff ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SUPPORT.md ├── codemeta.json ├── devonthink-config-scripts ├── README.md └── print-hidden-preferences │ ├── README.md │ └── print-devonthink-preferences ├── devonthink-smart-rule-scripts ├── Makefile ├── auto-convert-web-page-to-PDF │ ├── .graphics │ │ └── auto-convert-smart-rule.png │ ├── Auto convert web page to PDF.applescript │ ├── README.md │ └── bookmarklet.js ├── copy-metadata-to-annotation-docs │ ├── Copy metadata to annotation docs.applescript │ └── Makefile ├── create-image-file │ ├── Create image file for transclusion.applescript │ ├── Makefile │ └── Test image file exists.applescript ├── pause │ ├── Makefile │ ├── Pause 10 seconds.applescript │ └── Pause 5 seconds.applescript ├── remove-pdf-keywords │ ├── .graphics │ │ ├── smart-rule-screenshot.png │ │ └── zotfile.png │ ├── Makefile │ ├── README.md │ └── Remove PDF keywords.applescript ├── replace-custom-placeholders │ ├── Makefile │ └── Replace custom placeholders in document.applescript ├── send-url-to-internet-archive │ ├── Makefile │ └── Send URL to Internet Archive.applescript ├── set-annotation-url │ ├── Makefile │ └── Set annotation document URL.applescript ├── unset-common-zotero-field-values │ ├── Makefile │ └── Unset common Zotero field values.applescript ├── write-uri-into-comments │ ├── Makefile │ ├── README.md │ ├── Write URI into Finder comment using Urial.applescript │ └── smart-rule-screenshot.png ├── zoinks-scripts │ ├── Add Zotero abstract.applescript │ ├── Add Zotero citekey.applescript │ ├── Makefile │ ├── README.md │ └── t.py ├── zotero-scripts │ ├── Makefile │ ├── Set reference field using BBT.applescript │ ├── Start Zotero + BBT if needed.applescript │ └── tests │ │ ├── test-framework-failure.applescript │ │ ├── test-ping.applescript │ │ ├── test-without-framework.applescript │ │ └── testapi.applescript └── zowie-scripts │ ├── Makefile │ ├── README.md │ └── Run Zowie on newly indexed PDF.applescript ├── devonthink-templates ├── Annotations.noindex │ └── Reading notes.md ├── README.md ├── Templates.noindex │ ├── .gitignore │ ├── Brief note.ooutline │ ├── Card.md │ ├── Clipboard to markdown.md │ ├── Code.md │ ├── Diary.ooutline │ ├── Empty markdown.md │ ├── Glossary.md │ ├── Goal plan.ooutline │ ├── Markdown.md │ ├── Meeting.ooutline │ ├── Notes.ooutline │ ├── Quote.md │ ├── Records markdown.md │ ├── Spreadsheet.numbers │ └── Term definition.md └── makelinks.sh ├── devonthink-toolbar-scripts ├── .gitignore ├── Makefile ├── copy-location-paths-of-selected-items │ ├── Copy location paths of selected items.applescript │ ├── Makefile │ ├── README.md │ └── icon.svg ├── copy-markdown-links-to-selected-items │ ├── Copy Markdown links to selected items.applescript │ ├── Makefile │ ├── README.md │ └── icon.svg ├── copy-paths-to-files-on-disk │ ├── Copy disk paths of selected files.applescript │ └── Makefile ├── create-document-from-template │ ├── .graphics │ │ └── km-shortcut-screenshot.png │ ├── Create document from template.applescript │ ├── Makefile │ └── README.md ├── open-in-new-window │ ├── Makefile │ ├── Open in new window.applescript │ ├── README.md │ └── icon.svg ├── set-icon-to-parent-group-icon │ ├── .graphics │ │ ├── folder-icons-dark.png │ │ └── folder-icons-light.png │ ├── Makefile │ ├── README.md │ ├── Set icon to parent group icon.applescript │ └── icon.svg ├── switch-workspace │ ├── Makefile │ ├── README.md │ └── Switch workspace.applescript └── unset-field-values │ ├── Makefile │ ├── README.md │ ├── Unset Aliases.applescript │ ├── Unset Finder comment.applescript │ ├── Unset URL.applescript │ └── Unset common Zotero field values.applescript ├── finder-folder-actions ├── Makefile └── import-to-devonthink │ ├── Import to DEVONthink with selector.applescript │ ├── Makefile │ └── README.md ├── global-scripts ├── Makefile ├── README.md ├── create-markdown-note-for-quote │ ├── Create note in DEVONthink for code.applescript │ ├── Create note in DEVONthink for quote.applescript │ └── Makefile └── open-url-in-devonthink │ ├── Makefile │ ├── Open current browser URL in DEVONthink.applescript │ └── README.md └── obsolete-no-longer-used ├── README.md ├── insert-tags-into-markdown ├── README.md └── insert-tags-into-markdown.applescript └── open-annotation-file ├── README.md ├── annotations-drop-down.png └── open-annotation-file.applescript /.editorconfig: -------------------------------------------------------------------------------- 1 | # Summary: EditorConfig file for this project. -*- conf -*- 2 | # 3 | # For more information, see https://EditorConfig.org 4 | # 5 | # Copyright 2024 Michael Hucka. 6 | # License: MIT License – see file "LICENSE" in the project website. 7 | # Website: https://github.com/mhucka/urial 8 | 9 | root = true 10 | 11 | [*] 12 | charset = utf-8 13 | end_of_line = lf 14 | indent_size = 4 15 | indent_style = space 16 | insert_final_newline = true 17 | max_line_length = 90 18 | tab_width = 4 19 | trim_trailing_whitespace = true 20 | 21 | [*.cfg] 22 | indent_size = 2 23 | 24 | [*.json] 25 | indent_size = 2 26 | 27 | [*.{yml, yaml}] 28 | indent_size = 2 29 | 30 | # Shell scripts on Windows. 31 | [*.{cmd, bat}] 32 | end_of_line = crlf 33 | 34 | [Makefile, makefile] 35 | indent_size = 4 36 | indent_style = tab 37 | tab_width = 8 38 | 39 | [.applescript] 40 | indent_size = 4 41 | indent_style = tab 42 | tab_width = 4 43 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Summary: repository-specific file attributes assignments for git. 2 | # 3 | # Copyright 2024 Michael Hucka. 4 | # License: MIT License – see file "LICENSE" in the project website. 5 | # Website: https://github.com/mhucka/devonthink-hacks 6 | 7 | # Set default interpretation of line endings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | * text=auto 10 | 11 | # Interpretation of common text files ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 12 | 13 | *.bat text eol=crlf 14 | *.cff text 15 | *.cfg text 16 | *.css text diff=css 17 | *.env text 18 | *.html text diff=html 19 | *.ini text 20 | *.ipynb text eol=lf 21 | *.js text 22 | *.json text 23 | *.md text diff=markdown 24 | *.py text diff=python 25 | *.rst text 26 | *.sh text 27 | *.sql text 28 | *.svg text 29 | *.toml text 30 | *.tex text diff=tex 31 | *.txt text 32 | *.yaml text merge=yaml 33 | *.yml text merge=yaml 34 | 35 | # RC files like .babelrc, .eslintrc, etc. 36 | *.*rc text 37 | 38 | # This avoids compatibility issues between Windows and Mac. 39 | *.csv text eol=crlf 40 | 41 | LICENSE text 42 | Makefile text 43 | 44 | *.*ignore text 45 | *.gitattributes text 46 | .gitconfig text 47 | 48 | # Interpretation of common binary files ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 49 | 50 | *.bz binary 51 | *.DOC binary 52 | *.doc binary 53 | *.DOCX binary 54 | *.docx binary 55 | *.DOT binary 56 | *.dot binary 57 | *.gz binary 58 | *.jpeg binary 59 | *.jpg binary 60 | *.PDF binary 61 | *.pdf binary 62 | *.RTF binary 63 | *.rtf binary 64 | *.tar binary 65 | *.tgz binary 66 | *.tif binary 67 | *.tiff binary 68 | *.xls binary 69 | *.zip binary 70 | 71 | # Interpretation of Mac-specific files ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 72 | 73 | *.shortcut binary 74 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting security issues 2 | 3 | Please report security issues using the [issue tracker](https://github.com/mhucka/template/issues) for this repository. 4 | -------------------------------------------------------------------------------- /.github/workflows/bad-link-reporter.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions workflow for Baler (BAd Link reportER) version 2.0.2. 2 | # This is available as the file "sample-workflow.yml" from the source 3 | # code repository for Baler: https://github.com/caltechlibrary/baler 4 | 5 | name: Bad Link Reporter 6 | 7 | # Configure this section ───────────────────────────────────────────── 8 | 9 | env: 10 | # Files to check. (Put patterns on separate lines, no leading dash.) 11 | files: | 12 | **/*.md 13 | 14 | # Label assigned to issues created by this workflow: 15 | labels: bug 16 | 17 | # Number of previous issues to check for duplicate reports. 18 | lookback: 10 19 | 20 | # Time (sec) to wait on an unresponsive URL before trying once more. 21 | timeout: 15 22 | 23 | # Optional file containing a list of URLs to ignore, one per line: 24 | ignore: .github/workflows/ignored-urls.txt 25 | 26 | on: 27 | schedule: # Cron syntax is: "min hr day-of-month month day-of-week" 28 | - cron: 00 04 * * 1 29 | push: 30 | paths: ['**.md'] 31 | workflow_dispatch: 32 | 33 | # The rest of this file should be left as-is ───────────────────────── 34 | 35 | run-name: Test links in Markdown files 36 | jobs: 37 | Baler: 38 | name: Link checker and reporter 39 | runs-on: ubuntu-latest 40 | permissions: 41 | issues: write 42 | steps: 43 | - uses: caltechlibrary/baler@v2 44 | with: 45 | files: ${{github.event.inputs.files || env.files}} 46 | labels: ${{github.event.inputs.labels || env.labels}} 47 | ignore: ${{github.event.inputs.ignore || env.ignore}} 48 | timeout: ${{github.event.inputs.timeout || env.timeout}} 49 | lookback: ${{github.event.inputs.lookback || env.lookback}} 50 | -------------------------------------------------------------------------------- /.github/workflows/markdown-linter.yml: -------------------------------------------------------------------------------- 1 | # Summary: GitHub Actions workflow to run a Markdown linter on .md files. 2 | # 3 | # Copyright 2024 California Institute of Technology. 4 | # License: Modified BSD 3-clause – see file "LICENSE" in the project website. 5 | # Website: https://github.com/caltechlibrary/devonthink-hacks 6 | 7 | name: Markdown file linter 8 | run-name: Lint Markdown files after ${{github.event_name}} by ${{github.actor}} 9 | 10 | on: 11 | push: 12 | branches: 13 | - main 14 | paths: 15 | - '*.md' 16 | pull_request: 17 | branches: 18 | - main 19 | paths: 20 | - '*.md' 21 | workflow_dispatch: 22 | paths: 23 | - '*.md' 24 | 25 | jobs: 26 | lint: 27 | name: Run Markdown linter 28 | runs-on: ubuntu-latest 29 | steps: 30 | - name: Check out copy of git repository. 31 | uses: actions/checkout@v4 32 | 33 | - name: Run Markdownlint on .md files. 34 | uses: DavidAnson/markdownlint-cli2-action@v15 35 | with: 36 | config: .markdownlint.json 37 | globs: | 38 | *.md 39 | -------------------------------------------------------------------------------- /.github/workflows/yaml-linter.yml: -------------------------------------------------------------------------------- 1 | # Summary: GitHub Actions workflow to run a YAML linter on .yml files. 2 | # 3 | # Copyright 2024 California Institute of Technology. 4 | # License: Modified BSD 3-clause – see file "LICENSE" in the project website. 5 | # Website: https://github.com/caltechlibrary/devonthink-hacks 6 | 7 | name: YAML file linter 8 | 9 | on: 10 | pull_request: 11 | types: [opened, synchronize] 12 | paths: 13 | - '**.yml' 14 | - '**.yaml' 15 | push: 16 | branches: 17 | - main 18 | paths: 19 | - '**.yml' 20 | - '**.yaml' 21 | 22 | run-name: Run linter on YAML files 23 | jobs: 24 | Yamllint: 25 | name: GitHub YAMLlint 26 | runs-on: ubuntu-latest 27 | steps: 28 | - name: Check out copy of git repository 29 | uses: actions/checkout@v4 30 | 31 | - name: Run YAMLlint 32 | uses: ibiqlik/action-yamllint@v3.1.1 33 | with: 34 | config_file: .yamllint.yml 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Summary: rules for files and subdirectories to be ignored by git. 2 | # 3 | # Copyright 2024 Michael Hucka. 4 | # License: MIT License – see file "LICENSE" in the project website. 5 | # Website: https://github.com/mhucka/devonthink-hacks 6 | # 7 | # Note: the recommended approach for using this file is to add only project- 8 | # specific rules to the .gitignore of a repository. Each user should put rules 9 | # that apply to their tools and ways of working into a global git ignore file 10 | # using (e.g.) "git config --global core.excludesfile ~/.gitignore_global". 11 | # The rationale for this is that a number of file types, such as Emacs 12 | # checkpoint and backup files, are things that are not specific to a given 13 | # project; rather, Emacs creates them everywhere, in all projects, because 14 | # they're a byproduct of how Emacs works. Thus, rules to ignore them belong 15 | # in a user's own global .gitignore file, not in a project's .gitignore. 16 | # A useful starting point for global .gitignore file contents can be found 17 | # at https://github.com/github/gitignore/tree/main/Global (as of 2022-07-14) 18 | 19 | # AppleScript-specific things to ignore 20 | # ............................................................................. 21 | 22 | *.scpt 23 | 24 | # Python-specific things to ignore 25 | # ............................................................................. 26 | 27 | __pycache__/ 28 | *.py[cod] 29 | *$py.class 30 | *.egg-info/ 31 | .eggs/ 32 | .pytest_cache 33 | .coverage 34 | 35 | 36 | # Project-specific things to ignore: 37 | # ............................................................................. 38 | 39 | build 40 | dist 41 | write-uri-into-comments/*.scpt 42 | zowie-scripts/*.scpt 43 | set-icon-to-parent-icon/*.scpt 44 | unset-url/*.scpt 45 | unset-finder-comment/*.scpt 46 | zoinks-scripts/*.scpt 47 | create-document-from-template/*.scpt 48 | switch-workspace/*.scpt 49 | create-markdown-note-for-quote/*.scpt 50 | -------------------------------------------------------------------------------- /.jsonlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": false, 3 | "trailing-commas": false, 4 | "duplicate-keys": false, 5 | "log-files": false, 6 | "compact": true, 7 | "continue": true, 8 | "patterns": ["**/*.json"] 9 | } 10 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "blank_lines": { 3 | "maximum": 2 4 | }, 5 | "html": { 6 | "allowed_elements": [ 7 | "a", 8 | "b", 9 | "br", 10 | "code", 11 | "details", 12 | "div", 13 | "em", 14 | "i", 15 | "img", 16 | "ins", 17 | "kbd", 18 | "p", 19 | "span", 20 | "sup", 21 | "summary" 22 | ] 23 | }, 24 | "line-length": { 25 | "line_length": 10000 26 | }, 27 | "no-alt-text": true, 28 | "no-duplicate-heading": { 29 | "allow_different_nesting": true 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.yamllint.yml: -------------------------------------------------------------------------------- 1 | # Summary: configuration file for .github/workflows/yaml-linter.yml. 2 | # 3 | # Copyright 2024 California Institute of Technology. 4 | # License: Modified BSD 3-clause – see file "LICENSE" in the project website. 5 | # Website: https://github.com/caltechlibrary/baler 6 | 7 | rules: 8 | colons: 9 | max-spaces-after: -1 10 | quoted-strings: 11 | required: only-when-needed 12 | document-start: 13 | present: false 14 | document-end: 15 | present: false 16 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it using the metadata from this file." 3 | authors: 4 | - family-names: Hucka 5 | given-names: Michael 6 | email: mhucka@caltech.edu 7 | orcid: https://orcid.org/0000-0001-9105-5960 8 | title: Mike's DEVONthink hacks 9 | abstract: Scripts and programs I use with DEVONthink 10 | date-released: 2024-01-17 11 | repository-code: "https://github.com/mhucka/devonthink-hacks" 12 | license: MIT 13 | type: software 14 | keywords: 15 | - software 16 | - DEVONthink 17 | - personal information management 18 | - AppleScript 19 | - Python 20 | - macOS 21 | - automation 22 | - scripting 23 | - archiving 24 | - PDF 25 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project contributors are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project contributors have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project contributors. 34 | 35 | ## Enforcement 36 | 37 | If a contributor engages in harassing behaviour, the project organizer(s) may take any action they deem appropriate, including warning the offender or expelling them from online forums, online project resources, face-to-face meetings, or any other project-related activity or resource. 38 | 39 | If you are being harassed, notice that someone else is being harassed, or have any other concerns, please contact a member of the project team immediately. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 40 | 41 | ## Attribution 42 | 43 | Portions of this Code of Conduct were adapted from Electron's [Contributor Covenant Code of Conduct](https://github.com/electron/electron/blob/main/CODE_OF_CONDUCT.md), which itself was adapted from the [Contributor Covenant](http://contributor-covenant.org/version/1/4), version 1.4. 44 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Guidelines for contributing to this project 2 | 3 | Any constructive contributions – bug reports, pull requests (code or documentation), suggestions for improvements, and more – are welcome. 4 | 5 | ## Conduct 6 | 7 | Everyone is asked to read and respect the [code of conduct](CODE_OF_CONDUCT.md) before participating in this project. 8 | 9 | ## Coordinating work 10 | 11 | A quick way to find out what is currently in the near-term plans for this project is to look at the [GitHub issue tracker](https://github.com/mhucka/devonthink-hacks/issues), but the possibilities are not limited to what you see there – if you have ideas for new features and enhancements, please feel free to write them up as a new issue or contact the developers directly! 12 | 13 | ## Submitting contributions 14 | 15 | Please feel free to contact the author directly, or even better, jump right in and use the standard GitHub approach of forking the repo and creating a pull request. When committing code changes and submitting pull requests, please write a clear log message for your commits. 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 by Michael Hucka. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mike's DEVONthink Hacks 2 | 3 | These are scripts and programs I developed to work with [DEVONthink](https://www.devontechnologies.com/apps/devonthink), a powerful personal database and information management system. 4 | 5 | [](https://github.com/mhucka/devonthink-hacks/blob/main/LICENSE) 6 | [](https://www.devontechnologies.com/apps/devonthink) 7 | [](https://developer.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptLangGuide/introduction/ASLR_intro.html) 8 | 9 | 10 | ## Table of contents 11 | 12 | * [Introduction](#introduction) 13 | * [Installation and usage](#installation-and-usage) 14 | * [Known issues and limitations](#known-issues-and-limitations) 15 | * [Getting help](#getting-help) 16 | * [Contributing](#contributing) 17 | * [License](#license) 18 | 19 | 20 | ## Introduction 21 | 22 | In the process of using [DEVONthink](https://www.devontechnologies.com/apps/devonthink) more fully, I've been developing scripts to automate various procedures. This repository contains the results. 23 | 24 | 25 | ## Installation and usage 26 | 27 | There is no one-shot installation procedure nor a single usage procedure for all of these hacks. Please check each subdirectory separately; there is a README file that explains what needs to be done for each case. 28 | 29 | 30 | ## Known issues and limitations 31 | 32 | The solutions and code in this repository were developed over a period of years spanning multiple releases of [DEVONthink](https://www.devontechnologies.com/apps/devonthink) in my sometimes idiosyncratic macOS environment. While they work for me (or did at the time I put them here), it is possible they will not work in your environment or in the version of DEVONthink that you are using. 33 | 34 | 35 | ## Getting help 36 | 37 | If you find an issue, please submit it in [the GitHub issue tracker](https://github.com/mhucka/devonthink-hacks/issues) for this repository. 38 | 39 | 40 | ## Contributing 41 | 42 | I would be happy to receive your help and participation with enhancing the solutions and code contained here. Please visit the [guidelines for contributing](CONTRIBUTING.md) for some tips on getting started. 43 | 44 | 45 | ## License 46 | 47 | Unless otherwise noted, the software in this repository is licensed under the [MIT](https://github.com/mhucka/devonthink-hacks/blob/main/LICENSE) license. 48 | 49 | 50 | ## Do you like it? 51 | 52 | If you like this software, don't forget to give this repo a star on GitHub to show your support! 53 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | Support 2 | ======= 3 | 4 | Thank you for your interest in this project. If you are experiencing problems or have questions, the following are the preferred methods of reaching someone: 5 | 6 | 1. Report a new issue using the [issue tracker](https://github.com/mhucka/devonthink-hacks/issues). 7 | 2. Send email to the author: [mhucka@caltech.edu](mailto:mhucka@caltech.edu). 8 | 3. Send email to an individual involved in the project. People's names appear in the top-level `README.md` file in the source code repository. 9 | -------------------------------------------------------------------------------- /codemeta.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://doi.org/10.5063/schema/codemeta-2.0", 3 | "@type": "SoftwareSourceCode", 4 | "name": "DEVONthink Hacks", 5 | "identifier": "devonthink-hacks", 6 | "description": "Random hacks for working with DEVONthink", 7 | "datePublished": "2024-01-17", 8 | "dateCreated": "2023-12-11", 9 | "author": [ 10 | { 11 | "@type": "Person", 12 | "givenName": "Michael", 13 | "familyName": "Hucka", 14 | "affiliation": { 15 | "@type": "Organization", 16 | "name": "California Institute of Technology Library" 17 | }, 18 | "email": "mhucka@caltech.edu", 19 | "@id": "https://orcid.org/0000-0001-9105-5960" 20 | } 21 | ], 22 | "maintainer": [ 23 | { 24 | "@type": "Person", 25 | "givenName": "Michael", 26 | "familyName": "Hucka", 27 | "affiliation": { 28 | "@type": "Organization", 29 | "name": "California Institute of Technology Library" 30 | }, 31 | "email": "mhucka@caltech.edu", 32 | "@id": "https://orcid.org/0000-0001-9105-5960" 33 | } 34 | ], 35 | "copyrightHolder": [ 36 | { 37 | "@type": "Person", 38 | "givenName": "Michael", 39 | "familyName": "Hucka", 40 | "affiliation": { 41 | "@type": "Organization", 42 | "name": "California Institute of Technology Library" 43 | }, 44 | "email": "mhucka@caltech.edu", 45 | "@id": "https://orcid.org/0000-0001-9105-5960" 46 | } 47 | ], 48 | "copyrightYear": 2024, 49 | "license": "https://github.com/mhucka/devonthink-hacks/blob/main/LICENSE", 50 | "isAccessibleForFree": true, 51 | "codeRepository": "https://github.com/mhucka/devonthink-hacks", 52 | "readme": "https://github.com/mhucka/devonthink-hacks/blob/main/README.md", 53 | "issueTracker": "https://github.com/mhucka/devonthink-hacks/issues", 54 | "downloadUrl": "https://github.com/mhucka/devonthink-hacks/archive/main.zip", 55 | "keywords": [ 56 | "software", 57 | "command line", 58 | "macOS", 59 | "shell", 60 | "AppleScript", 61 | "Bash" 62 | ], 63 | "programmingLanguage": [ 64 | { 65 | "@type": "ComputerLanguage", 66 | "name": "AppleScript", 67 | "version": "2.8", 68 | "url": "https://developer.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptLangGuide/" 69 | } 70 | ], 71 | "developmentStatus": "active" 72 | } 73 | -------------------------------------------------------------------------------- /devonthink-config-scripts/README.md: -------------------------------------------------------------------------------- 1 | # Config scripts 2 | 3 | In this directory are things I use to configure DEVONthink or do other configuration-related activities. 4 | -------------------------------------------------------------------------------- /devonthink-config-scripts/print-hidden-preferences/README.md: -------------------------------------------------------------------------------- 1 | # Config hidden preferences 2 | 3 | This is a simple shell script I use to both print and set DEVONthink hidden preferences, for installing DEVONthink on a new machine. The script is designed to output the values in a way that makes it possible to take the output and run it as a shell script to set the values. Example: 4 | 5 | ```sh 6 | # Get the values on one machine 7 | ./print-devonthink-preferences > prefs.sh 8 | 9 | # Copy prefs.sh to another machine 10 | # Set the values on the other machine 11 | sh prefs.sh 12 | ``` 13 | -------------------------------------------------------------------------------- /devonthink-config-scripts/print-hidden-preferences/print-devonthink-preferences: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Summary: print the current values of all DEVONthink hidden preferences. 3 | # 4 | # Warning: this contains an explicit list of hidden preferences that were 5 | # documented as of version 3.9.4 of DEVONthink in the DEVONthink user manual. 6 | # The list needs to be kept up-to-date manually. 7 | # 8 | # Copyright 2024 Michael Hucka. 9 | # License: MIT License – see file "LICENSE" in the project website. 10 | # Website: https://github.com/mhucka/devonthink-hacks 11 | 12 | year=$(date +%Y) 13 | key="com.devon-technologies.think3" 14 | 15 | print_value() { 16 | value=$(defaults read $key $1 2>&1 /dev/null | egrep -v ^$year) 17 | if [[ $value =~ 'does not exist' ]] ; then 18 | echo \# $1 not set 19 | else 20 | type=$(defaults read-type $key $1 2>&1 /dev/null | egrep -v ^$year) 21 | if [[ $type =~ 'string' ]]; then 22 | echo defaults write $key $1 -string $value 23 | elif [[ $type =~ 'boolean' ]]; then 24 | if [[ $value -eq 1 ]] ; then 25 | echo defaults write $key $1 -bool TRUE 26 | else 27 | echo defaults write $key $1 -bool FALSE 28 | fi 29 | else 30 | echo defaults write $key value is $value 31 | fi 32 | fi 33 | } 34 | 35 | print_value AVSkippingInterval 36 | print_value AdditionalPlainTextExtensions 37 | print_value AdditionalXMLExtensions 38 | print_value BatesNumberDigits 39 | print_value CounterDigits 40 | print_value DisableActivityWindow 41 | print_value DisableAutomaticDeconsolidation 42 | print_value DisableAutomaticUpdatingOfIndexedItems 43 | print_value DisableBadgeLabel 44 | print_value DisableFileCoordination 45 | print_value DisableFileSystemEvents 46 | print_value DisableFinderTags 47 | print_value DisableHighlightColorMapping 48 | print_value DisablePDFValidation 49 | print_value DisablePreprocessedClipping 50 | print_value DisableRelativeDates 51 | print_value DisableTagAutocompletion 52 | print_value DisplayGroupsInPreviewPane 53 | print_value DontAutomaticallyEnableOperatorsOptionOfSearchInspector 54 | print_value EnableApplicationFiles 55 | print_value EnableAutomaticConsolidation 56 | print_value EnableEvernoteRTFDImport 57 | print_value EnableFSEventLogging 58 | print_value EnableSearchFieldAutocompletion 59 | print_value ForceEditablePDFs 60 | print_value IndexRawMarkdownSource 61 | print_value MaximumNumberOfRecentDestinations 62 | print_value MaximumNumberOfRecentSearches 63 | print_value MonospacedSidebarFont 64 | print_value MonospacedViewFont 65 | print_value PersistentSortingOfSearchResults 66 | print_value PlainTextIsMarkdown 67 | print_value RawMarkdownPasting 68 | print_value RawOPMLImport 69 | print_value RichNotesWithoutAttachments 70 | print_value ShowAdditionalInfoInPathBar 71 | print_value SyncDebugLog 72 | print_value UsePDFDocumentDates 73 | print_value WindowToolbarStyleExpanded 74 | -------------------------------------------------------------------------------- /devonthink-smart-rule-scripts/Makefile: -------------------------------------------------------------------------------- 1 | # Summary: my generic Makefile for compiling AppleScript files. 2 | # 3 | # Copyright 2024 Michael Hucka. 4 | # License: MIT License – see file "LICENSE" in the project website. 5 | # Website: https://github.com/mhucka/devonthink-hacks 6 | 7 | # Preliminary settings and tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | SHELL=/bin/bash 10 | .ONESHELL: # Run all commands in the same shell. 11 | .SHELLFLAGS += -e # Exit at the first error. 12 | 13 | thisdir := $(shell basename $(CURDIR)) 14 | 15 | # When I run M-x compile using this Makefile, the compile target works but the 16 | # install target fails. It works outside Emacs in a regular shell terminal. I 17 | # haven't figured out the reason, so for now, this test reminds me to avoid it. 18 | 19 | ifeq ($(origin INSIDE_EMACS),environment) 20 | $(error "Do not run make from inside Emacs with this Makefile.") 21 | endif 22 | 23 | 24 | # Print help if no command is given ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 25 | 26 | # The help scheme works by looking for lines that begin with "#:" above the 27 | # definitions of commands. Originally based on code posted to Stack Overflow on 28 | # 2019-11-28 by Richard Kiefer at https://stackoverflow.com/a/59087509/743730 29 | 30 | #: Print a summary of available commands. 31 | help: 32 | @echo "This is the Makefile for $(bright)$(thisdir)$(reset)." 33 | @echo "Available commands:" 34 | @echo 35 | @grep -B1 -E "^[a-zA-Z0-9_-]+\:([^=]|$$)" $(MAKEFILE_LIST) \ 36 | | grep -v -- -- \ 37 | | sed 'N;s/\n/###/' \ 38 | | sed -n 's/^#: \(.*\)###\([a-zA-Z0-9_-]*\):.*/$(color)\2$(reset):###\1/p' \ 39 | | column -t -s '###' 40 | 41 | 42 | # Compile source files ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 43 | 44 | # Getting Make to handle path names with embedded spaces is insanely hard. 45 | # The following incredibly obscure workarounds are the combination of two 46 | # methods: (1) by user "mathematical.coffee" posted on 2014-02-11 47 | # at https://stackoverflow.com/a/21694624/743730, and (2) by user Yann-Gaël 48 | # Guéhéneuc posted on 2016-02-25 at https://stackoverflow.com/a/35617603/743730 49 | 50 | # We first create a list of source files in a way that bypasses the regular 51 | # Make file pattern matching, and then replace every space character in 52 | # each pathname with a question mark character. 53 | 54 | sources = $(shell find . -iname '*.applescript' -maxdepth 1 | cut -d/ -f2- | tr ' ' '?') 55 | 56 | # Next, define a function named "q2s" to replace question marks by spaces, 57 | # using an absolutely bonkers trick to assign a space character to "space". 58 | 59 | space := 60 | space += 61 | q2s = $(subst ?, $(space),$1) 62 | 63 | # Now define the pattern rule to create compile .scpt files from .applescript 64 | # files. A rule of the form %.scpt would not work if the file names had space 65 | # characters in them (because the outcome of Make expanding %.scpt would have 66 | # spaces in the result, and those spaces would act as file name delimiters). 67 | # But if the file names used with this pattern rule have question marks 68 | # instead of spaces, they're atomic units as far as the pattern matching 69 | # mechanism is concerned, and so the rule works. We just have to do some 70 | # substitution to reconstruct the real file names before we run commands that 71 | # act on the files themselves. (That's where q2s comes in.) 72 | 73 | .SECONDEXPANSION: 74 | %.scpt: %.applescript 75 | osacompile -o "$(call q2s,$*.scpt)" "$<" 76 | 77 | # The final ingredient is to define the target "compile" such that it depends 78 | # on a list of .scpt files. This is simply done by taking the list of source 79 | # files (names such as "Some?file?name.applescript") and replacing the 80 | # extensions. The resulting list (names like "Some?file?name.scpt") then 81 | # satisfies the %.scpt rule above. 82 | 83 | #: Compile the AppleScript files in this directory. 84 | compile: $(sources:.applescript=.scpt) 85 | 86 | 87 | # Install binaries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 88 | 89 | # Note the use of ? in the directory name below, instead of the space character 90 | # (i.e. ~/Library/Application Scripts/com.devon-technologies.think3/Smart Rules). 91 | 92 | destdir = "$(HOME)/Library/Application?Scripts/com.devon-technologies.think3/Smart?Rules" 93 | 94 | .SECONDEXPANSION: 95 | $(destdir)/%.scpt: %.applescript 96 | install -bpS "$(call q2s,$*.scpt)" $(call q2s,$(destdir)) 97 | 98 | #: Install the compiled scripts in DEVONthink's script directory. 99 | install: $(addprefix $(destdir)/,$(sources:.applescript=.scpt)) 100 | 101 | 102 | # Cleanup ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 103 | 104 | #: Clean this directory. 105 | clean: 106 | rm -rf *.scpt 107 | 108 | 109 | # Miscellaneous directives ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 110 | 111 | #: Print a random joke from https://icanhazdadjoke.com/. 112 | joke: 113 | @echo "$(shell curl -s https://icanhazdadjoke.com/)" 114 | 115 | # Color codes used in messages. 116 | color := $(shell tput bold; tput setaf 6) 117 | bright := $(shell tput bold; tput setaf 15) 118 | dim := $(shell tput setaf 66) 119 | link := $(shell tput setaf 111) 120 | reset := $(shell tput sgr0) 121 | 122 | .PHONY: help clean joke 123 | -------------------------------------------------------------------------------- /devonthink-smart-rule-scripts/auto-convert-web-page-to-PDF/.graphics/auto-convert-smart-rule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhucka/devonthink-hacks/9a4211aa29f9163ce625b9fb9ede2dccdb6546f1/devonthink-smart-rule-scripts/auto-convert-web-page-to-PDF/.graphics/auto-convert-smart-rule.png -------------------------------------------------------------------------------- /devonthink-smart-rule-scripts/auto-convert-web-page-to-PDF/Auto convert web page to PDF.applescript: -------------------------------------------------------------------------------- 1 | -- Script for DEVONthink to convert a webpage bookmark to PDF 2 | -- 3 | -- Copyright 2024 Michael Hucka. 4 | -- License: MIT License – see file "LICENSE" in the project website. 5 | -- Website: https://github.com/mhucka/devonthink-hacks 6 | -- 7 | -- This is an AppleScript fragment that will only work as the script 8 | -- executed by a Smart Rule in DEVONthink. For more information, see 9 | -- https://github.com/mhucka/devonthink-hacks/auto-convert-web-page-to-PDF/ 10 | 11 | -- This script will add a tag to the PDF document it creates. Set the 12 | -- next property value to the tag name that you want it to use. 13 | 14 | property tagForPDF : "π-archived-page" 15 | 16 | -- The companion bookmarklet can be used on regular web pages as well 17 | -- as pages that are open on PDF files. In the case of web pages, this 18 | -- script uses heuristics to try to force page content to be loaded 19 | -- fully before creating a full-page PDF snapshot of the page using the 20 | -- PDF conversion facilityin DEVONthink. Beware that this takes 10+ 21 | -- seconds to finish (possibly longer, depending on page contents). 22 | 23 | on performSmartRule(theRecords) 24 | repeat with selected in theRecords 25 | set recordURL to (URL of selected) as string 26 | set recordWindow to open window for record selected 27 | delay 2 28 | 29 | -- Wait until it's finished loading. 30 | repeat while loading of recordWindow 31 | delay 0.5 32 | end repeat 33 | 34 | -- If it's already a PDF, don't need to do more. 35 | if (recordURL ends with ".pdf") is not true then 36 | -- Some pages load content dynamically, with elements not 37 | -- displayed until they come into view. This is a hopeless 38 | -- situation in general but the following heuristic improves 39 | -- outcomes for some cases. It scrolls the window by 40 | -- quarters to try to trigger loading of more page elements. 41 | repeat with n from 1 to 4 42 | set scroll to "window.scrollTo(0," & n ¬ 43 | & "*document.body.scrollHeight/4)" 44 | do JavaScript scroll in current tab of recordWindow 45 | delay 0.75 46 | end repeat 47 | 48 | -- Return to the top. Do it twice because sometimes on some 49 | -- pages (notably Twitter), the first attempt gets stuck in 50 | -- some random location. (Ugh, what an utter hack this is.) 51 | do JavaScript "window.scrollTo(0,0)" ¬ 52 | in current tab of recordWindow 53 | delay 0.5 54 | do JavaScript "window.scrollTo(0,0)" ¬ 55 | in current tab of recordWindow 56 | delay 0.25 57 | end if 58 | 59 | -- Get the content of our current viewer window, in PDF form. 60 | -- This approach instead of using DEVONthink's "convert record 61 | -- to single page PDF document" is critical to getting the 62 | -- version of the page that is produced with the user's 63 | -- login/session state. 64 | set contentAsPDF to get PDF of recordWindow 65 | 66 | set recordName to (name of selected) 67 | set recordParents to (parents of selected) 68 | set recordGroup to item 1 of recordParents 69 | 70 | -- Create the new record in the 1st parent group of the selected 71 | -- item. (FIXME: that's an arbitrary choice of groups if item is 72 | -- replicated. Should do something smarter here.) 73 | set newDoc to create record with ¬ 74 | {name:recordName, URL:recordURL, type:PDF document} ¬ 75 | in recordGroup 76 | set data of newDoc to contentAsPDF 77 | set creation date of newDoc to creation date of selected 78 | set modification date of newDoc to modification date of selected 79 | set comment of newDoc to comment of selected 80 | set label of newDoc to label of selected 81 | 82 | -- Copy the tags of the original document and add tagForPDF. 83 | set {od, AppleScript's text item delimiters} ¬ 84 | to {AppleScript's text item delimiters, ","} 85 | set the tags of newDoc to (tags of selected & tagForPDF) 86 | set AppleScript's text item delimiters to od 87 | 88 | close recordWindow 89 | end repeat 90 | end performSmartRule 91 | -------------------------------------------------------------------------------- /devonthink-smart-rule-scripts/auto-convert-web-page-to-PDF/README.md: -------------------------------------------------------------------------------- 1 | Auto convert web page to PDF 2 | ============================ 3 | 4 | I find that DEVONthink is one of the best tools for saving snapshots of web pages as full-page PDF files, because its embedded web browser is interactive and lets you log into websites that use sessions. Once logged in, you can save pages that would otherwise be inaccessible; moreover, the login sessions persist just as they do in a regular browser, so that you don't always have to log back in every time. 5 | 6 | However, I'm not always browsing the web from within DEVONthink, and if I want to save a page encountered while using a normal web browser like Safari, I need to send it to DEVONthink and tell it to save it as a full-page PDF. This could be done using the standard clipper extension or bookmarklet provided with DEVONthink, but there is a catch: some web pages are generated dynamically and sent incrementally, with content not revealed until you scroll further down on the page. DEVONthink's default capture method will not always get the full page in those cases. 7 | 8 | In general, capturing dynamically-generated pages is a hopelessly difficult task. However, for my purposes, I've solved this for most cases using a combination of mechanisms: 9 | 10 | 1. A Smart Rule in DEVONthink that is triggered when a new web page bookmark is created if the bookmark contains a certain tag; 11 | 2. An AppleScript program that is executed by the Smart Rule; it opens the page, scrolls down the page a few times, and tells DEVONthink to convert the resulting window content to PDF. 12 | 3. A custom bookmarklet that can be used from Safari that, when invoked, sends a URL to DEVONthink with the required tag. 13 | 14 | The following image shows the configuration of my Smart Rule in DEVONthink: 15 | 16 |
17 |
18 |
15 |
16 |
23 |
24 |
8 |
9 |
10 |
11 | The rule's conditions match files that do not contain an `x-devonthink-item` URI in the Finder comment, and trigger on certain events such as creating a document in DEVONthink. In the bottom part of the rule, there are two actions. First, it writes the `x-devonthink-item` URI into the Finder comment in DEVONthink (which DEVONthink shows to the user, but beware of some implications noted below); second, it runs a short AppleScript that invokes [Urial](https://github.com/mhucka/urial). The AppleScript code is the file `Write URI into Finder comment using Urial.applescript` in this directory. In order to make the script accessible as an external AppleScript file within DEVONthink, I compiled it and copied it to my `~/Library/Application Scripts/com.devon-technologies.think3/Smart Rules` directory (and called it "Write URI into Finder comment using Urial.scpt", which is the name you see in the rule definition above).
12 |
13 | The need for both actions is due to how DEVONthink behaves (at least up to version 3.8). When you modify the metadata field called "Finder Comments" in the DEVONthink user interface, it [does not actually write the field value to the macOS file](https://discourse.devontechnologies.com/t/how-can-i-make-finder-comments-added-in-dt-show-up-in-finder-get-info-box/68186) at that point. In fact, it never writes the metadata to the file properties unless the file is an indexed file or you export the file. Thus, merely updating the comment in DEVONthink is not enough to make it visible outside of DEVONthink, and an additional action is needed.
14 |
15 | I wrote [Urial](https://github.com/mhucka/urial) to help with this task. The program intelligently updates URIs in Finder comments rather than blindly replacing the comment text.
16 |
--------------------------------------------------------------------------------
/devonthink-smart-rule-scripts/write-uri-into-comments/Write URI into Finder comment using Urial.applescript:
--------------------------------------------------------------------------------
1 | -- Summary: write the ref. URL of each record to its Finder comment.
2 | --
3 | -- ╭─────────────────── Notice ── Notice ── Notice ───────────────────╮
4 | -- │ This AppleScript code will only work as a script executed by a │
5 | -- │ Smart Rule in DEVONthink. It won't work as a standalone script. │
6 | -- ╰──────────────────────────────────────────────────────────────────╯
7 | --
8 | -- A DEVONthink "reference URL" is a URI that begins with the URI scheme
9 | -- "x-devonthink-item" and contains the UUID of an item in DEVONthink.
10 | -- This script reads the DEVONthink reference URL of each selected
11 | -- document in DEVONthink, and runs an external program (Urial) to set
12 | -- the document's Finder comment to that URI. The reason this is useful
13 | -- is that when you are editing a document in an external editor
14 | -- (something that is permitted by DEVONthink), there is no inherent
15 | -- information linking the file on disk to the database record that
16 | -- corresponds to it. Using the reference URL is convenient on macOS
17 | -- because of macOS's built-in URI scheme handler mechanisms, but the
18 | -- reference URL is not a property of the file on disk. If you want to
19 | -- know the reference URL of a given file, you have to come up with an
20 | -- approach to storing it yourself in such a way that external programs
21 | -- can find it. The approach taken here is to write it into the Finder
22 | -- comment. This makes it possible to find the DEVONthink document by
23 | -- reading the Finder comment (something that can be done in various
24 | -- ways, such as with the use of AppleScript) and asking macOS to open
25 | -- the document using the reference URI. For example, you can do
26 | -- "open x-devonthink-item://474AB439-369E-429D-A856-924DDABC" from a
27 | -- shell terminal and macOS will tell DEVONthink to open the document,
28 | -- effectively letting you jump directly to the document from outside
29 | -- of DEVONthink.
30 | --
31 | -- Copyright 2024 Michael Hucka.
32 | -- License: MIT License – see file "LICENSE" in the project website.
33 | -- Website: https://github.com/mhucka/devonthink-hacks
34 |
35 | use AppleScript version "2.4" -- Yosemite (10.10) or later
36 | use scripting additions
37 |
38 | -- Config variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
39 |
40 | -- This list is used to set the shell's command search $PATH. The values
41 | -- cover the most likely places where Urial may be installed. If your
42 | -- copy of Urial is not found in one of these locations, add the
43 | -- appropriate directory to this list.
44 |
45 | property shell_paths: { ¬
46 | "$PATH" , ¬
47 | "$HOME/.local/bin" , ¬
48 | "$HOME/.pyenv/shims" , ¬
49 | "$HOME/.pyenv/bin" , ¬
50 | "$HOME/bin" , ¬
51 | "/usr/local/bin" , ¬
52 | "/opt/homebrew/bin" ¬
53 | }
54 |
55 | -- Helper functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
56 |
57 | on concat(string_list, separator)
58 | set output to ""
59 | repeat with i from 1 to count of string_list
60 | set output to output & item i of string_list
61 | if i < count of string_list then
62 | set output to output & separator
63 | end if
64 | end repeat
65 | return output
66 | end concat
67 |
68 | on sh(shell_command)
69 | set paths to "PATH=" & my concat(shell_paths, ":")
70 | set result to do shell script (paths & " " & shell_command)
71 | end shell_cmd
72 |
73 | -- The following function is based on code posted by user "mb21" on
74 | -- 2016-06-26 at https://stackoverflow.com/a/38042023/743730
75 |
76 | on substituted(search_string, replacement_string, this_text)
77 | set AppleScript's text item delimiters to the search_string
78 | set the item_list to every text item of this_text
79 | set AppleScript's text item delimiters to the replacement_string
80 | set this_text to the item_list as string
81 | set AppleScript's text item delimiters to ""
82 | return this_text
83 | end substituted
84 |
85 | -- Main body ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
86 |
87 | on performSmartRule(selected_records)
88 | -- The "tell" here isn't needed for DEVONthink (this whole script is
89 | -- executed by DEVONthink), but osacompile gives errors without it.
90 | tell application id "DNtp"
91 | try
92 | repeat with _record in selected_records
93 | set uri to reference URL of the _record as string
94 | set file_path to the path of _record
95 |
96 | -- Some chars in file names are problematic due to having
97 | -- special meaning to the shell. Need to quote them, but
98 | -- here, need to use 2 blackslashes, b/c the 1st backslash
99 | -- will be removed when the string is handed to the shell.
100 | set file_path to my substituted("&", "\\\\&", file_path)
101 |
102 | -- Another problem for shell strings is embedded single
103 | -- quotes. Combo of changing the text delimiter & using
104 | -- the AS "quoted form of" below seems to do the trick.
105 | set AppleScript's text item delimiters to "\\\\"
106 | set fp to quoted form of file_path
107 |
108 | set out to my sh("urial -m update -U " & uri & " " & fp)
109 |
110 | -- Display a notification if urial returned a msg.
111 | if out is not equal to "" then
112 | display alert "Urial" message out as warning
113 | end if
114 | end repeat
115 | on error msg number code
116 | if the code is not -128 then
117 | display alert "DEVONthink" message msg as warning
118 | end if
119 | end try
120 | end tell
121 | end performSmartRule
122 |
--------------------------------------------------------------------------------
/devonthink-smart-rule-scripts/write-uri-into-comments/smart-rule-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhucka/devonthink-hacks/9a4211aa29f9163ce625b9fb9ede2dccdb6546f1/devonthink-smart-rule-scripts/write-uri-into-comments/smart-rule-screenshot.png
--------------------------------------------------------------------------------
/devonthink-smart-rule-scripts/zoinks-scripts/Add Zotero abstract.applescript:
--------------------------------------------------------------------------------
1 | -- Set custom metadata "abstract" using Zonks
2 | --
3 | -- Copyright 2024 Michael Hucka.
4 | -- License: MIT License – see file "LICENSE" in the project website.
5 | -- Website: https://github.com/mhucka/devonthink-hacks
6 | --
7 | -- This is an AppleScript fragment that will only work as the script
8 | -- executed by a Smart Rule in DEVONthink. It runs Zoinks to get the
9 | -- abstract for a Zotero record (technically, what Better BibTeX alls
10 | -- the "abstractNote" field), and sets a metadata field in DEVONthink
11 | -- for storing the abstract. If there is no abstract in the Zotero
12 | -- record, this sets the metadata field value to "(No abstract.)".
13 | --
14 | -- This expects to be passed a record that has a zotero://select/...
15 | -- link in its "URL" metadata field. This Zotero link value is set by
16 | -- a separate DEVONthink Smart Rule that runs another program, Zowie.
17 | -- (C.f. https://github.com/mhucka/devonthink-hacks/zowie-scripts)
18 |
19 | on performSmartRule(selectedRecords)
20 | tell application id "DNtp"
21 | try
22 | repeat with _record in selectedRecords
23 | set _abstract to do shell script ¬
24 | "echo " & (URL of _record) & " | " & ¬
25 | "PATH=$PATH:$HOME/.local/bin:$HOME/.pyenv/shims:$HOME/.pyenv/bin:/usr/local/bin:/opt/homebrew/bin" ¬
26 | & " zoinks -U abstractNote"
27 | if _abstract ≠ "" then
28 | -- Remove embedded newlines in the abstract.
29 | set _abstract to do shell script ¬
30 | "echo " & quoted form of _abstract & "| tr -s '\\r\\n' ' '"
31 | else
32 | set _abstract to "(No abstract.)"
33 | end if
34 | add custom meta data _abstract for "abstract" to _record
35 | end repeat
36 | on error msg number code
37 | if the code is not -128 then
38 | display alert "Zoinks" message msg as warning
39 | end if
40 | end try
41 | end tell
42 | end performSmartRule
43 |
--------------------------------------------------------------------------------
/devonthink-smart-rule-scripts/zoinks-scripts/Add Zotero citekey.applescript:
--------------------------------------------------------------------------------
1 | -- Set custom metadata "abstract" using Zonks
2 | --
3 | -- Copyright 2024 Michael Hucka.
4 | -- License: MIT License – see file "LICENSE" in the project website.
5 | -- Website: https://github.com/mhucka/devonthink-hacks
6 | --
7 | -- This is an AppleScript fragment that will only work as the script
8 | -- executed by a Smart Rule in DEVONthink. It runs Zoinks to get the
9 | -- Better BibTeX citation key for a Zotero record, and sets a custom
10 | -- metadata field in DEVONthink for storing the citation key.
11 | --
12 | -- This expects to be passed a record that has a zotero://select/... link
13 | -- in its "URL" metadata field. This Zotero link value is set by a
14 | -- separate DEVONthink Smart Rule that runs another program, Zowie.
15 | -- (C.f. https://github.com/mhucka/devonthink-hacks/zowie-scripts)
16 |
17 | on performSmartRule(selectedRecords)
18 | tell application id "DNtp"
19 | try
20 | repeat with _record in selectedRecords
21 | set _citekey to do shell script ¬
22 | "echo " & (URL of _record) & " | " & ¬
23 | "PATH=$PATH:$HOME/.local/bin:$HOME/.pyenv/shims:$HOME/.pyenv/bin:/usr/local/bin:/opt/homebrew/bin" ¬
24 | & " zoinks -U citekey"
25 | if _citekey ≠ "" then
26 | add custom meta data _citekey for "citekey" to _record
27 | else
28 | display notification ¬
29 | "Could not get citekey for " & (name of _record)
30 | end if
31 | end repeat
32 | on error msg number code
33 | if the code is not -128 then
34 | display alert "Zoinks" message msg as warning
35 | end if
36 | end try
37 | end tell
38 | end performSmartRule
39 |
--------------------------------------------------------------------------------
/devonthink-smart-rule-scripts/zoinks-scripts/Makefile:
--------------------------------------------------------------------------------
1 | ../Makefile
--------------------------------------------------------------------------------
/devonthink-smart-rule-scripts/zoinks-scripts/README.md:
--------------------------------------------------------------------------------
1 | Run Zoinks
2 | =========
3 |
4 | Scripts for running [Zoinks](https://mhucka.github.io/zoinks).
5 |
--------------------------------------------------------------------------------
/devonthink-smart-rule-scripts/zoinks-scripts/t.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | # See https://github.com/tobywf/pasteboard
4 |
5 | from appscript import app
6 | from commonpy.network_utils import net
7 | import json
8 | import pasteboard
9 | import sys
10 |
11 | bbt_url = 'http://localhost:23119/better-bibtex/json-rpc'
12 |
13 | # pb = pasteboard.Pasteboard()
14 | # text = pb.get_contents()
15 |
16 | app = app('DEVONthink 3')
17 | if not app.isrunning():
18 | exit('DEVONthink is not running')
19 |
20 | windows = app.think_window()
21 | front_window = windows[0]
22 | selected_docs = front_window.selection()
23 |
24 | selected = selected_docs[0]
25 | url = selected.URL()
26 |
27 | # zotero://select/library/items/5A94F2PP
28 | if url.startswith('zotero:'):
29 | key = url.split('/')[-1]
30 |
31 | headers = {'Content-Type': 'application/json', 'Accept' : 'application/json'}
32 | data = ('{"jsonrpc": "2.0", "method": "items.citationkey", '
33 | + '"params": [["' + key + '"]] }')
34 |
35 | (response, error) = net('post', bbt_url, headers = headers, data = data)
36 | if not error:
37 | bbt_output = json.loads(response.text)
38 | if 'result' in bbt_output and key in bbt_output['result']:
39 | print(bbt_output['result'][key])
40 | sys.exit(0)
41 |
42 | print('no zotero URL in clipboard')
43 |
--------------------------------------------------------------------------------
/devonthink-smart-rule-scripts/zotero-scripts/Makefile:
--------------------------------------------------------------------------------
1 | ../Makefile
--------------------------------------------------------------------------------
/devonthink-smart-rule-scripts/zotero-scripts/Start Zotero + BBT if needed.applescript:
--------------------------------------------------------------------------------
1 | # Summary: if Zotero & BBT are not running, launch & wait until they respond.
2 | #
3 | # This assumes that the Better BibTeX plugin has been installed in Zotero
4 | # (see https://retorque.re/zotero-better-bibtex/) but it should be safe
5 | # to use even if the plugin is not installed. (If it's not installed, this
6 | # program will end up waiting the full duration of the wait_time value.)
7 | #
8 | # Copyright 2024 Michael Hucka.
9 | # License: MIT License – see file "LICENSE" in the project website.
10 | # Website: https://github.com/mhucka/devonthink-hacks
11 |
12 | use AppleScript version "2.5"
13 | use scripting additions
14 |
15 | # Config variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16 |
17 | # URL of the Zotero Connector endpoint for Better BibTeX.
18 | property bbt_api_endpoint: "http://localhost:23119/better-bibtex/json-rpc"
19 |
20 | # Approximate duration to wait for Zotero + BBT to start, in seconds.
21 | property wait_time: 20
22 |
23 |
24 | # Helper functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25 |
26 | # Log a message in DEVONthink's log and include the name of this script.
27 | on report(error_text)
28 | local script_path
29 | tell application "System Events"
30 | set script_path to POSIX path of (path to me as alias)
31 | end tell
32 | tell application id "DNtp"
33 | log message script_path info error_text
34 | end tell
35 | log error_text # Useful when running in a debugger.
36 | end report
37 |
38 | # Launch application, wait to see it launched, and bring it to the front.
39 | # Only waits for a limited time, in case something is wrong.
40 | on launch_application(app_name, activate_app)
41 | launch application app_name
42 | tell application "System Events"
43 | set times_left to 30 # 15 sec in 0.5 sec iterations
44 | # This roundabout approach of testing process names is because the more
45 | # direct "repeat until application app_name is running" causes errors.
46 | repeat while times_left > 0
47 | if count of (every process whose name is app_name) > 0 then
48 | exit repeat
49 | end if
50 | delay 0.5
51 | set times_left to (times_left - 1)
52 | end repeat
53 | if times_left ≤ 0 then
54 | error "Timed out waiting for " & app_name & " to launch."
55 | end if
56 | end tell
57 | if activate_app then
58 | tell application app_name to activate
59 | end if
60 | end launch_application
61 |
62 | # Return true if we connected to the BBT endpoint URL in < max_time seconds,
63 | # false if there was no response, and an integer HTTP status code if there was
64 | # a response but it was an HTTP code indicating a problem.
65 | on connect_to_bbt(max_time)
66 | script wrapperScript
67 | property ca: a reference to current application
68 | use framework "Foundation"
69 | on connect_to_bbt(max_time)
70 | # Create the request object with a timeout.
71 | set url_str to ca's NSURL's URLWithString:bbt_api_endpoint
72 | set ignore_cache to ca's NSURLRequestReloadIgnoringCacheData
73 | set request to ca's NSURLRequest's alloc()'s initWithURL:url_str ¬
74 | cachePolicy:(ignore_cache) timeoutInterval:max_time
75 |
76 | # Try to connect.
77 | set {body, response, err} to ca's NSURLConnection's ¬
78 | sendSynchronousRequest:request ¬
79 | returningResponse:(reference) |error|:(reference)
80 |
81 | # Interpret the outcome. No err doesn't necessarily mean success.
82 | if (err is not missing value) or (response is missing value) then
83 | return false
84 | else if response's statusCode() >= 400 then
85 | # This shouldn't happen. Something is wrong with the endpoint.
86 | return response's statusCode()
87 | else
88 | return true
89 | end if
90 | end connect_to_bbt
91 | end script
92 | return wrapperScript's connect_to_bbt(max_time)
93 | end connect_to_bbt
94 |
95 |
96 | # Main body ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
97 |
98 | on performSmartRule(selected_records)
99 | my launch_application("Zotero", false)
100 | tell application "Zotero"
101 | # Better BibTeX takes time to start up after Zotero is running.
102 | # Wait until we can connect to the JSON-RPC endpoint. Note: it's
103 | # okay to modify wait_time directly b/c every execution of this
104 | # script from a Smart Rule will reload the file and thus reset it.
105 | set logged_error to false
106 | repeat while wait_time > 0
107 | set bbt_connection to my connect_to_bbt(1)
108 | if bbt_connection is true then
109 | return
110 | else if (bbt_connection is not false) and not logged_error then
111 | my report("Unable to connect to the Zotero Connector " ¬
112 | & "endpoint for Better BibTeX (HTTP code " ¬
113 | & bbt_connection & ") at " & bbt_api_endpoint)
114 | set logged_error to true
115 | end if
116 | set wait_time to (wait_time - 1)
117 | delay 1
118 | end repeat
119 | my report("Wait time exceeded for starting Zotero & Better BibTeX")
120 | end tell
121 | end performSmartRule
122 |
123 | # Scaffolding for execution outside of a Smart Rule (e.g., in a debugger).
124 | tell application id "DNtp"
125 | my performSmartRule(selection as list)
126 | end tell
127 |
--------------------------------------------------------------------------------
/devonthink-smart-rule-scripts/zotero-scripts/tests/test-framework-failure.applescript:
--------------------------------------------------------------------------------
1 | -- This script will produce an error if used as the
2 |
3 | use AppleScript version "2.5"
4 | use framework "Foundation"
5 | use scripting additions
6 |
7 | on performSmartRule(selectedRecords)
8 | tell application id "DNtp"
9 | try
10 | repeat with theRecord in selectedRecords
11 | set theRecord to item 1 of selectedRecords
12 | display dialog (name of theRecord) as text
13 | end repeat
14 | on error msg number code
15 | if the code is not -128 then
16 | display alert "DEVONthink" message msg as warning
17 | end if
18 | end try
19 | end tell
20 | end performSmartRule
21 |
--------------------------------------------------------------------------------
/devonthink-smart-rule-scripts/zotero-scripts/tests/test-ping.applescript:
--------------------------------------------------------------------------------
1 | use AppleScript version "2.5"
2 | use framework "Foundation"
3 |
4 | on ping(api_url, max_time)
5 | -- Create the request object.
6 | set ca to current application
7 | set url_string to ca's NSURL's URLWithString:api_url
8 | set ignore_cache to ca's NSURLRequestReloadIgnoringCacheData
9 | set request to ca's NSURLRequest's alloc()'s initWithURL:url_string ¬
10 | cachePolicy:(ignore_cache) timeoutInterval:max_time
11 |
12 | -- Try to connect.
13 | set {body, response, err} to ca's NSURLConnection's ¬
14 | sendSynchronousRequest:request ¬
15 | returningResponse:(reference) |error|:(reference)
16 |
17 | -- Interpret the outcome. No error does not necessarily mean success.
18 | if (err is not missing value) or (response is missing value) then
19 | return false
20 | else if {404, 410} contains response's statusCode() then
21 | return false
22 | else
23 | return true
24 | end if
25 | end ping
26 |
27 | if ping("https://httpstat.us/504?sleep=3000", 5) then
28 | log "yes (expected)"
29 | else
30 | log "no (unexpected)"
31 | end if
32 |
33 | if ping("https://httpstat.us/504?sleep=3000", 2) then
34 | log "yes (unexpected)"
35 | else
36 | log "no (expected)"
37 | end if
38 |
--------------------------------------------------------------------------------
/devonthink-smart-rule-scripts/zotero-scripts/tests/test-without-framework.applescript:
--------------------------------------------------------------------------------
1 | -- This script will produce an error if used as the
2 |
3 | use AppleScript version "2.5"
4 | use scripting additions
5 |
6 | on performSmartRule(selectedRecords)
7 | tell application id "DNtp"
8 | try
9 | repeat with theRecord in selectedRecords
10 | set theRecord to item 1 of selectedRecords
11 | display dialog (name of theRecord) as text
12 | end repeat
13 | on error msg number code
14 | if the code is not -128 then
15 | display alert "DEVONthink" message msg as warning
16 | end if
17 | end try
18 | end tell
19 | end performSmartRule
20 |
--------------------------------------------------------------------------------
/devonthink-smart-rule-scripts/zotero-scripts/tests/testapi.applescript:
--------------------------------------------------------------------------------
1 | use AppleScript version "2.4"
2 | use framework "Foundation"
3 |
4 | -- Function to check if an API endpoint responds.
5 | on ping(service_url)
6 | -- Create the request object.
7 | set ca to current application
8 | set _url to ca's |NSURL|'s URLWithString:service_url
9 | set request to ca's |NSURLRequest|'s requestWithURL:_url
10 |
11 | -- Try to connect to the endpoint.
12 | set responseData to missing value
13 | set status to missing value
14 |
15 | set _response to ca's NSHTTPURLResponse's alloc()'s init()
16 | set {_data, _error} to ca's NSURLConnection's sendSynchronousRequest:request ¬
17 | returningResponse:_response |error|:(reference)
18 |
19 | if _error is missing value then
20 | -- No error => connected.
21 | return true
22 | else if class of _error is ca's NSURLError then
23 | -- Could not connect to the URL
24 | return false
25 | else
26 | log "Unexpected error code received by ping()."
27 | return false
28 | end if
29 | end ping
30 |
31 | -- Example usage
32 | set apiURL to "http://localhost:23119/better-bibtex/json-rpc"
33 | set responseStatus to ping(apiURL)
34 |
35 |
--------------------------------------------------------------------------------
/devonthink-smart-rule-scripts/zowie-scripts/Makefile:
--------------------------------------------------------------------------------
1 | ../Makefile
--------------------------------------------------------------------------------
/devonthink-smart-rule-scripts/zowie-scripts/README.md:
--------------------------------------------------------------------------------
1 | Run Zowie
2 | =========
3 |
4 | Scripts for running [Zowie](https://mhucka.github.io/zowie).
5 |
--------------------------------------------------------------------------------
/devonthink-smart-rule-scripts/zowie-scripts/Run Zowie on newly indexed PDF.applescript:
--------------------------------------------------------------------------------
1 | -- Script for DEVONthink smart rule to run Zowie on new additions
2 | --
3 | -- This is an AppleScript fragment that will only work as the script
4 | -- executed by a Smart Rule in DEVONthink.
5 | --
6 | -- Copyright 2024 Michael Hucka.
7 | -- License: MIT License – see file "LICENSE" in the project website.
8 | -- Website: https://github.com/mhucka/devonthink-hacks
9 |
10 | on performSmartRule(selectedRecords)
11 | tell application "System Events"
12 | -- In my environment, Zotero takes time to upload a newly-added
13 | -- PDF to the cloud. The following delay is needed to give time
14 | -- for the upload to take place, so that when Zowie runs and
15 | -- queries Zotero via the network API, the data will be there.
16 | delay 15
17 | end tell
18 | tell application id "DNtp"
19 | try
20 | repeat with _record in selectedRecords
21 | set raw_path to the path of _record
22 |
23 | -- A problem for shell strings is embedded single quotes.
24 | -- Combo of changing text delimiters & using AppleScript
25 | -- "quoted form of" seems to do the trick.
26 | set AppleScript's text item delimiters to "\\\\"
27 | set quoted_path to quoted form of raw_path
28 |
29 | -- Now run Zowie. The PATH setting adds common locations
30 | -- where Zowie may be installed on the user's computer.
31 | set result to do shell script ¬
32 | "PATH=$PATH:$HOME/.local/bin:$HOME/.pyenv/shims:$HOME/.pyenv/bin:/usr/local/bin:/opt/homebrew/bin" ¬
33 | & " zowie -s -q " & quoted_path
34 |
35 | -- If Zowie returned a msg, something went wrong.
36 | if result ≠ "" then
37 | display notification result
38 | else
39 | -- Finish by telling DT explicitly to set the comment,
40 | -- because it doesn't consistently "notice" addition
41 | -- of a comment after a file has already been indexed.
42 | set comment of _record to my finderComment(raw_path)
43 | end if
44 | end repeat
45 | on error msg number code
46 | if the code is not -128 then
47 | display alert "DEVONthink" message msg as warning
48 | end if
49 | end try
50 | end tell
51 | end performSmartRule
52 |
53 | -- Function to ask the Finder to read the comment.
54 |
55 | on finderComment(f)
56 | tell application "Finder"
57 | return comment of (POSIX file f as alias)
58 | end tell
59 | end finderComment
60 |
61 | -- Function to make literal text substitutions.
62 | -- The following function is based on code posted by user "mb21" on
63 | -- 2016-06-26 at https://stackoverflow.com/a/38042023/743730
64 |
65 | on substituted(search_string, replacement_string, this_text)
66 | set AppleScript's text item delimiters to the search_string
67 | set the item_list to every text item of this_text
68 | set AppleScript's text item delimiters to the replacement_string
69 | set this_text to the item_list as string
70 | set AppleScript's text item delimiters to ""
71 | return this_text
72 | end substituted
73 |
--------------------------------------------------------------------------------
/devonthink-templates/Annotations.noindex/Reading notes.md:
--------------------------------------------------------------------------------
1 | CSS: x-devonthink-item://C1043FB7-A957-4CCA-848D-D3FDB79B5C62
2 | Backlink: %documentLink%
3 |
4 | %documentName%
5 |
6 | {{firstheading}}Full reference{{_heading}}
7 |
8 | %bibReference%
9 |
10 | {{heading}}Topic area{{_heading}}
11 |
12 | _(Briefly, what's the subject? Use some common identifying keywords)_
13 |
14 | {{heading}}Summary{{_heading}}
15 |
16 | _(Put myself in the mind of summarizing the work in an email to a colleague)_
17 |
18 | - [ ] Who are the authors?
19 | - [ ] What did they do?
20 | - [ ] How did they do it?
21 | - [ ] What were their results?
22 |
23 | {{heading}}Evaluation and reflection{{_heading}}
24 |
25 | - [ ] Clear aims and objectives?
26 | - [ ] Well-defined hypothesis?
27 | - [ ] Reasonable methods?
28 | - [ ] Reasonable analyses?
29 | - [ ] Reasonable conclusions?
30 | - [ ] Strengths?
31 | - [ ] Weaknesses/limitations?
32 | - [ ] Credibility of this work?
33 |
34 | {{heading}}Important outcomes or ideas{{_heading}}
35 |
36 | _(What are notable, new, interesting, significant points about this work?)_
37 |
38 | {{heading}}What are related works?{{_heading}}
39 |
40 | _(Link to other papers)_
41 |
42 | {{heading}}Checklist{{_heading}}
43 |
44 | _(Delete this section & heading when done)_
45 |
46 | - [ ] subject tags
47 | - [ ] result type tag
48 | - [ ] value rating
49 | - [ ] confidence tags
50 |
--------------------------------------------------------------------------------
/devonthink-templates/README.md:
--------------------------------------------------------------------------------
1 | # Template files I use in DEVONthink
2 |
3 | This contains template files that I use for creating certain kinds of new documents from within DEVONthink. Beware that these are extremely idiosyncratic to my way of doing things, and moreover, make a lot of assumptions about other software I use, and other things I set up in DEVONthink (like smart rules), so they are unlikely to be useful as-is for anyone else. Still, I need to keep them in version control _somewhere_, so they're here.
4 |
5 | Note that some of these may be old and no longer in active use.
6 |
7 | ## Setup
8 |
9 | DEVONthink doesn't provide a way for users to store their templates in a user-defined location. To be accessible in DEVONthink, template files _must_ be located in these locations:
10 |
11 | * `~/Library/Application Support/DEVONthink 3/Annotations.noindex` (for creating annotation files from the _Annotations & Reminders_ inspector)
12 | * `~/Library/Application Support/DEVONthink 3/Templates.noindex` (for creating new documents using the menu item Data ▹New From Template)
13 |
14 | Those folders must exist in those locations, but at the file system level, DEVONthink can't distinguish between an actual directory and a symbolic link to a directory. Thus, what we can do is move those directories elsewhere, and replace the locations above with symbolic links.
15 |
16 | ```sh
17 | cd "~Library/Application Support/DEVONthink 3"
18 | mv Annotations.noindex ~/projects/software/repos/devonthink-hacks/devonthink-templates/
19 | mv Templates.noindex ~/projects/software/repos/devonthink-hacks/devonthink-templates/
20 | ln -s ~/projects/software/repos/devonthink-hacks/devonthink-templates/Annotations.noindex
21 | ln -s ~/projects/software/repos/devonthink-hacks/devonthink-templates/Templates.noindex
22 | ```
23 |
24 | DEVONthink includes a lot of default templates, and when you upgrade DEVONthink, it rewrites its default templates in `Templates.noindex`. Thankfully all of those DEVONthink-provided templates are in subdirectories, so we can tell them apart from personal additions. I don't keep their defaults in version control and there's a `.gitignore` file inside `Templates.noindex` here to leave them out of git.
25 |
26 | (I personally don't use any of the default templates, and would rather that DEVONthink didn't keep adding them back to my `Templates.noindex` directory. It would be possible to prevent DEVONthink from overwriting `Templates.noindex` by locking the directory, but then it would be a pain to update my own templates. So, I live with the current situation.)
27 |
28 | To make the templates available on my iPad and iPhone, I create hard links in an iCloud storage folder. On the Mac, at leat in macOS Ventura, the location of a user's iCloud drive is
29 |
30 | ```
31 | ~/Library/Mobile Documents/com~apple~CloudDocs/
32 | ```
33 |
34 | Inside that, I create a `DEVONthink templates` directory, then inside _that_, I put hard links to all the template files folder. The files are in a flat organization, without subdirectories, because I don't have enough templates to be worth making it more complicated than that.
35 |
--------------------------------------------------------------------------------
/devonthink-templates/Templates.noindex/.gitignore:
--------------------------------------------------------------------------------
1 | Classifications/
2 | Education/
3 | Productivity/
4 | Registers/
5 | Smart\ Groups/
6 | Accounts\ &\ Passwords/
7 |
--------------------------------------------------------------------------------
/devonthink-templates/Templates.noindex/Brief note.ooutline:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhucka/devonthink-hacks/9a4211aa29f9163ce625b9fb9ede2dccdb6546f1/devonthink-templates/Templates.noindex/Brief note.ooutline
--------------------------------------------------------------------------------
/devonthink-templates/Templates.noindex/Card.md:
--------------------------------------------------------------------------------
1 | {{cardtitle}}%title%{{_cardtitle}}
2 |
--------------------------------------------------------------------------------
/devonthink-templates/Templates.noindex/Clipboard to markdown.md:
--------------------------------------------------------------------------------
1 | Title: %@
2 | Creation date: %shortDate%
3 | DEVONthink database: %databaseName%
4 |
5 | %clipboard%
6 |
--------------------------------------------------------------------------------
/devonthink-templates/Templates.noindex/Code.md:
--------------------------------------------------------------------------------
1 | ```
2 | %clipboard%
3 | ```
4 |
5 | Source: [%sourceTitle%](%sourceURL%) (copied on %shortDate% at %time%).
6 |
--------------------------------------------------------------------------------
/devonthink-templates/Templates.noindex/Diary.ooutline:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhucka/devonthink-hacks/9a4211aa29f9163ce625b9fb9ede2dccdb6546f1/devonthink-templates/Templates.noindex/Diary.ooutline
--------------------------------------------------------------------------------
/devonthink-templates/Templates.noindex/Empty markdown.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhucka/devonthink-hacks/9a4211aa29f9163ce625b9fb9ede2dccdb6546f1/devonthink-templates/Templates.noindex/Empty markdown.md
--------------------------------------------------------------------------------
/devonthink-templates/Templates.noindex/Glossary.md:
--------------------------------------------------------------------------------
1 | Base Header Level: 0
2 |
3 | ### %title%
4 |
--------------------------------------------------------------------------------
/devonthink-templates/Templates.noindex/Goal plan.ooutline:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhucka/devonthink-hacks/9a4211aa29f9163ce625b9fb9ede2dccdb6546f1/devonthink-templates/Templates.noindex/Goal plan.ooutline
--------------------------------------------------------------------------------
/devonthink-templates/Templates.noindex/Markdown.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhucka/devonthink-hacks/9a4211aa29f9163ce625b9fb9ede2dccdb6546f1/devonthink-templates/Templates.noindex/Markdown.md
--------------------------------------------------------------------------------
/devonthink-templates/Templates.noindex/Meeting.ooutline:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhucka/devonthink-hacks/9a4211aa29f9163ce625b9fb9ede2dccdb6546f1/devonthink-templates/Templates.noindex/Meeting.ooutline
--------------------------------------------------------------------------------
/devonthink-templates/Templates.noindex/Notes.ooutline:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhucka/devonthink-hacks/9a4211aa29f9163ce625b9fb9ede2dccdb6546f1/devonthink-templates/Templates.noindex/Notes.ooutline
--------------------------------------------------------------------------------
/devonthink-templates/Templates.noindex/Quote.md:
--------------------------------------------------------------------------------
1 | > %clipboard%
2 |
3 | Source: [%sourceTitle%](%sourceURL%) (copied on %shortDate% at %time%).
4 |
5 |
--------------------------------------------------------------------------------
/devonthink-templates/Templates.noindex/Records markdown.md:
--------------------------------------------------------------------------------
1 | ---
2 | File: "%fileName%"
3 | Creation-date: %sortableDate%
4 | DEVONthink-database: "%databaseName%"
5 | ---
6 |
--------------------------------------------------------------------------------
/devonthink-templates/Templates.noindex/Spreadsheet.numbers:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhucka/devonthink-hacks/9a4211aa29f9163ce625b9fb9ede2dccdb6546f1/devonthink-templates/Templates.noindex/Spreadsheet.numbers
--------------------------------------------------------------------------------
/devonthink-templates/Templates.noindex/Term definition.md:
--------------------------------------------------------------------------------
1 | Base Header Level: 0
2 |
3 | ### %title%
4 |
5 | #### Synonyms
6 |
7 | #### Related
8 |
--------------------------------------------------------------------------------
/devonthink-templates/makelinks.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Summary: create hard links to these template files in an iCloud folder.
3 | #
4 | # Copyright 2024 Michael Hucka.
5 | # License: MIT license – see file "LICENSE" in the project website.
6 | # Website: https://github.com/mhucka/devonthink-hacks
7 |
8 | set -e
9 |
10 | thisdir="$(cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P)"
11 | clouddir="${HOME}/Library/Mobile Documents/com~apple~CloudDocs/DEVONthink templates"
12 |
13 | # Dealing with spaces in file names is such a PITA.
14 | IFS=$'\n'
15 | for path in `find -E *.noindex -maxdepth 2 -iregex '.*\.(md|ooutline|numbers)'`; do
16 | filename=$(basename "$path")
17 | ln "$thisdir/$path" "$clouddir/"
18 | done
19 |
--------------------------------------------------------------------------------
/devonthink-toolbar-scripts/.gitignore:
--------------------------------------------------------------------------------
1 | */icon.png
2 |
--------------------------------------------------------------------------------
/devonthink-toolbar-scripts/Makefile:
--------------------------------------------------------------------------------
1 | # Summary: my generic Makefile for compiling AppleScript files.
2 | #
3 | # Copyright 2024 Michael Hucka.
4 | # License: MIT License – see file "LICENSE" in the project website.
5 | # Website: https://github.com/mhucka/devonthink-hacks
6 |
7 | # Preliminary settings and tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8 |
9 | SHELL=/bin/bash
10 | .ONESHELL: # Run all commands in the same shell.
11 | .SHELLFLAGS += -e # Exit at the first error.
12 |
13 | thisdir := $(shell basename $(CURDIR))
14 |
15 | # When I run M-x compile using this Makefile, the compile target works but the
16 | # install target fails. It works outside Emacs in a regular shell terminal. I
17 | # haven't figured out the reason, so for now, this test reminds me to avoid it.
18 |
19 | ifeq ($(origin INSIDE_EMACS),environment)
20 | $(error "Do not run make from inside Emacs with this Makefile.")
21 | endif
22 |
23 | # This Makefile uses syntax that needs at least GNU Make version 3.82.
24 | # The following test is based on the approach posted by Eldar Abusalimov to
25 | # Stack Overflow in 2012 at https://stackoverflow.com/a/12231321/743730
26 |
27 | ifeq ($(filter undefine,$(value .FEATURES)),)
28 | $(error Unsupported version of Make. \
29 | This Makefile does not work properly with GNU Make $(MAKE_VERSION); \
30 | it needs GNU Make version 3.82 or later)
31 | endif
32 |
33 | # Before we go any further, test if certain programs are available.
34 | # The following is based on the approach posted by Jonathan Ben-Avraham to
35 | # Stack Overflow in 2014 at https://stackoverflow.com/a/25668869
36 |
37 | programs_needed = osacompile yq fileicon convert
38 | TEST := $(foreach p,$(programs_needed),\
39 | $(if $(shell which $(p)),_,$(error Cannot find program "$(p)")))
40 |
41 |
42 | # Print help if no command is given ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
43 |
44 | # The help scheme works by looking for lines that begin with "#:" above the
45 | # definitions of commands. Originally based on code posted to Stack Overflow on
46 | # 2019-11-28 by Richard Kiefer at https://stackoverflow.com/a/59087509/743730
47 |
48 | #: Print a summary of available commands.
49 | help:
50 | @echo "This is the Makefile for $(bright)$(thisdir)$(reset)."
51 | @echo "Available commands:"
52 | @echo
53 | @grep -B1 -E "^[a-zA-Z0-9_-]+\:([^=]|$$)" $(MAKEFILE_LIST) \
54 | | grep -v -- -- \
55 | | sed 'N;s/\n/###/' \
56 | | sed -n 's/^#: \(.*\)###\([a-zA-Z0-9_-]*\):.*/$(color)\2$(reset):###\1/p' \
57 | | column -t -s '###'
58 |
59 |
60 | # Create icon file ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
61 |
62 | # This assumes the file is an SVG file from Noun Project. The steps here are:
63 | #
64 | # 1. Filter the SVG file to remove the text. Yes, this removes the author and
65 | # source attributions, but I think that's okay because (a) this is for my
66 | # personal use on my own computer -- I'm not distributing the result -- and
67 | # (b) I attribute the icon author in the README.md file in this directory.
68 | # 2. Filter the SVG file to change the color from black to a green-gray, so
69 | # that when they're added to the toolbar, I can tell which icons are mine.
70 | # 3. Convert SVG to PNG.
71 | # 4. Add some whitespace padding around the icon, based on the proportions of
72 | # that I see in icons in DEVONthink (vers. 3.9.4 on macOS 10.13.6).
73 | # 5. Turn white background to transparent.
74 |
75 | icon.png: icon.svg
76 | yq -p xml -o xml 'del(.svg.text)' icon.svg > cleaned.svg
77 | sed 's/#000000/#7fc43b/' < cleaned.svg > green.svg
78 | convert -trim -size "400x400>" green.svg green.png
79 | convert -background white -gravity center -extent 512x512 \
80 | -alpha remove -alpha off green.png tmp.png
81 | convert -fuzz 25% -fill "#7fc43b" -opaque "#000000" tmp.png green.png
82 | convert green.png -fuzz 10% -transparent white icon.png
83 | rm -f cleaned.svg green.svg green.png tmp.png
84 |
85 |
86 | # Compile source files ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
87 |
88 | # Getting Make to handle path names with embedded spaces is insanely hard.
89 | # The following incredibly obscure workarounds are the combination of two
90 | # methods: (1) by user "mathematical.coffee" posted on 2014-02-11
91 | # at https://stackoverflow.com/a/21694624/743730, and (2) by user Yann-Gaël
92 | # Guéhéneuc posted on 2016-02-25 at https://stackoverflow.com/a/35617603/743730
93 |
94 | # We first create a list of source files in a way that bypasses the regular
95 | # Make file pattern matching, and then replace every space character in
96 | # each pathname with a question mark character.
97 |
98 | sources = $(shell find . -iname '*.applescript' -maxdepth 1 | cut -d/ -f2- | tr ' ' '?')
99 |
100 | # Next, define a function named "q2s" to replace question marks by spaces,
101 | # using an absolutely bonkers trick to assign a space character to "space".
102 |
103 | space :=
104 | space +=
105 | q2s = $(subst ?, $(space),$1)
106 |
107 | # Now define the pattern rule to create compile .scpt files from .applescript
108 | # files. A rule of the form %.scpt would not work if the file names had space
109 | # characters in them (because the outcome of Make expanding %.scpt would have
110 | # spaces in the result, and those spaces would act as file name delimiters).
111 | # But if the file names used with this pattern rule have question marks
112 | # instead of spaces, they're atomic units as far as the pattern matching
113 | # mechanism is concerned, and so the rule works. We just have to do some
114 | # substitution to reconstruct the real file names before we run commands that
115 | # act on the files themselves. (That's where q2s comes in.)
116 |
117 | .SECONDEXPANSION:
118 | %.scpt: %.applescript
119 | osacompile -o "$(call q2s,$*.scpt)" "$<"
120 |
121 | # The final ingredient is to define the target "compile" such that it depends
122 | # on a list of .scpt files. This is simply done by taking the list of source
123 | # files (names such as "Some?file?name.applescript") and replacing the
124 | # extensions. The resulting list (names like "Some?file?name.scpt") then
125 | # satisfies the %.scpt rule above.
126 |
127 | #: Compile the AppleScript files in this directory.
128 | compile: $(sources:.applescript=.scpt) icon.png
129 | fileicon set "$(call q2s,$<)" icon.png
130 |
131 |
132 | # Install binaries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
133 |
134 | # The /Menu location is to add items to the "Scripts" menu that appears in some
135 | # DEVONthink windows such as group/folder lists. The /Toolbar location is for
136 | # making scripts available in the menubar of document windows. For the latter,
137 | # you need to quit and relaunch DEVONthink because the items are cached.
138 |
139 | # Note the use of ? in the directory name below, instead of the space character
140 | # (i.e. ~/Library/Application Scripts/com.devon-technologies.think3/Menu).
141 |
142 | destdir1 = "$(HOME)/Library/Application?Scripts/com.devon-technologies.think3/Menu"
143 | destdir2 = "$(HOME)/Library/Application?Scripts/com.devon-technologies.think3/Toolbar"
144 |
145 | .SECONDEXPANSION:
146 | $(destdir)/%.scpt: %.applescript
147 | install -bpS "$(call q2s,$*.scpt)" $(call q2s,$(destdir1))
148 | install -bpS "$(call q2s,$*.scpt)" $(call q2s,$(destdir2))
149 |
150 | #: Install the compiled scripts in DEVONthink's script directory.
151 | install: $(addprefix $(destdir)/,$(sources:.applescript=.scpt))
152 | @echo "$(bright)Restart DEVONthink to use additions to the Toolbar.$(reset)"
153 |
154 |
155 | # Cleanup ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
156 |
157 | #: Clean this directory.
158 | clean:
159 | rm -rf *.scpt *.png
160 |
161 |
162 | # Miscellaneous directives ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
163 |
164 | #: Print a random joke from https://icanhazdadjoke.com/.
165 | joke:
166 | @echo "$(shell curl -s https://icanhazdadjoke.com/)"
167 |
168 | # Color codes used in messages.
169 | color := $(shell tput bold; tput setaf 6)
170 | bright := $(shell tput bold; tput setaf 15)
171 | dim := $(shell tput setaf 66)
172 | link := $(shell tput setaf 111)
173 | reset := $(shell tput sgr0)
174 |
175 | .PHONY: help clean joke
176 |
--------------------------------------------------------------------------------
/devonthink-toolbar-scripts/copy-location-paths-of-selected-items/Copy location paths of selected items.applescript:
--------------------------------------------------------------------------------
1 | -- Summary: copy the absolute database paths of the selected items.
2 | --
3 | -- Copyright 2024 Michael Hucka.
4 | -- License: MIT License – see file "LICENSE" in the project website.
5 | -- Website: https://github.com/mhucka/devonthink-hacks
6 |
7 | use AppleScript version "2.4" -- Yosemite (10.10) or later
8 | use scripting additions
9 |
10 | tell application id "DNtp"
11 | try
12 | set paths to ""
13 | -- Start with no linebreak for the first time through the loop.
14 | set linebreak to ""
15 | repeat with _record in (selected records)
16 | set p to (location of _record) & (name of _record)
17 | set paths to paths & linebreak & p
18 | -- Separate subsequent paths with linebreaks. This repeated
19 | -- assignment may seem inefficient, but the alternative is
20 | -- adding a conditinal in addition to having an assignment.
21 | set linebreak to character id 10
22 | end repeat
23 | set the clipboard to paths
24 | on error msg number err
25 | if err is not -128 then
26 | display alert "DEVONthink error" message msg as warning
27 | end if
28 | end try
29 | end tell
30 |
--------------------------------------------------------------------------------
/devonthink-toolbar-scripts/copy-location-paths-of-selected-items/Makefile:
--------------------------------------------------------------------------------
1 | ../Makefile
--------------------------------------------------------------------------------
/devonthink-toolbar-scripts/copy-location-paths-of-selected-items/README.md:
--------------------------------------------------------------------------------
1 | # Copy location paths of selected items
2 |
3 | For each item selected in the frontmost window of DEVONthink, this copies to the clipboard a link to the absolute path to the item in the DEVONthink database. This is a concatenation of the "location" property value and the document name. The result is a string that looks like, for example, `/path/to/the/itemname.md`.
4 |
5 | ## Compilation and installation
6 |
7 | The make procedures require the programs [ImageMagick](https://imagemagick.org/index.php) and [fileicon](https://github.com/mklement0/fileicon). Running `make compile` followed by `make install` in this directory will compile the AppleScript file, give it an icon, and copy the results to two locations:
8 |
9 | ```txt
10 | ~/Library/Application Scripts/com.devon-technologies.think3/Menu/
11 | ~/Library/Application Scripts/com.devon-technologies.think3/Toolbar/
12 | ```
13 |
14 | By putting it in both locations, the program becomes accessible from both the _Scripts_ menu item that appears in certain window contexts in DEVONthink, and as individual toolbar items you can add to the toolbar in certain other contexts. (For example, in windows showing single documents like Markdown documents, there is no _Scripts_ toolbar item available, but programs you install in the `~/Library/.../Toolbar/` location become available as individual toolbar items after you restart DEVONthink.)
15 |
16 |
17 | ## About the icon
18 |
19 | The [vector artwork](https://thenounproject.com/icon/pointer-330358/) used as a starting point for the icon was created by [Shital Patel](https://thenounproject.com/shital777/) for the Noun Project. It is licensed under the Creative Commons [Attribution 3.0 Unported](https://creativecommons.org/licenses/by/3.0/deed.en) license.
20 |
--------------------------------------------------------------------------------
/devonthink-toolbar-scripts/copy-location-paths-of-selected-items/icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/devonthink-toolbar-scripts/copy-markdown-links-to-selected-items/Copy Markdown links to selected items.applescript:
--------------------------------------------------------------------------------
1 | -- Summary: create Markdown-formatted links to the selected items.
2 | -- The links use the DEVONthink reference URLs of the items.
3 | --
4 | -- Copyright 2024 Michael Hucka.
5 | -- License: MIT License – see file "LICENSE" in the project website.
6 | -- Website: https://github.com/mhucka/devonthink-hacks
7 |
8 | use AppleScript version "2.4" -- Yosemite (10.10) or later
9 | use scripting additions
10 |
11 | tell application id "DNtp"
12 | try
13 | set links to ""
14 | -- Start with no linebreak for the first time through the loop.
15 | set linebreak to ""
16 | repeat with _record in (selected records)
17 | set _name to name of _record
18 | set _ref_url to reference URL of _record
19 | set _link to "[" & _name & "](" & _ref_url & ")"
20 | set links to links & linebreak & _link
21 | -- Separate subsequent items with linebreaks. This repeated
22 | -- assignment may seem inefficient, but the alternative is
23 | -- adding a conditinal in addition to having an assignment.
24 | set linebreak to character id 10
25 | end repeat
26 | set the clipboard to links
27 | on error msg number err
28 | if err is not -128 then
29 | display alert "DEVONthink error" message msg as warning
30 | end if
31 | end try
32 | end tell
33 |
--------------------------------------------------------------------------------
/devonthink-toolbar-scripts/copy-markdown-links-to-selected-items/Makefile:
--------------------------------------------------------------------------------
1 | ../Makefile
--------------------------------------------------------------------------------
/devonthink-toolbar-scripts/copy-markdown-links-to-selected-items/README.md:
--------------------------------------------------------------------------------
1 | # Copy Markdown links to selected items
2 |
3 | For each item selected in the frontmost window of DEVONthink, this copies to the clipboard a link to the item in Markdown format. The name of the link is the name of the item in DEVONthink, and the link URL has the form `x-devonthink-item://UUID`. For example, if you select a document named "Some document" and run this script, it will create a link that looks something like this:
4 |
5 | ```md
6 | [Some document](x-devonthink-item://6C764-014F-4DDC-B074-054637F5)
7 | ```
8 |
9 | If multiple items are selected in DEVONthink, the links put in the clipboard are separated by line breaks.
10 |
11 | ## Compilation and installation
12 |
13 | The make procedures require the programs [ImageMagick](https://imagemagick.org/index.php) and [fileicon](https://github.com/mklement0/fileicon). Running `make compile` followed by `make install` in this directory will compile the AppleScript file, give it an icon, and copy the results to two locations:
14 |
15 | ```txt
16 | ~/Library/Application Scripts/com.devon-technologies.think3/Menu/
17 | ~/Library/Application Scripts/com.devon-technologies.think3/Toolbar/
18 | ```
19 |
20 | By putting it in both locations, the program becomes accessible from both the _Scripts_ menu item that appears in certain window contexts in DEVONthink, and as individual toolbar items you can add to the toolbar in certain other contexts. (For example, in windows showing single documents like Markdown documents, there is no _Scripts_ toolbar item available, but programs you install in the `~/Library/.../Toolbar/` location become available as individual toolbar items after you restart DEVONthink.)
21 |
22 |
23 | ## About the icon
24 |
25 | The [vector artwork](https://thenounproject.com/icon/share-link-875784/) used as a starting point for the icon created by [Arunkumar](https://thenounproject.com/arun122/) for the [Noun Project](https://thenounproject.com). It is licensed under the Creative Commons [Attribution 3.0 Unported](https://creativecommons.org/licenses/by/3.0/deed.en) license.
26 |
--------------------------------------------------------------------------------
/devonthink-toolbar-scripts/copy-markdown-links-to-selected-items/icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/devonthink-toolbar-scripts/copy-paths-to-files-on-disk/Copy disk paths of selected files.applescript:
--------------------------------------------------------------------------------
1 | -- Summary: copy the absolute file paths of the selected documents.
2 | --
3 | -- Copyright 2024 Michael Hucka.
4 | -- License: MIT License – see file "LICENSE" in the project website.
5 | -- Website: https://github.com/mhucka/devonthink-hacks
6 |
7 | use AppleScript version "2.4" -- Yosemite (10.10) or later
8 | use scripting additions
9 |
10 | tell application id "DNtp"
11 | try
12 | set paths to ""
13 | -- Start with no linebreak for the first time through the loop.
14 | set linebreak to ""
15 | repeat with _record in (selected records)
16 | set _type to (type of _record) as string
17 | -- Skip groups in DEVONthink b/c they don't exist on disk.
18 | if _type is not in {"group", "«constant ****DTgr»", ¬
19 | "smart group", "«constant ****DTsg»"} then
20 | set paths to paths & linebreak & (path of _record)
21 | end if
22 | -- Separate subsequent paths with linebreaks. This repeated
23 | -- assignment may seem inefficient, but the alternative is
24 | -- adding a conditinal in addition to having an assignment.
25 | set linebreak to character id 10
26 | end repeat
27 | set the clipboard to paths
28 | on error msg number err
29 | if err is not -128 then
30 | display alert "DEVONthink error" message msg as warning
31 | end if
32 | end try
33 | end tell
34 |
--------------------------------------------------------------------------------
/devonthink-toolbar-scripts/copy-paths-to-files-on-disk/Makefile:
--------------------------------------------------------------------------------
1 | ../Makefile
--------------------------------------------------------------------------------
/devonthink-toolbar-scripts/create-document-from-template/.graphics/km-shortcut-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhucka/devonthink-hacks/9a4211aa29f9163ce625b9fb9ede2dccdb6546f1/devonthink-toolbar-scripts/create-document-from-template/.graphics/km-shortcut-screenshot.png
--------------------------------------------------------------------------------
/devonthink-toolbar-scripts/create-document-from-template/Create document from template.applescript:
--------------------------------------------------------------------------------
1 | -- Summary: create a doc from a template file, then substitute keywords.
2 | --
3 | -- Copyright 2024 Michael Hucka.
4 | -- License: MIT License – see file "LICENSE" in the project website.
5 | -- Website: https://github.com/mhucka/devonthink-hacks
6 |
7 | -- List of template files -- MUST BE UPDATED MANUALLY ~~~~~~~~~~~~~~~~~~
8 |
9 | set templates to { ¬
10 | "Card.md", ¬
11 | "Code.md", ¬
12 | "Diary.ooutline", ¬
13 | "Empty markdown.md", ¬
14 | "Goal plan.ooutline", ¬
15 | "Markdown.md", ¬
16 | "Meeting.ooutline", ¬
17 | "Notes.ooutline", ¬
18 | "Records markdown.md", ¬
19 | "Spreadsheet.numbers", ¬
20 | "Term definition.md" ¬
21 | }
22 |
23 | -- Helper functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24 |
25 | -- The following code is based on a function posted by "jobu" on 2004-08-11
26 | -- at https://macscripter.net/viewtopic.php?pid=32191--p32191
27 |
28 | on withoutExtension(name)
29 | if name contains "." then
30 | set delim to AppleScript's text item delimiters
31 | set AppleScript's text item delimiters to "."
32 | set basename to (text items 1 through -2 of (name as string) as list) as string
33 | set AppleScript's text item delimiters to delim
34 | return basename
35 | else
36 | return name
37 | end if
38 | end withoutExtension
39 |
40 | on replace(theText, placeholder, value)
41 | if theText contains placeholder then
42 | local od
43 | set {od, text item delimiters of AppleScript} to ¬
44 | {text item delimiters of AppleScript, placeholder}
45 | set theText to text items of theText
46 | set text item delimiters of AppleScript to value
47 | set theText to "" & theText
48 | set text item delimiters of AppleScript to od
49 | end if
50 | return theText
51 | end replace
52 |
53 | -- Main body ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
54 |
55 | try
56 | tell application id "DNtp"
57 | set templateNames to {}
58 | repeat with t from 1 to length of templates
59 | copy my withoutExtension(item t of templates) to the end of templateNames
60 | end repeat
61 |
62 | set userSelection to (choose from list templateNames ¬
63 | with prompt "Template to use for new document:" default items {"Notes"})
64 | if userSelection is false then
65 | error number -128
66 | end if
67 |
68 | set chosenTemplate to first item of userSelection
69 | set prompt to "Name for the new " & chosenTemplate & " document:"
70 | set docName to display name editor "New document" info prompt as string
71 | if docName is false then
72 | error number -128
73 | end if
74 |
75 | set supportDir to path to application support from user domain as text
76 | set templateDir to POSIX path of (supportDir & "DEVONthink 3:Templates.noindex:")
77 |
78 | repeat with n from 1 to (count of templateNames)
79 | if templates's item n starts with chosenTemplate then
80 | set templatePath to (POSIX path of templateDir & templates's item n)
81 | set templateFullName to templates's item n
82 | end if
83 | end repeat
84 |
85 | set theGroup to current group
86 | set groupURL to reference URL of theGroup as string
87 | -- If you don't use the "placeholders" option, DEVONthink will not
88 | -- substitute its built-in placeholders like sortableDate. So, use
89 | -- at least one, just to trigger the built-in replacements.
90 | set newRecord to import templatePath placeholders {|%title%|:docName} to theGroup
91 | set creation date of newRecord to current date
92 | set modification date of newRecord to current date
93 | set the name of newRecord to docName
94 | set filePath to the path of the first item of newRecord
95 | set recordURL to reference URL of newRecord as string
96 | set docUUID to uuid of newRecord
97 | set docRevealURL to recordURL & "?reveal=1"
98 | set docFileName to filename of newRecord
99 |
100 | if templateFullName ends with "ooutline" then
101 | do shell script "/opt/homebrew/bin/ottoman -m -r -i '" & filePath ¬
102 | & "' description=" & recordURL & " subject=" & groupURL
103 | else if templateFullName ends with "md" then
104 | set body to plain text of newRecord
105 | set body to my replace(body, "%UUID%", docUUID)
106 | set body to my replace(body, "%fileName%", docFileName)
107 | set body to my replace(body, "%groupURL%", groupURL)
108 | set body to my replace(body, "%documentURL%", recordURL)
109 | set body to my replace(body, "%documentRevealURL%", docRevealURL)
110 | set body to my replace(body, "%documentName%", docName)
111 | set plain text of newRecord to body
112 | end if
113 |
114 | -- Execute all smart rules as the final step. This ignores the
115 | -- result, because the value is either true/false, and there's no
116 | -- point to showing a dialog that says "a smart rule failed but I
117 | -- can't tell you which one".
118 | perform smart rule record newRecord trigger creation event
119 | end tell
120 | on error error_message number error_number
121 | if the error_number is not -128 then
122 | display alert "DEVONthink error" message error_message as warning
123 | end if
124 | end try
125 |
--------------------------------------------------------------------------------
/devonthink-toolbar-scripts/create-document-from-template/Makefile:
--------------------------------------------------------------------------------
1 | ../Makefile
--------------------------------------------------------------------------------
/devonthink-toolbar-scripts/create-document-from-template/README.md:
--------------------------------------------------------------------------------
1 | Create document from template
2 | =============================
3 |
4 | In my quest to do as much as possible via keyboard shortcuts rather than having to reach for the trackpad, I had previously been using a Keyboard Maestro macro to invoke DEVONthink's _Data_ ➜ _New from Template_ menu item. However, the first thing I always do after creating a new document that way is to rename it, since the items' initial name in DEVONthink is the name of the template file. I had code in the Keyboard Maestro macro to create a name, but the interaction between Keyboard Maestro and DEVONthink was such that sometimes something would go wrong with item selection and keyboard focus, and the name change wouldn't happen. This got annoying enough that I wrote some AppleScript code to create new documents from templates and do the naming all in one go.
5 |
6 | Note that if you wish to use this script, **make sure to adapt it for your DEVONthink installation**. The script uses hardwired values for the template names, as well the name of a Smart Rule that is specific to my DEVONthink installation, and these will need to be changed.
7 |
8 | I use a simple Keyboard Maestro shortcut to invoke this script. (I also compile the script using `osacompile` first.)
9 |
10 |
11 |
12 |
6 |
10 |
6 |
7 |
8 | 9 | Editing annotations is much more conveniently done in a separate window. If a document has an annotation file associated with it, the "Open" command is available in the pull-down menu (as shown above). I wanted to have a keyboard shortcut to open annotation files in separate windows, but unfortunately, DEVONthink does not provide a keyboard shortcut for this command; more unfortunately, you can't target this command via macOS's built-in shortcuts facility, and I haven't found a way to invoke it directly via [Keyboard Maestro](https://www.keyboardmaestro.com/main/). 10 | 11 | Thankfully, it _is_ possible to write a short AppleScript program to tell DEVONthink to open the annotation file in a new window, and this in turn _can_ be set up as an action in [Keyboard Maestro](https://www.keyboardmaestro.com/main/) with a keyboard shortcut. 12 | 13 | The script in this directory tells DEVONthink to open a new window on the annotation file for the currently-selected document. It will also work if multiple documents are selected when it is invoked. 14 | 15 | -------------------------------------------------------------------------------- /obsolete-no-longer-used/open-annotation-file/annotations-drop-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhucka/devonthink-hacks/9a4211aa29f9163ce625b9fb9ede2dccdb6546f1/obsolete-no-longer-used/open-annotation-file/annotations-drop-down.png -------------------------------------------------------------------------------- /obsolete-no-longer-used/open-annotation-file/open-annotation-file.applescript: -------------------------------------------------------------------------------- 1 | -- Summary: open annotation file associated w/ selected document. 2 | -- 3 | -- Copyright 2024 Michael Hucka. 4 | -- License: MIT License – see file "LICENSE" in the project website. 5 | -- Website: https://github.com/mhucka/devonthink-hacks 6 | 7 | tell application id "DNtp" 8 | try 9 | repeat with theRecord in (selection as list) 10 | if (exists annotation of theRecord) then 11 | set annot to get annotation of theRecord 12 | set annotRecord to get record with uuid (get uuid of annot) 13 | open window for record annotRecord with force 14 | else 15 | set rec_name to get name of theRecord 16 | display alert "Document has no annotation" message rec_name 17 | end if 18 | end repeat 19 | on error error_message number error_number 20 | if the error_number is not -128 then 21 | display alert "DEVONthink" message error_message as warning 22 | end if 23 | end try 24 | end tell 25 | --------------------------------------------------------------------------------