Creating Bluebird Themes
Creating themes for Bluebird is easy: all you need is a bit of knowledge of HTML and CSS. For the most part, JavaScript knowledge is not necessary unless you want to do something really crazy with your theme.
Bluebird utilizes the powerful WebKit engine, the same one that powers Safari and Dashboard. As a result, anything you could do on a Safari web page, you can do in a Bluebird theme. In fact, all you need to do to create a Bluebird theme is create a web page, a special Info.plist
file, and put it all in a folder with the .bbtheme
extension.
Getting Started
To get started, create a folder for your theme. It is recommended that this folder share your theme’s name, and required that it have the extension “bbtheme” (for example, the theme named “Torn Paper” is contained in a folder called Torn Paper.bbtheme
). If you have Bluebird installed, this folder should automatically become a “package”, or a folder that’s trained to act as a single file. To get into it from here, Control + Click it, and select “Show Package Contents”. Alternatively, you can just remove the “.bbtheme” extension until you’re ready to test it.
Theme Location
When Bluebird launches, it searches three places for themes:
- Internal Themes directory: (Bluebird.app)/Contents/Themes/
- User Application Support directory: ~/Library/Application Support/Bluebird/
- System Application Support directory: /Library/Application Support/Bluebird/
You should never place your own themes in the internal themes directory, and only sparingly use the System Application Support directory (for themes that you want accessible to every user on your system). That leaves us with the User Application Support directory, so we’ll move our theme there for now.
Info.plist
Bluebird themes contain metadata in an Info.plist
file, the de facto standard for OS X package metadata. Info.plist
is a property list file, which is simply an XML file using Apple’s Property List syntax. If you have the Developer tools (ie, Xcode) installed, you already have an application on your system called Property List Editor, which you can use to help you create these files.
If you don’t have this application, you can create one in any text editor — the basic syntax is shown below:
<?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">
<dict>
<key>BBMainFile</key>
<string>index.html</string>
</dict>
</plist>
Creating Your Theme
Your theme will be comprised of a combination of HTML, CSS, and MGTemplateEngine
instructions, along with a little bit of JavaScript to facilitate the loading of new tweets. Information on MGTemplateEngine
can be found in the package, available at Matt Gemmell’s Cocoa Source website. The basics, however, are available here.
First, design your theme as you would design any normal HTML website. The fun starts when you adapt it for use with Bluebird. There are two primary types of statements that you will use when building your template: syntax statements, and output statements. Command statements look like this, and serve some sort of language functional purpose:
{% for tweet in tweets %}
Output statements look like this, and are replaced by the value of what they contain:
{{tweet.author.fullName}}
The body of your tweets will be contained within an HTML file called tweet.html
(and optionally dm.html
to include a custom template for direct messages). Within your main template file, you then use a for
loop, to loop over the global tweets
array, and print out the rendered HTML for each. IE,
{% for tweet in tweets %}
{{tweet.html}}
{% /for %}
… would go in your main template file, and
<div class="tweet">
{{tweet.content}}
</div>
… would go in a file called tweet.html
.
The above snippet would loop over all the tweets, printing a div
containing the content of each one. You can use conditionals like in most languages with the if
statement. For instance, this is how you might conditionally assign the class of a tweet:
<div class="tweet {% if tweet.isOwn %}self{% else %}{% if tweet.type == BBReply %}reply{% /if %}{% /if %}">
If this is by me, it has the class "self"... if it's a reply,
it has the class "reply".
</div>
Once your theme is designed, you’ll need to add some very basic JavaScript to your theme to allow Bluebird to update the tweets. You will have to implement three JavaScript functions (samples are provided):
<script type="text/javascript">
function $(el) { return document.getElementById(el); }
function addTweet(theTweet, shouldDisplay, nextTweet, isNew, tweetObj)
{
if (!shouldDisplay)
theTweet.style.display = 'none';
$('container').insertBefore(theTweet, nextTweet);
}
function removeTweet(theTweet)
{
$('container').removeChild(theTweet);
}
function changeVisibility(theTweet, shouldDisplay)
{
theTweet.style.display = (shouldDisplay) ? '' : 'none';
}
</script>
Note that the above snippet assumes that the tweets are contained within an HTML element with an ID of “container
” (this can be anything, including a div
or a ul
); it also defines the common JavaScript function $()
for convenience (this is defined for you by many popular JavaScript libraries, like Prototype). These functions should not require modification (except for changing instances of “container
” to the ID of your actual tweet-containing element), but you can modify them to add funky things like animation of new tweets, etc.
The last thing you’ll need to do is setup your theme to notify Bluebird when it’s time to load the list of tweets. This is done by calling the Bluebird.loadTweets()
JavaScript method once your page has loaded. Typically, you’ll just want to insert it into your body
’s onload
attribute like so:
<body onload="Bluebird.loadTweets();">
<!-- Blah blah blah -->
</body>
And voila! That’s all there is to it. You should also note that, if you do want to do anything funky with JavaScript and need to debug, you’re provided with a function called Bluebird.log()
, which outputs whatever you pass it to the Console.
For more information, look through the provided themes, they should give you all the information you need.
Theme API
The Theme API is what you will use to get your theme to interact with Bluebird properly. For this, three mechanisms are provided for you: a set of keys in your Info.plist
file, an API exposed to MGTemplateEngine
, and a special bluebird://
URL scheme. (As a note, Bluebird also responds to twitter://
URLs of the same format, but you should only ever use bluebird://
within the application to ensure that another Twitter client does not attempt to handle your requests).
Info.plist Keys
BBMainFile (string):
This key should contain the name of the main HTML file that makes up your theme. For instance, if you call your theme’s HTML file “index.html”, set this to “index.html”.
BBDMFile (string, optional):
This key should contain the name of the HTML file to use when displaying Direct Messages. It is optional, but if provided, will allow you to display different HTML templates in the tweet and Direct Message views (perhaps in order to conserve screen real estate in the DM view).
BBThemeName (string):
This is the name of your theme (example, “Torn Paper”).
BBThemeArtist (string):
This is the artist of your theme (that’s you!). Just put your name here, unless you have something creative you want to specify.
BBArtistURL (string):
This is the URL to the artist’s website. Credit where credit is due. ;)
BBThemeIdentifier (string):
The “identifier” is a common idiom in OS X development that allows systems to uniquely identify packages. Ordinarily, this is done by using a reverse domain format, followed by the name of the package. For instance, if my website is mattpat.net (we use domain names because they are guaranteed to be unique), and my theme is called “My Theme”, my identifier would probably be “net.mattpat.My Theme”. Technically, this can be anything, as long as you can reasonably guarantee that it will be unique to your theme.
BBThemeVersion (string):
This is the version of your theme. It allows people to distinguish between different copies of the same theme after files have been changed.
BBThemePreviewFile (string):
This is the name of the HTML file that you are using as a preview of your theme; it is displayed in Bluebird’s Preferences window when your theme is selected. It is recommended that you create a sample HTML file with some bogus tweet content in it, and link it to your theme’s stylesheet so it looks as if it were being used in real life. The file will be displayed in a 450x300 WebView, and it will be scrollable.
BBSuppressSmilies (bool, optional):
This key will tell Bluebird to not automatically replace Emoji unicode characterss and common smiley key sequences (ie, “:-)”) with image tags. This allows you to prohibit the use of Emoji and smilies if your theme does not look good with them; generally, though, this is not recommended, because most users expect to see Emoji and smilies automatically displayed correctly.
MGTemplateEngine
API
Execution in a theme begins at a top level, with the tweets
array.
tweets (array):
At the top level, you are given an array called
tweets
. This contains all the tweets that are to be displayed in the current view. You can iterate over these tweets as desired. It is assumed that you will use the form:{% for tweet in tweets %} {{tweet.html}} {% /for %}
From your main template, you iterate over the set of tweets, printing the HTML content of each tweet as it’s encountered with the tweet.html
command.
tweet.html (string):
The rendered HTML for the tweet, taken from the contents of
tweet.html
(ordm.html
, if provided, for direct messages). Note that, while this command shares the name of the file in which your tweet content is contained, it is coincidental; calling{{flarg.html}}
will not process a file calledflarg.html
.You should NEVER use this command inside
tweet.html
(ordm.html
), as it will trigger an infinite loop, a program crash, or a massive implosion of the universe. Seriously, don’t even try it. Only use it from your main template files.
Each object in the tweets
array (referred to here as tweet
) then has a certain set of properties you can use.
tweet.tweetID (string):
The numerical tweet identifier. Use this whenever you need to refer to the tweet in a URL, for instance.
tweet.date (date):
The date and time at which the tweet was posted. This is a date object, and can be formatted using constructs of
MGTemplateEngine
. See theMGTemplateEngine
documentation for more information on this. In general, the format strings are those used byNSDateFormatter
. An example might look like this:{{tweet.date | date_format: "MMMM d, yyyy 'at' h:mm a" }} // prints of form "March 30, 2009 at 3:39 AM"
tweet.type (integer, of
BBStatusUpdate
,BBReply
,BBDirectMessage
):The type of the tweet. The value of the integer maps to top-level constants assigned to the names above. As such, you can use something of the following format to check type:
{% if tweet.type == BBStatusUpdate || tweet.type == BBDirectMessage %} // if status update or direct message {% else %} // is reply {% /if %}
tweet.content (string):
The rendered HTML content of the tweet. This string already has all “@username” references replaced with the appropriate links, as well as all URLs linked to their destinations. In addition, unless suppressed as explained above, this will contain all Emoji Unicode characters and smiley key sequences replaced with image tags as appropriate. Any HTML tags that Bluebird inserts into this content should have classes assigned to them for easy styling; these are explained below in “Classes Assigned by Bluebird”.
tweet.isFavorite (bool):
A boolean value indicating whether or not the user has favorited the tweet.
tweet.isOwn (bool):
A boolean value indicating whether or not the user posted the tweet.
tweet.userURL (string):
The
bluebird://
URL to use when linking to the author of the tweet. Use this in links wherever it would be appropriate for a click to trigger a user action (for instance, if you display the user’s profile image, you might link clicks on that image to this URL; the user’s full name, if displayed, should also link to this URL). Clicks on this link will tell Bluebird to take the appropriate action from that tweet, which may include initiating an @reply, initiating a direct message, opening the user’s profile or website, or presenting the user with a list of choices, depending on the user’s preferences.tweet.replyURL (string):
The
bluebird://
URL to use when creating a “reply” button. Clicks on this link will immediately initiate an @reply to the given tweet. Use this ONLY for specific reply buttons. Do not ever link a user’s name to the reply link; only use the user URL for that. Often, the user URL and reply URL will perform the same resulting action, but the reply URL will always initiate a reply, while the user URL’s action can be customized by the user. Pay close attention to semantics!tweet.retweetURL (string):
The
bluebird://
URL to use when creating a “retweet” button. Clicks on this link will copy the text of the tweet into the tweet entry field, prepended with “RT @username “, where “username” is the Twitter username of the author of the tweet.tweet.sourceClientLink (string):
A pre-formatted HTML link tag to the client that posted the tweet. If a tweet was posted from the Twitter web interface, this will just be “web”, but if it was posted from Bluebird, for example, it will be something like this:
<a href="http://bluebirdapp.com">Bluebird</a>
This link/tag combo is provided directly by Twitter, and not altered by Bluebird. It is not assigned a class of
BBExternalLink
.
In addition to the properties outlined above, each tweet
object also has an author
property, which is an object of type Person
. Each Person
also has a select number of properties that you can use. Note that, for direct messages, a recipient
property of type Person
is also provided; it matches the author
property exactly in syntax.
tweet.author.userID (string):
The numerical user identifier. This is seldom used, but available if necessary.
tweet.author.username (string):
The username of the author of the tweet. Useful for both display purposes, and also forming
bluebird://
URLs.tweet.author.fullName (string):
The full name of the author of the tweet, as set in his or her Twitter profile settings. Used most commonly for display.
tweet.author.description (string):
The tweet author’s description (the mini bio), as set in his or her Twitter profile settings.
tweet.author.imageURL (string):
The URL to the tweet author’s current profile image. Use this in an
img
tag to display the author’s profile image, like so:<img src="{{tweet.author.imageURL}}" alt="{{tweet.author.fullName}}" />
tweet.author.website (string):
The URL to the tweet author’s website. Ordinarily, this should not be used unless creating an explicit “website” button/link; generally, it is more appropriate to use a user link (
tweet.userURL
), and the user can choose whether or not he or she wants those links to open the user’s website.
bluebird://
URL Scheme
bluebird://reply-to/
username
, bluebird://reply-to/username
/tweetID
Initiates a reply to the provided username. If a tweet ID is specified, the reply will be associated directly with that tweet.
bluebird://direct/
username
Initiates a direct message to the provided username.
bluebird://open-link/
anyURL
Opens a provided URL in the default web browser. Used primarily for backwards compatibility. (IE,
bluebird://open-link/http://bluebirdapp.com
would simply openhttp://bluebirdapp.com
).bluebird://user/
username
/{from-status,from-direct}, bluebird://user/username
/{from-status,from-direct}/tweetID
Tells Bluebird to take the user-specified action for whenever a username/user profile image is clicked in the client. The second parameter indicates which action should be taken: if the link is shown in a status update, the “from-status” parameter should be used; if in a direct message, “from-direct” should be used (Bluebird allows users to choose different user actions for status updates and direct messages). If a tweet ID is specified, it will be used if the user chooses to initiate an @reply (it will be associated directly with that tweet).
bluebird://retweet/
tweetID
Initiates a retweet of the specified tweet (by tweet ID). This will tell Bluebird to copy the contents of the specified tweet into the text field, prepended with “RT @username “, where “username” is replaced by the username of the author of the tweet.
Classes Assigned by Bluebird
Bluebird assigns some standard CSS classes to HTML it inserts into the content of tweets.
BBLink
Any links that use either the
bluebird://
ortwitter://
protocols that are inserted into a tweet by Bluebird will have the classBBLink
.BBUsername
Any links that contain an @-prefixed username will contain the class
BBUsername
. Note that this means that username links will often contain the class definition “BBLink BBUsername”.BBExternalLink
Any links that will open in a new window (except links generated by the
tweet.sourceClientLink
command) will contain the classBBExternalLink
so you can style them at will.BBSmiley
Any Emoji or smilies that Bluebird inserts into your tweets will contain the
BBSmiley
class so you can resize and style them at will.
JavaScript Interaction
The specifics and exact syntax of the necessary JavaScript functions and their implementation is detailed in the document A New Theme Format for Bluebird.
Sample Project
A sample project will be provided with a future build of Bluebird. However, one is not currently available.