Skip to content
KemoNine edited this page Sep 23, 2024 · 2 revisions

Welcome to the pomidor wiki!

Tips by @mcrosson

I setup pomidor to be run in a few different ways and wanted to share the config so others wouldn't have to re-create things from scratch if they have similar needs.

What's Below?

As I've spent time with pomidor, I discovered I needed a couple different ways to work with pomidor as well as a few helper functions and tweaks.

The below config snippets are what I was able to put together for my different use cases and needs.

Full config

If you'd like to see my full pomidor config; I've published the full code here.

Invoke pomidor without resetting existing state

As I worked to setup different ways of interacting with pomidor I noticed that the state can be reset when it's invoked via the (pomidor) function if it's already running.

Since I want only a single instance of pomidor running and no automatic state resets but I do want it invoked it not already running, I setup a function to manage this programatically.

This function also calls (previous-buffer) as I generally don't want pomidor to take over the current, active buffer. Remove this line of code if you are ok with pomidor taking over the current, active buffer.

; safely run pomidor in a buffer programatically
;    doesnt mung or reset state
(defun my-pomidor-run-safe ()
  ; make sure pomidor is running + active w/o resetting the current state
  (when (or (not (boundp 'pomidor-buffer-name))
            (not (get-buffer pomidor-buffer-name)))
    (pomidor)
    (previous-buffer) ; pomidor switches buffers -- go back to orig buffer
  )
)

Buffer cursor positioning

I've been running pomidor in a small, dedicated frame (smaller than the full pomidor output) a lot and the rendering puts the cursor at the end of the buffer (when the frame is active) which can be problematic when I click into the frame to use hot keys like 'hold' and 'un-hold' of the active session.

To make it a bit easier to work with a small frame holding pomidor, I setup a function that's called after pomidor renders to always position the cursor at the start of the buffer. This allows me to always see the timer. If I need to see 'more', I'll use a different way of viewing pomidor. The different methods I use to view pomodor are described below.

; position cursor @ top of pomidor buffer after rendering
(defun my-pomidor-beginning-of-buffer (buffer states)
  (interactive)
  (with-current-buffer (get-buffer pomidor-buffer-name)
    (goto-char (point-min))
  )
)
(advice-add #'pomidor--render :after #'my-pomidor-beginning-of-buffer)

Warmup interval

I have a hard time 'getting into' a task which can be incredibly frustrating as pomodoro timers 'keeps going' even though I'm actively working to get under way with a task and it feels like I'm wasting my session. I don't want this warm up period to 'count against' my focused work time. Instead of trying to monkey patch another state into pomidor, I setup a custom separator so I can tell the 'warmup period' and 'focused work period' apart from each other. This also allows me to get a little extra focused work in if my warm up is complete prior to the timeout.

I will also use this warmup period to peform mini tasks like 'respond to email and chat' to clear small items that may have piled up during prior sessions.

(setq pomidor-warmup-seconds (* 15 60)) ; warmup time
; setup 'warmup period' functionality
(defun my-pomidor-update-hook ()
  (let* ((state (pomidor--current-state))
         (total (pomidor--total-duration state))
         (ellapsed (round (time-to-seconds total))))
      (if (<= ellapsed pomidor-warmup-seconds)
        (setq pomidor-header-separator " 🌡️↑ ")
        (setq pomidor-header-separator "")
      )
  )
)
(add-hook 'pomidor-update-hook #'my-pomidor-update-hook)

More complete emoji for states and visual indication

After using pomidor for awhile I made some additional enhancements to my config related to using the time separator as an additional visual cue. I have notifications and sounds turned off so I went ahead and used the custom separator to indicate the different states that can happen with pomidor.

; setup emoji separators to enhance visual state indicators
(defun my-pomidor-separator-hook ()
  (let* ((state (pomidor--current-state))
         (total (pomidor--total-duration state))
         (elapsed (round (time-to-seconds total))))
      (cond ; watch out for the order of this conditional
            ; there are overlapping states and this order is meaningful
            ((or (pomidor-overwork-p) (pomidor-break-over-p))
                (setq pomidor-header-separator " ⚠️ "))
            ((pomidor-should-long-break-p)
                (setq pomidor-header-separator " 🪁 "))
            ((pomidor--break state)
                (setq pomidor-header-separator " 🚶 "))
            ((<= elapsed pomidor-warmup-seconds)
                (setq pomidor-header-separator " 🌡️↑ "))
            (t (setq pomidor-header-separator " 🏢 "))
      )
  )
)
; add separator logic as std pomidor update hook to ensure the emoji separators get displayed
;     this will be delayed up to the pomidor-update-interval for display
(add-hook 'pomidor-update-hook #'my-pomidor-separator-hook)
; trigger the emoji separator for common operations so it 'activates' faster than via the std pomidor update hook
;     this should show the emoji separators 'faster' than relying on just the update hook
(advice-add #'pomidor-reset :after #'my-pomidor-separator-hook)
(advice-add #'pomidor-stop :after #'my-pomidor-separator-hook)
(advice-add #'pomidor-break :after #'my-pomidor-separator-hook)

; 'hold' visual indicator
(defun my-pomidor-hold-separator ()
  (interactive)
  (setq pomidor-header-separator " 💤 ")
  (pomidor--update)
)
(advice-add #'pomidor-hold :before #'my-pomidor-hold-separator)

; 'unhold' -- reset visual indicator
(defun my-pomidor-unhold-separator ()
  (interactive)
  (my-pomidor-separator-hook)
  (pomidor--update)
)
(advice-add #'pomidor-unhold :before #'my-pomidor-unhold-separator)

Dedicated timer frame

I try to keep the pomidor timer 'always on top' in my GUI environments (Windows 11 in particular). I position it at the bottom, center of my primary monitor (I have multiple monitors). I use this as a kind of overview of the current session with just the timer shown.

I use an external utility (Power Toys - Always On Top in Windows) to mark the frame as always on top of other windows. This is important as I usually have windows overlapping with the pomidor timer frame.

; frame for pomidor, just the main timer text visible
;   centered at bottom of the screen
(defun my-pomidor-frame ()
  (interactive)
  (select-frame (make-frame '(
    (name . "Pomidor")
    (menu-bar-lines 0)
    (tool-bar-lines 0)
    (width . (text-pixels . 818))
    (height . (text-pixels . 144))
  )))
  ; single monitor / main monitor positing
  (let* ((dw (display-pixel-width))
         (dh (display-pixel-height))
         (f  (selected-frame))
         (fw (frame-pixel-width f))
         (fh (frame-pixel-height f))
         (x  (- (/ dw 2) (/ fw 2)))
         (y  (- (- dh fh ) 100)))
    (set-frame-position f x y)
  )
  ; ensure pomidor is running & do /not/ reset state
  (my-pomidor-run-safe)
  ; switch to pomidor buffer
  (switch-to-buffer pomidor-buffer-name)
)
(global-set-key (kbd "C-M-p") 'my-pomidor-frame) ; adjust key binding as appropriate

Pop up window

There are times I want to see 'more' of pomidor than just the timer text and to quickly get status information and/or interact with the buffer. I setup popwin to show the buffer as a sticky pop up window that I can show/hide freely.

This allows me to pop up the pomidor buffer from anywhere within my emacs window layout to see more information than just the current timer and to interact with the pomidor buffer (hold/un-hold in particular).

; pop win pomidor setup
;   main way to invoke
;   ensure pomidor is running before trying to open the buffer
;   use global buffer name for pomidor
;   make the popwin window 'sticky'
;   position sticky popwin @ top
(push '(pomidor-mode :position top :stick t) popwin:special-display-config) ; use major mode as the buffer name won't be set when emacs evaluates this portion of the config if you don't `setq` the buffer name as part of your config
(global-set-key (kbd "C-w p") ; adjust key binding as appropriate
  (lambda () (interactive)
    (my-pomidor-run-safe)
    ; open pomidor in popwin
    (popwin:display-buffer (get-buffer pomidor-buffer-name))
  )
)