Importing notes from Bear
Bear, my note-taking app of choice, stores its notes in a SQLite database at ~
SELECT
*,
datetime(zmodificationdate, 'unixepoch', '31 years')
AS zmodificationdate2
FROM
zsfnote
WHERE
ztrashed == 0 AND zarchived == 0
The zmodificationdate2
derived column takes care of the way timestamps are stored in Bear: these are Core Data timestamps, which are the number of seconds elapsed since January 1st, 2001.
Once I have the query, I can load the notes from the database:
notes = db[query].map do |row|
{
id: row[:ZUNIQUEIDENTIFIER],
title: row[:ZTITLE],
text: row[:ZTEXT],
modification_date: row[:zmodificationdate2]
}
end
I don’t use the note ID directly. The IDs of Bear notes are quite long (longer than UUIDs). I hash the UUIDs, then Avoid naughty words in hex digest strings by changing the set of characters, and finally take the first 15 characters, dash-separated in groups of 5 characters, as the new ID, e.g. 7fmkz-pkwxt-kcxvt
, which I use in note URLs.
Tags and metadata
The Nanoc importer reads the note tags from the second line, below the header. This particular note has the #public
tag:
# Importing notes from Bear
\#public
[[Bear]], my note-taking app of choice, …
To do: My importer doesn’t properly handle # escapes.
Only public notes, i.e. notes with the #public
tag, are published on my web site.
The top of the note can have specific metadata, too. At the moment, only slug
is supported, which is used to give the note a clean URL on my web site:
---
slug: importing-notes-from-bear
---
This yields a note at /notes/importing-notes-from-bear/
.
Importing images
Images live in ~
Where an imagine appears in a note, the note content has To do: This is not correct as of Bear 2.0 anymore.[image:<PATH>]
, with <PATH>
being the path to the image relative to the Note Images
[image:C6BEE843-E18A-4C98-82F0-EFA6499274E2-2641-0000089C7C19B1C6/Screen Shot 2022-10-27 at 21.03.30.png]
With that relative path known, I copy the image into my site’s content
A Nanoc filter then replaces those [image:<PATH>]
markup directives with HTML img
elements. The filter looks like this:
content.gsub(/\[image:([^\]]+)\]/) do |match|
partial_path = Regexp.last_match[1]
relative_path = '/notes/images/' + partial_path
%[<figure><img src="#{relative_path}"></figure>]
end
Adding image metadata
An image can be followed immediately by a code block with YAML metadata:
alt: "A kitten"
wide: true
The importer treats this as metadata for the preceding image. The alt
key specifies the alt text, and wide
is a boolean that determines whether or not the image should also span the sidebar.