I’m enjoying logging with Day One right now, and getting geeky with it. To that end, I put this project together during the few breaks I’ve had over the last couple of days leading up to the new Engadget live blog launch today1. The result is a practical proof of concept in the form of a System Service for clipping any text to Day One. I figured that this could actually be really handy for more people than just me, so here it is.

Dayone uses a very simple XML format in separate files to store your entries. It’s easy to replicate the structure using a script, allowing you to directly generating your own Day One entries from any part of your workflow. I’m bypassing the dayone CLI and dropping the updates in directly for added flexibility. Don’t get me wrong, I absolutely love that Day One comes with a CLI, I just wanted to experiment around it.

I’m using the new iCloud support for sync, and this Service is built to work with that folder structure by default. The version below (and in the download) looks in “~/Library/Mobile Documents/” (where iCloud stores documents) for a folder containing the word “dayoneapp.” I don’t know offhand what the bizarre names of the folders indicate, but they differ between accounts, so we have to grep out the matching folder. Assuming your Journal is called Journal_dayone (default) and you’re using iCloud, you shouldn’t have to edit anything. If it doesn’t work, run this in Terminal and copy the resulting path into the “dayonepath” variable:

find ~/Library/Mobile\ Documents/ -name "Journal_dayone"

If you’re using Dropbox, you’ll just need to make a minor alteration to the “dayonepath” variable. Delete the line starting with “dayonedir” and hardcode the Unix (POSIX) path to the “entries” folder, located directly inside of the Journal.dayone bundle in your Dropbox root folder. It will most likely be /Users/[yourusername]/Dropbox/Journal.dayone/entries/ if you haven’t changed any defaults in Dropbox or Day One.

The service itself is available for download at the end of the post, or you can take the script below and home-roll your own in Automator. It should work out of the box, no need for ruby gems or symlinked CLIs.

The only configuration you may want to edit is the “starred” variable. This defaults to off because Services aren’t interactive and you probably don’t want to star every entry you clip. If you do, though, just change “starred = false” to “starred = true” and it will make your calendar look like a planetarium.

The Service will use /usr/bin/textutil to strip out any rich text artifacts. I’m not certain this is necessary, but I found that when clipping rich text I would lose all of my line breaks and indents. This seems to solve it. The script also looks for text with hard breaks and handles them Github-style, preserving breaks using Markdown line break syntax.

If you have Growl running, you’ll get a notification. To be polite, it checks for Growl before attempting any notifications. The check can be slow sometimes, though, so you may want to either remove the check if you always have Growl running, or remove the whole Growl section at the end if you don’t.

The script:

Here’s the script. The same script can be found in the Service download at the end by opening the .workflow file in Automator. You can easily customize/edit from there, so this is posted just for reference.

require 'time'
require 'erb'

def e_sh(str)
	str.to_s.gsub(/(?=[^a-zA-Z0-9_.\/\-\x7F-\xFF\n])/n, '\\').gsub(/\n/, "'\n'").sub(/^$/, "''")

input = STDIN.read
entrytext = %x{echo #{e_sh input}|textutil -stdin -convert txt -stdout}
entrytext.gsub!(/\n([^\n])/,"    \n\\1")
uuid = %x{uuidgen}.gsub(/-/,'').strip
datestamp = Time.now.utc.iso8601
starred = false

dayonedir = %x{ls ~/Library/Mobile\\ Documents/|grep dayoneapp}.strip
dayonepath = "~/Library/Mobile\ Documents/#{dayonedir}/Documents/Journal_dayone/entries/"

template = ERB.new <<-XMLTEMPLATE
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<key>Creation Date</key>
	<date><%= datestamp %></date>
	<key>Entry Text</key>
	<string><![CDATA[<%= entrytext %>]]></string>
	<<%= starred %>/>
	<string><%= uuid %></string>

fh = File.new(File.expand_path(dayonepath+uuid+".doentry"),'w+')
fh.puts template.result(binding)

growl_running = %x{osascript -e 'tell application "system events" to return (count of (every process whose bundle identifier is "com.Growl.GrowlHelperApp")) > 0'}

if growl_running
  %x{osascript -e 'tell application id "com.Growl.GrowlHelperApp" to register as application "Clip to Day One" all notifications {"Clip Complete"} default notifications {"Clip Complete"} icon of application "Day One"'}
  %x{osascript -e 'tell application id "com.Growl.GrowlHelperApp" to notify with name "Clip Complete" title "Log to Day One" description "Clipped to Day One" application name "Clip to Day One"'}

Side note: I mentioned to @brianstucki that this Service might contain the necessary ingredients for an Evernote-to-Day-One solution, but I’ve realized it’s really only the last part of the equation. What you’d probably want to do with most RTF/text notes is export HTML, extract the “created on” meta and parse it into the correct date format, then pipe the rest of the contents through markdownify_cli.php, passing the result to dayone new. Then you’ll have nice, Markdown versions of your notes from Evernote, preserving much of the formatting. I tested this, and it works pretty flawlessly for text notes.


For complete instructions on installing the service (and adding a keyboard shortcut for it), see the how-to posted here.

Clip to Day One Service v1.1

A System Service to clip any text to a Day One journal entry.

Published 11/06/13.

Updated 11/06/13. Changelog

DonateMore info…

  1. If you missed it, you missed out, but it’s going to get even better. You’ll have a ton of opportunities in the next couple of months to check out what is almost certainly–and it’s not just me saying this–the best tool available for live blogging events. I’m so excited, I just can’t hide it…