Capture link from Mac app into org-mode document

To quickly grab a link from an open Mac app and use this link into an org-mode document use the following package:

An optional keybind as follows:

(add-hook 'org-mode-hook (lambda ()
                           (define-key org-mode-map (kbd "C-c g") 'org-mac-grab-link)))

This enables you to quickly grab a link using C-c g

Paste image from clipboard directly into org-mode document

Personally I really like using org-mode files for creating documentation and presentations. When working with screenshots my normal workflows always has been.

  1. Capture screenshot to clipboard
  2. Save the screenshot to a relative ./images folder and give it a descriptive name.
  3. Manually create link to the screenshot

This works ok but after a while you get the feel that the whole process can be automated.

The following function can help in automating the whole process.

  1. Capture screenshot
  2. In your org-document paste using C-M-y (or call my/insert-clipboard-image) and automatically add the screenshot to an =./images- directory and insert the link to the image.
;; Overview
;; --------
;; Inserts an image from the clipboard by prompting the user for a filename.
;; Default extension for the pasted filename is .png

;; A ./images directory will be created relative to the current Org-mode document to store the images.

;; The default name format of the pasted image is:
;; filename: <yyyymmdd>_<hhmmss>_-_<image-filename>.png

;; Important
;; --------
;; This function depends on 'pngpaste' to paste the clipboard image
;; -> $ brew install pngpaste

;; Basic Customization
;; -------------------
;; Include the current Org-mode header as part of the image name.
;; (setq my/insert-clipboard-image-use-headername t)
;; filename: <yyyymmdd>_<hhmmss>_-_<headername>_-_<image-filename>.png

;; Include the buffername as part of the image name.
;; (setq my/insert-clipboard-image-use-buffername t)
;; filename: <yyyymmdd>_<hhmmss>_-_<buffername>_-_<image-filename>.png

;; Full name format
;; filename: <yyyymmdd>_<hhmmss>_-_<buffername>_-_<headername>_-_<image-filename>.png
(defun my/insert-clipboard-image (filename)
  "Inserts an image from the clipboard"
  (interactive "sFilename to paste: ")
  (let ((file
         (concat
          (file-name-directory buffer-file-name)
          "images/"
          (format-time-string "%Y%m%d_%H%M%S_-_")
          (if (bound-and-true-p my/insert-clipboard-image-use-buffername)
              (concat (s-replace "-" "_"
                                 (downcase (file-name-sans-extension (buffer-name)))) "_-_")
            "")
          (if (bound-and-true-p my/insert-clipboard-image-use-headername)
              (concat (s-replace " " "_" (downcase (nth 4 (org-heading-components)))) "_-_")
            "")
          filename ".png")))

    ;; create images directory
    (unless (file-exists-p (file-name-directory file))
      (make-directory (file-name-directory file)))

    ;; paste file from clipboard
    (shell-command (concat "pngpaste " file))
    (insert (concat "[[./images/" (file-name-nondirectory file) "]]"))))

(map! :desc "Insert clipboard image"
      :n "C-M-y" 'my/insert-clipboard-image)

A nice setting in org-mode that also helps when viewing large screenshots is the following:

This display the taken screenshot in a acceptable format in your org-mode file.

(after! org
  (setq org-image-actual-width (/ (display-pixel-width) 2)))

Time tracking with Org Mode and sum time per tag

Tracking time using Org Mode is simple and easy. You can quickly create reports of the time spend on specific tasks. But how do you aggregate time across tasks belonging to tags?

This can be achieved by using a simple formula and the usage of an awesome Org package called Org Aggregate.

Input data

The data below is used for time tracking, note that individual items are tagged!

- Take out the trash :private:
:LOGBOOK:
CLOCK: [2021-03-12 Fri 11:24]--[2021-03-12 Fri 11:30] =>  0:06
:END:
- Update document for client :client1:
:LOGBOOK:
CLOCK: [2021-03-12 Fri 12:45]--[2021-03-12 Fri 13:30] =>  0:45
:END:
- Create my awesome note for work :work:
:LOGBOOK:
CLOCK: [2021-03-13 Sat 11:24]--[2021-03-13 Sat 12:53] =>  1:29
:END:
- Fill in timesheet :work:
:LOGBOOK:
CLOCK: [2021-03-12 Fri 11:24]--[2021-03-12 Fri 11:40] =>  0:16
:END:

Reporting

#+BEGIN: clocktable :scope file :maxlevel 3 :tags t :match "work|client1" :header "#+TBLNAME: timetable\n"
#+TBLNAME: timetable
| Tags    | Headline                              | Time |      |      |     T |
|---------+---------------------------------------+------+------+------+-------|
|         | *Total time*                            | *2:30* |      |      |       |
|---------+---------------------------------------+------+------+------+-------|
|         | Report with filtered tags and sum...  | 2:30 |      |      |       |
|         | \_  Input data                        |      | 2:30 |      |       |
| client1 | \_    Update document for client      |      |      | 0:45 | 00:45 |
| work    | \_    Create my awesome note for work |      |      | 1:29 | 01:29 |
| work    | \_    Fill in timesheet               |      |      | 0:16 | 00:16 |
#+TBLFM: $6='(convert-org-clocktable-time-to-hhmm $5)::@1$6='(format "%s" "T")
#+END:
  • :tags t used to display the tags
  • :match “work|client'= used to filter the tags of interest
  • :header “#+TBLNAME: timetable\n”= used to name our table so we can process it later on using Org Aggregate
  • #+TBLFM: is using a function to correctly display time in hh:mm so we can use it later on to sum. Note: this is required as the package Org Aggregate that we are using to aggregate data is expecting the time in a hh:mm format
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
(defun convert-org-clocktable-time-to-hhmm (time-string)
  "Converts a time string to HH:MM"
  (if (> (length time-string) 0)
      (progn
        (let* ((s (s-replace "*" "" time-string))
               (splits (split-string s ":"))
               (hours (car splits))
               (minutes (car (last splits)))
               )
          (if (= (length hours) 1)
              (format "0%s:%s" hours minutes)
            (format "%s:%s" hours minutes))))
    time-string))

Use Org Aggregate to sum the times of the tags

#+BEGIN: aggregate :table "timetable" :cols "Tags sum(T);U" :cond (not (equal Tags ""))
#+TBLNAME: timetable
| Tags    | sum(T);U |
|---------+----------|
| client1 |    00:45 |
| work    |    01:45 |
#+END:
  • :cond used to filter empty rows from the data input!

Get pretty org-bullets in Doom Emacs

When installing Doom Emacs and using org-mode the defaults bullets are `*`. In order to get some fancy bullets the following steps need to be taken.

  1. Add the org-mode +pretty flag to your org settings in init.el To read more on the available flags check the org-mode Doom Emacs module `lang/org`
:lang
(org +pretty ) ; organize your plain life in plain text
(setq
    org-superstar-headline-bullets-list '("⁖" "◉" "○" "✸" "✿")
)

Ranger - Show File in Path Finder

Ranger is a VIM-inspired filemanager for the console (https://ranger.github.io/) and can easily be installed by using brew install ranger. When working in the terminal sometimes it is nice to open the files in the default Finder app or use the excellent alternative called Path Finder. (https://cocoatech.com/#/)

To reveal your files in the Finder or Path Finder create a commands.py in ~/.config/ranger and paste the following code.

from ranger.api.commands import Command

class show_files_in_path_finder(Command):
    """
    :show_files_in_path_finder

    Present selected files in finder
    """

    def execute(self):
        import subprocess
        files = ",".join(['"{0}" as POSIX file'.format(file.path) for file in self.fm.thistab.get_selection()])
        reveal_script = "tell application \"Path Finder\" to reveal {{{0}}}".format(files)
        activate_script = "tell application \"Path Finder\" to activate"
        script = "osascript -e '{0}' -e '{1}'".format(reveal_script, activate_script)
        self.fm.notify(script)
        subprocess.check_output(["osascript", "-e", reveal_script, "-e", activate_script])

class show_files_in_finder(Command):
    """
    :show_files_in_finder

    Present selected files in finder
    """

    def execute(self):
        import subprocess
        files = ",".join(['"{0}" as POSIX file'.format(file.path) for file in self.fm.thistab.get_selection()])
        reveal_script = "tell application \"Finder\" to reveal {{{0}}}".format(files)
        activate_script = "tell application \"Finder\" to set frontmost to true"
        script = "osascript -e '{0}' -e '{1}'".format(reveal_script, activate_script)
        self.fm.notify(script)
        subprocess.check_output(["osascript", "-e", reveal_script, "-e", activate_script])

Restart Ranger and now you can execute the commands :show_files_in_pathfinder or :show_files_in_finder.