<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Tech - Stuart Jones]]></title><description><![CDATA[Designs & Code]]></description><link>https://stuart-jones.com/</link><image><url>https://stuart-jones.com/favicon.png</url><title>Tech - Stuart Jones</title><link>https://stuart-jones.com/</link></image><generator>Ghost 3.13</generator><lastBuildDate>Tue, 31 Mar 2026 17:19:26 GMT</lastBuildDate><atom:link href="https://stuart-jones.com/tag/tech/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Sous Chef, The Cooking Stream Companion App]]></title><description><![CDATA[Sous Chef is a comprehensive companion application for hosting cooking shows on the Twitch livestreaming platform.]]></description><link>https://stuart-jones.com/blog/sous-chef-the-cooking-stream-companion-app/</link><guid isPermaLink="false">601b28eeac48ec069c96ddda</guid><category><![CDATA[Design]]></category><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Sat, 22 May 2021 19:24:36 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-03-06-at-3.28.11-PM-1.png" medium="image"/><content:encoded><![CDATA[<h2 id="overview">Overview</h2><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/03/Screen-Shot-2021-02-28-at-4.34.24-PM-2.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-03-06-at-3.28.11-PM-1.png" alt="Sous Chef, The Cooking Stream Companion App"><p>Sous Chef is a comprehensive companion app for hosting cooking shows on the Twitch livestreaming platform. It integrates with every aspect of the streaming experience, providing animated video overlays for rendering show graphics, a web-based control panel for switching cameras and managing other show elements, and integration with Twitch's chat functionality for engaging with audiences.</p><p>The application is built upon a variety of open source tools, including Electron, Express, Socket.io, React, Webpack, and Bootstrap. Sous Chef has a modular architecture, allowing pieces to be added or removed depending on the host's particular needs. It was developed in collaboration with <a href="http://twitter.com/andrewcookslive">@AndrewCooksLive</a>, and its code can be found on <a href="https://github.com/imstubtw/sous-chef">GitHub</a>. Icons and graphics were provided by the <a href="https://mutant.tech/">Mutant Standard</a> alternative emoji set.</p><!--kg-card-begin: html-->
<div class="og-main-interior"><!--kg-card-end: html--><h2 id="features">Features</h2><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/03/Screen-Shot-2021-03-06-at-3.28.11-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><!--kg-card-begin: markdown--><ul>
<li>Runs locally as a native application on both macOS and Windows.</li>
<li>Provides a web-based control panel that can be accessed from any computer, tablet, or smartphone on the host computer's LAN.</li>
<li>Includes integration with <a href="https://github.com/Palakis/obs-websocket">OBS-Websocket</a> plugin for software-based scene switching and device muting.</li>
<li>Locally hosts a web overlay which can be integrated into the Open Broadcaster Software (OBS) or Streamlabs OBS. Overlay elements include:
<ul>
<li>Video inlay panels</li>
<li>Stream info panels (Recipe details, etc.)</li>
<li>Multiple simultaneous timers</li>
<li>Image and Tweet embeds (including animated gifs and Twitter videos.)</li>
<li>Probe thermometer temperature charts and live readouts</li>
<li>iTunes integration for displaying background music details.</li>
<li>Full-screen confetti and other celebratory animations</li>
</ul>
</li>
<li>Supports Twitch's chat API. Can post messages to chat, respond to viewer commands, allow select viewers to trigger overlay actions, and engage viewers with automated trivia questions.</li>
<li>Additionally supports Twitch's event APIs, allowing actions to automatically trigger when users perform standard actions such as following or subscribing.</li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h2 id="background">Background</h2><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-09-at-6.31.12-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>This project might take a bit more explaining than usual. I never saw myself as the type of person to co-host a cooking show, but 2020 turned out to be an interesting time for everyone. By now most people are at least passingly familiar with Amazon's livestreaming video platform Twitch. It originally launched as a platform solely dedicated to allowing gamers to livestream their video gaming sessions, but in the years since it launched, they've opened it up to all sorts of non-gaming topics.</p><p>Twitch streams now feature everything from artists painting to live music concerts, or in our case, a homegrown cooking show. Hosting a show on Twitch is a bit like running you own TV show, with a few subtile differences. Most notably, there's a chatroom running alongside every broadcast. Your typical Twitch streamer isn't broadcasting out into the void, but rather interacting with a community of regular viewers. For our stream in particular, <a href="https://twitter.com/andrewcookslive">@AndrewCooksLive</a> handles the bulk of the culinary workload, and I help produce things. (I've been known to occasionally hop in and chop some onions when things get hectic.) It's been a fun way of staying connected to other people during these quarantine times, and stopped us from falling into a culinary rut.</p><p>In addition to having to come up with something new to cook every week, it turns out there is a <em>ton</em> of specialized technology that goes into producing a livestream. Your basic video game stream simply needs a webcam, a microphone, and streaming screen capture software. Your basic cooking stream however typically involves two or three cameras, wireless microphones, streaming software, and usually some additional plugins or programs. That's where Sous Chef comes in. What originally started as a side project to show recipe info and egg timers on our stream has morphed into the glue holding the entire show together.</p><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h2 id="our-setup">Our Setup</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-02-at-4.01.12-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"><figcaption>Can't forget the dog cam!</figcaption></figure><p>While the software's important, I want to touch briefly on the hardware we're working with. Our setup and configurations could be its own specific writeup, but in brief, we're currently using:</p><ul><li>Three Logitech C920 Webcams</li><li>A wireless IP camera</li><li>Two wireless UHF lavalier microphones</li><li>A PC with enough horsepower to handle all of the above</li><li>A kitchen's worth of pots, pans, and accoutrement</li></ul><p>Everything's arranged in the kitchen through a variety of camera arms, tripods, and 3D printed mounts. (It's seriously tricky to hang a camera over a cutting board while still being able to chop efficiently.) Our next big project is to improve the lighting of the show, but for now, it's a pretty solid cooking stream setup for only a few hundred dollars. It also helps that we're currently not entertaining company who might have more than a few things to say about the number of USB cables scattered about our kitchen.</p><p>On the software side of things, you've got one big choice you need to make: Are you going to go with the Open Broadcaster Software's <a href="https://obsproject.com">OBS Studio</a>, or Streamlabs.com's custom <a href="https://streamlabs.com/streamlabs-obs">Streamlabs OBS</a>? OBS Studio is the classic piece of open source software that helped jumpstart the particular streaming revolution that we're currently in, while Streamlabs OBS is a proprietary layer of niceties on top of the open source software to make things run a bit more smoother. For the purposes of Sous Chef, either work. You simply need to be able to create a web browser overlay element, which both support.</p><p>To touch on Streamlabs.com for a moment: Their site touts it as the "All-in-One Live Streaming Software". If this sounds familiar, it's because Sous Chef is essentially a competitor to Streamlabs. Streamlabs can handle onscreen elements, manage chatbots, and even take care of complicated processes like taking money for donation drives. It's essentially the Wordpress of web streaming, if you have any experience in the blog CMS world. Streamlabs.com is the Swiss army knife of live streaming services, while Sous Chef is designed to be more specifically focused on the elements of live streaming that would be useful during a cooking stream. I generally prefer working with OBS Studio to avoid Streamlabs' constant nudging that I pay for a premium subscription, or check out their gallery of paid themes, but I still will occasionally use some of Streamlabs.com's different plugins for non-standard parts of our show. Either way, whichever flavor of OBS you decide to go with, Sous Chef should have something to offer.</p><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h2 id="getting-started-with-sous-chef">Getting Started With Sous Chef</h2><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-12.04.07-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>Sous Chef is structured a bit unusually as far as <a href="https://www.electronjs.org/">Electron</a> applications go. Electron is a NodeJS framework which allows you to create cross-platform desktop applications using standard web technologies like HTML, CSS, and Javascript. In its most simple form, Electron creates a desktop app window, and then wants you to point it at an HTML file, a localhost URL, or an external web URL.</p><p>If you'd like to do something a bit more complicated than serving up a simple index.html file, there are two different approaches. The first is to use the popular <a href="https://create-react-app.dev">Create React App</a> boilerplate project to develop a complex React frontend for your Electron application. The second approach is to create a localhost environment using the <a href="https://expressjs.com/">Express</a> framework to create a robust backend that's also capable of serving the Electron frontend.</p><p>Sous Chef takes advantage of <em>both</em> of these approaches. Express manages a backend API, and serves the Sous Chef control panel to any browser on the local network. Create React App handles the inner workings of the control panel itself, and brings along some niceties for developing a complex React application like module hot-reloading. When working in dev mode, Create React App will host the frontend on its own localhost port using webpack, and Electron and Express will both just reference this instance. When compiling the application, Create React App will compile the application down to an index.html file that the Express instance manages serving.</p><p>So at a high level, the application structure looks something like this:</p><!--kg-card-begin: markdown--><ul>
<li>sous-chef/ (Top-level app folder)
<ul>
<li>plugins/ (Express back-end modules)
<ul>
<li>recipe.js</li>
<li>timer.js</li>
<li>...</li>
</ul>
</li>
<li>public/ (Create React App flat files)
<ul>
<li>favicon.ico</li>
<li>index.html</li>
<li>...</li>
</ul>
</li>
<li>src/ (Create React App React modules)
<ul>
<li>index.css</li>
<li>index.js</li>
<li>Control/
<ul>
<li>Control.js</li>
</ul>
</li>
<li>Overlay/
<ul>
<li>Overlay.js</li>
</ul>
</li>
<li>...</li>
</ul>
</li>
<li>main.js (App entry point, manages Electron &amp; Express configs)</li>
<li>package.json (App configs)</li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><p>Go ahead and run <code>npm install</code>. While npm fetches all the required packages, go ahead and take a look at the package.json file's script section. At the risk of losing you to another bulleted list, you can see the various commands that will finally allow us to finally get this bad boy up and running.</p><!--kg-card-begin: markdown--><ul>
<li>&quot;start&quot;: &quot;cross-env PORT=3000 react-scripts start&quot;
<ul>
<li>Launch the application's Create React App</li>
</ul>
</li>
<li>&quot;electron&quot;: &quot;wait-on tcp:3000 &amp;&amp; electron-forge start&quot;
<ul>
<li>Launch the application's Electron main.js component only after it sees a service running on port 3000.</li>
</ul>
</li>
<li>&quot;dev&quot;: &quot;concurrently -k &quot;BROWSER=none npm start&quot; &quot;npm:electron&quot;&quot;
<ul>
<li>Use concurrently to launch the <code>start</code> and <code>electron</code> scripts at the same time. Supress Create React App's standard browser-opening behavior.</li>
</ul>
</li>
<li>&quot;build&quot;: &quot;cross-env PORT=80 react-scripts build&quot;
<ul>
<li>Build the Create React App instance using port 80.</li>
</ul>
</li>
<li>&quot;make&quot;: &quot;cross-env PORT=80 react-scripts build &amp;&amp; electron-forge make&quot;
<ul>
<li>Build the Electron application using port 80.</li>
</ul>
</li>
<li>&quot;package&quot;: &quot;cross-env PORT=80 react-scripts build &amp;&amp; electron-forge package&quot;
<ul>
<li>Build the Create React App instance using port 80, then make and package the Electron application for distribution.</li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><p>As you can see, these scripts typically build on top of each other. <code>npm run dev</code> launches <code>npm start</code> and <code>npm electron</code> together, while <code>npm run package</code> kicks off <code>npm run build</code> and <code>electron-forge package</code> at the same time. Along with the packages I've already discussed, you've probably noticed that Sous Chef takes advantage of the <a href="https://www.electronforge.io">Electron Forge</a> toolkit to streamline the build and distribution process.</p><p>You're <em>almost</em> ready to get started. There's just one last piece that needs taking care of. Sous Chef uses the <a href="https://dev.twitch.tv/">Twitch</a> and <a href="https://developer.twitter.com/">Twitter</a>  APIs for fetching data and interacting with 3rd party services. You'll need developer API keys for both websites, so head over to their developer sections, register, and create a new dev application. Once you've got Twitch's client ID, go ahead and copy the format in <code>.twitch-keys_example.json</code> to create <code>.twitch-keys.json</code>. Paste your client ID into the new file's json. Do the same for <code>.twitter-key_example.json</code>. You'll need to put your Twitter consumer key, consumer secret, and bearer token into <code>.twitter-key.json</code>. Twitter can take a few days to create a dev account for new users, so go ahead and do this process now if you're thinking about using Sous Chef.</p><p>Alright, that's everything! Go ahead and fire up <code>npm run dev</code>, and you should see Sous Chef start. If everything looks good, you can use <code>npm run package</code> to create a nice compiled application with a single icon to click.</p><h2 id="using-sous-chef">Using Sous Chef</h2><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-03-06-at-3.28.11-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><h3 id="basic-setup">Basic Setup</h3><p>Once you've got OBS up and running, you'll need to integrate its overlay into your OBS scenes. Create a browser overlay element, and make sure it's the same resolution and size as your screen. Set the source to Sous Chef's overlay URL. There's a few options for this, which can be found in the Config tab of Sous Chef. There's the human-friendly <code>http://souschef.local/overlay</code> for system's with Apple's Bonjour multicast DNS service installed, <code>http://[Your IP]/overlay</code> as a fallback for systems without Bonjour, and <code>http://localhost/overlay</code> if you don't need to access Sous Chef on your LAN. If Sous Chef is running in dev mode, make sure to put <code>:3000</code> after the domain.</p><p>If you'd like to create a scene without the video inset panel, add the query string parameter <code>?noInset=true</code> to the end of the URL.</p><p>If you haven't already, go install the <a href="https://github.com/Palakis/obs-websocket">OBS-Websocket</a> plugin. This will allow Sous Chef to remotely control OBS from outside of the OBS application. You may keep OBS-Websocket's settings at their defaults. If you need to change the default port or would like to add authentication, these settings are managed in /plugins/obs.js via the obs-websocket-js package.</p><p>The "Now Playing" iTunes integration does not require any specific configuration. If you're using iTunes on Windows, iTunes on the Mac, or Apple's new Music app on the Mac, Sous Chef will automatically show song information and cover art when a new track starts playing.</p><p>The Sous Chef control panel is divided into three main sections: Cook, Prep, and Config. </p><h3 id="cook-tab">Cook Tab</h3><!--kg-card-begin: markdown--><h4 id="sceneswitchinghotkeys">Scene Switching &amp; Hotkeys</h4>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.57.34-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The top of Sous Chef has your classic scene-switcher interface. The button layout is designed to mimic <a href="https://www.partsnotincluded.com/diy-stream-deck-mini-macro-keyboard/">Dave Madison DIY Streamdeck project</a>. We've got one at the base of the stove, so it's nice to have the same button layout mapped in software as well. It provides a quick interface to move between the stove, cutting board, and kitchen cameras, as well as quick actions such as muting the microphones or toggling confetti.</p><h4 id="active-timer">Active Timer</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.57.25-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The Active Timer panel lists the timers currently being displayed on the overlay. Once a timer expires, the control panel will play a whistle sound effect, and the expired time will begin to flash red. There will also be an attention-grabbing red flashing boarder on the overlay itself. Timers can be canceled or restarted at any time. The scene Switching and Active Timers panels are responsive, so they'll be displayed side-by-side if the control panel is large enough.</p><h4 id="new-timer">New Timer</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.50.10-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The New Timer panel provides an interface for creating new timers. Users can optionally give the timer a unique name, and either provide a specific amount of minutes and seconds, or use one of the provided hotkeys for common timer amounts.</p><h4 id="show-image">Show Image</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.50.19-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>Sometimes a a picture is worth a thousand words, and you'd just like to show your viewers a specific image from somewhere on the web. The Show Image panel allows you to create a pop-up image panel on the overlay software. It accepts an image URL, and provides options for rotating images in the case that OBS's browser element has difficulty reading a camera's rotation EXIF data. The control panel provides a small thumbnail of the currently displayed image so that the producer can have immediate feedback as to whether or not anything is currently being displayed on the overlay.</p><h4 id="show-tweet">Show Tweet</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.50.52-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>Similar to the Show Image panel, the Show Tweet panel allows you to embed a tweet from Twitter.com on the overlay. The embed panel supports Twitter video and Twitter's .gifv videos, allowing you to quickly show off some animated content.  </p><h4 id="probe-thermometer-control">Probe Thermometer Control</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.49.13-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>This section could honestly be a standalone blog post. After a few streams involving frying, we realized that there's not much for the audience to watch while the oil warms up. There's a number of fry recipes that rely on maintaining a particular fry oil temperature, so we decided why not feed two birds with one scone and display our probe thermometer's output on the overlay itself?</p><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Nov-24-2020_23-27-48-3.gif" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>This is of course easier said than done. We'd been using the reliable <a href="https://www.thermoworks.com/ChefAlarm">Thermoworks Chef Alarm</a>, but it doesn't provide any Bluetooth or Wifi connectivity that we could piggyback off of for the overlay. Rather then investing in a new thermometer, I decided to build a small probe-thermometer-to-PC adapter using an Arduino-based microcontroller. The Arduino reads the electrical resistance of the probe thermometer, passes it through the <a href="https://github.com/giannivh/SmoothThermistor">Steinhart–Hart equation</a> to determine how hot the probe is, and then sends the data to the PC with a basic USB serial signal. There's even a small seven-segment display built in for some immediate feedback.</p><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Nov-24-2020_23-09-21-2.gif" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The Probe Control panel in Sous Chef lets you control what you'd like to do with this thermometer data. Once you selected the proper serial port, you can either display the current temperature, or you can show a live-updating plot of the data with a Chart.js graph. There's also the ability to set an alarm to go off when a target temperature is reached.</p><h4 id="toggles">Toggles</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.51.00-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>Alright, this one's a bit more straightforward. There's a few less-frequently used overlay elements such as confetti or an announcement banner. This panel does what it says on the tin, and allows you to toggle these on and off.</p><h4 id="remote-producers">Remote Producers</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.51.08-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>Part of streaming is letting your audience get in on the fun. A few of the overlay's elements have been tied to chat commands like <code>!cuttingboard</code> or <code>!confetti</code>. The Remote Producers panel allows you to grant permissions to specific users to take over producing responsibilities for the stream.</p><h3 id="prep-tab">Prep Tab</h3><h4 id="recipe-drink">Recipe &amp; Drink</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.51.16-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The Recipe and Drink panels control a small overlay element which let people know what's on the menu for the evening, and what cooking beer you're enjoying during the stream. The URL section controls a small chat bot which responds to !recipe or !drink.</p><h4 id="reminders-bot">Reminders Bot</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.51.25-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The Reminders Bot is a Twitch chatbot which posts specific messages at a regular interval. This is usually used to remind people of ongoing charity drives and other events.</p><h4 id="trivia-bot">Trivia Bot</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.51.51-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>We've been experimenting with a Trivia Bot recently as a way to boost viewer engagement. It's currently managed through a small trivia.json file stored within Sous Chef itself. The trivia bot will post a message at a regular interval, allowing people to score points for various stream prizes.</p><h3 id="config-tab">Config Tab</h3><h4 id="twitch-integration">Twitch Integration</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.52.00-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The aforementioned Twitch integration is controlled through Twitch's OAuth API. Sous Chef requires two separate logins. Collecting stream event data requires the broadcaster to log in, while posting chat bot messages requires the specific chat bot account to login.</p><h4 id="obs-integration">OBS Integration</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.52.05-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The camera switching components rely on OBS's websocket plugin. The connection kicks in automatically when Sous Chef launches, but its status can be double-checked here if something's gone wrong.</p><h4 id="sous-chef-information">Sous Chef Information</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.52.12-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The application has two main web endpoints: The control panel interface, and the overlay itself. The Sous Chef Information panel provides links to these pages in a variety of forms for testing purposes. The most user-friendly of these links is the souschef.local Bonjour networking (mDNS) URL. If the client machine does not have Bonjour installed however, a direct IP option may be used as well.</p><h1 id="closing-thoughts">Closing Thoughts</h1><p>Sous Chef has been a fun quarantine project. It's helped me learn a lot about NodeJs, React, and integrating with 3rd party services and clients. The app is currently fine-tuned to our cooking stream's specific needs, but if it looks like something you would be interested in using, don't hesitate to reach out to me with ideas. All of the code is up on my <a href="https://github.com/imstubtw/sous-chef">Github</a> for forking as well.</p></div>]]></content:encoded></item><item><title><![CDATA[Internet Famous, An Open-Source Rendition of a Party Game Classic]]></title><description><![CDATA[Internet Famous is a digital version of the classic Celebrities or Hat Game party game. More specifically, it's an open-source version of Alex Hague and Justin Vickers' excellent Monikers card game.]]></description><link>https://stuart-jones.com/blog/internet-famous-an-open-source-rendition-of-a-party-game-classic/</link><guid isPermaLink="false">5e913bf4017a114be0c96063</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Wed, 18 Jul 2018 22:14:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/Internet-Famous-2.png" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Internet-Famous-2-1.png" class="kg-image" alt="Internet Famous, An Open-Source Rendition of a Party Game Classic"></figure><!--kg-card-begin: markdown--><img src="https://stuart-jones.com/content/images/2020/04/Internet-Famous-2.png" alt="Internet Famous, An Open-Source Rendition of a Party Game Classic"><p><a href="https://stuart-jones.com/internet-famous/">Internet Famous</a> is a digital version of the classic Celebrities or Hat Game party game. More specifically, it's an open-source version of Alex Hague and Justin Vickers' <a href="https://github.com/ImStuBTW/internet-famous/blob/master/Monikers">Monikers</a>. The game itself is pretty straightforward. You spit your group into two teams, and have 60 seconds to get your team to guess as many weird, quirky card titles as possible. In the first round, you can say anything you want to. In the second round, you can only say a single word. In the third round, you can't say a thing, and play charades to get your friends to guess the card. The catch is that reach round is played with the same deck of cards, meaning that there's a pretty good chance you'll come up with some good in-jokes by the time you make it to the last round.</p>
<h2 id="howtoplay">How to Play</h2>
<p>Skip the boring compiling and building stuff, just point your web browser at <a href="https://stuart-jones.com/internet-famous/">/internet-famous/</a></p>
<h2 id="howtobuild">How to Build</h2>
<p>Oh, you actually want to do the compiling and building stuff. Internet Famous is built on the React JavaScript framework. It uses Redux as a state container, and Express as a simple webhost. The app doesn't have any logins or persistent state, which means it's pretty simple to build. The package.json file contains all of the NPM packages you'll need, and webpack puts everything together. If you'd like to run the game locally in development mode, copy this repository and run npm start -s to get a local sever running. If you'd rather build the game for production, run npm run build, and upload the resulting Node app to your webserver of chioce.</p>
<h2 id="disclaimerslicensing">Disclaimers &amp; Licensing</h2>
<p>Emoji art assets provided by EmojiOne 2.3, a Creative Commons BY 4.0 project. React-friendly SVG library courtesy of wilhearts' <a href="https://www.npmjs.com/package/react-svg-emojione">https://www.npmjs.com/package/react-svg-emojione</a> NPM package.</p>
<p>Internet Famous is a Creative Commons BY-NC-SA 4.0 derivative of the card game Monikers. The name Monikers is a registered trademarked of Palm Court LLC. Internet Famous is unaffiliated with Monikers and Palm Court LLC. If you enjoy Internet Famous, why not pick up a physical copy of their game at <a href="http://www.monikersgame.com">http://www.monikersgame.com</a>?</p>
<p>This work is available under the MIT and Creative Commons BY-NC-SA 4.0 licenses.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[BitBot - A BitBucket Push Bot for Discord and Slack]]></title><description><![CDATA[BitBot is a small Node script I created for sending commit messages from BitBucket pushes to the Discord or Slack room of your choice. BitBot takes advantage of BitBucket's outgoing webhooks, and Discord/Slack's incoming webhooks.]]></description><link>https://stuart-jones.com/blog/bitbot-a-bitbucket-push-bot-for-discord-and-slack/</link><guid isPermaLink="false">5e913bf4017a114be0c96060</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Mon, 06 Nov 2017 16:52:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2018-01-03-at-10.57.23-AM-2-1.png" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2018-01-03-at-10.57.23-AM-2.png" class="kg-image" alt="BitBot - A BitBucket Push Bot for Discord and Slack"></figure><!--kg-card-begin: markdown--><img src="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2018-01-03-at-10.57.23-AM-2-1.png" alt="BitBot - A BitBucket Push Bot for Discord and Slack"><p>While Github is all the rage, BitBucket is still a popular alternative for code repositories. A gamedev buddy <a href="https://twitter.com/redvonix">@RedVonix</a> has been working on an upcoming title, and wanted to automate BitBucket commit notifications to his Discord chat. This is built into Discord and Slack if you're using GitHub, but Bitbucket took a little extra finagling. BitBot is a small Node script I created for sending commit messages from BitBucket pushes to the Discord or Slack room of your choice. BitBot takes advantage of BitBucket's outgoing webhooks, and Discord/Slack's incoming webhooks. You'll need your own server running NodeJS in order to receive and pass on the messages. The source can be found on my <a href="https://github.com/ImStuBTW/bitbot">GitHub page</a>.</p>
<h2 id="configurediscordwebhooks">Configure Discord Webhooks</h2>
<p>Navigate to the channel of the Discord server you would like BitBot to post messages in. Click the gear icon next to the channel name. (Note: You will need admin privileges on the server to configure webhooks and use BitBot.) In the channel settings window, click 'Webhooks' on the left hand side of the screen. Click 'Create Webhook'. Enter a bot name, confirm the channel, and optionally upload an icon. Copy the Webhook URL, and click save.</p>
<h2 id="configureslackwebhooks">Configure Slack Webhooks</h2>
<p>Navigate to the channel of the Slack server you would like BitBot to post messages in. Click the gear icon on the top of the page. Select 'Add an app' from the dropdown menu. Your Slack channel's app manager will appear in a new browser window. (Note: You will need admin privileges on the server to configure webhooks and use BitBot.) Click 'Manage' in the top right of the browser window. Select 'Custom Integrations' from the left side of the page. Choose 'Incoming Webhooks', and then 'Add Configuration'. Slack will ask you which channel you would like the bot to post into. Click 'Add Incoming Webhook Integration'. On the next page, you can configure your bot further. Copy the Webhook URL.</p>
<h2 id="configurebitbot">Configure BitBot</h2>
<p>Clone this repository to an empty public folder or subdomain on a server running Node. Edit the config.js file. Set the name to whatever you would like BitBot's display name in the chat to be. Choose a port, and paste in at least one Webhook URL from the previous steps. BitBot can post to both Discord and Slack, or just one of these services. If you are not using a service, just leave the single quotes blank.</p>
<h2 id="buildandrunbitbot">Build and Run BitBot</h2>
<p>Run an <code>npm init</code> command to download the required npm packages. Run <code>npm start</code> to begin running BitBot.</p>
<h2 id="configurebitbucket">Configure BitBucket</h2>
<p>Finally, browse to your BitBucket repository. Click the 'Settings' option and choose 'Webhooks' from the settings menu. Click 'Add webhook'. Give your webhook a title, and paste in the server URL where you installed BitBot. Make sure 'Active' is checked. Check the 'Skip certificate verification' checkbox if your server is not running SSL. Make sure 'Triggers' is set to 'Repository push'.</p>
<p>Congratulations, BitBot should now be up and running.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Undercast - A Mac & Windows Desktop App for Overcast with Media Key Support]]></title><description><![CDATA[Undercast is a small third-party desktop applet for Marco Arment's popular iOS podcatching app Overcast.]]></description><link>https://stuart-jones.com/blog/undercast-a-mac-windows-desktop-app-for-overcast-with-media-key-support/</link><guid isPermaLink="false">5e913bf4017a114be0c9605d</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Wed, 21 Dec 2016 21:33:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/screenshot-5-2-1.png" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/screenshot-5-2.png" class="kg-image" alt="Undercast - A Mac & Windows Desktop App for Overcast with Media Key Support"></figure><!--kg-card-begin: markdown--><img src="https://stuart-jones.com/content/images/2020/04/screenshot-5-2-1.png" alt="Undercast - A Mac & Windows Desktop App for Overcast with Media Key Support"><p>Undercast is a small third-party desktop applet for <a href="https://marco.org/">Marco Arment's</a> popular iOS podcatching app <a href="https://overcast.fm/">Overcast</a>. This project can also be found on <a href="https://github.com/ImStuBTW/undercast">Github</a>.</p>
<h1 id="whatsundercastfor">What's Undercast For?</h1>
<p>Overcast has become a popular alternative to Apple's first-party Podcasts app. It's simple and clean interface makes it easy to access your shows, and it has a few nice power features like voice boost and smart speed. Overcast also has a syncing service to manage your subscriptions and playback progression across devices, but it unfortunately lacks an official Mac or Windows client. Overcast <em>does</em> sync your data with <a href="https://overcast.fm/">Overcast.fm</a> however. <a href="https://overcast.fm/">Overcast.fm</a> lets you access your subscriptions, and offers a basic web-based player for listening to your shows.</p>
<p>On it's own, <a href="https://overcast.fm/">Overcast.fm</a> is a good solution for accessing your podcasts on the desktop when in a pinch. It lives in one of your browser tabs however, so there's a chance you can accidentally close it, and more critically, it lacks support for the ubiquitous keyboard media keys.</p>
<p>Undercast tries to make up for these shortcomings. Rather than writing an entirely new podcasting client that highjacks Overcast's syncing service, Undercast simply wraps the <a href="https://overcast.fm/">Overcast.fm</a> web player inside of a desktop applet that lives in your menubar or system tray.</p>
<h1 id="features">Features</h1>
<ul>
<li>Undercast lives in your menubar or system tray.
<ul>
<li>Click on it to pull up the Overcast window, or right click on it to access the options or quit.</li>
</ul>
</li>
<li>Media Key Support. Undercast supports keyboard media keys on both Mac and Windows.
<ul>
<li>Play keys will play and pause your podcast. Stop keys will pause the currently playing podcast. Forward/Rewind keys will jump your podcast forward or back by a few seconds.</li>
<li>The amount of seconds Undercast skips is determined by a setting on the iOS app. In Overcast, tap on the Overcast icon on the top left of the main screen, then go into the &quot;Nitpicky Details&quot; menu, then change &quot;Seek Back By&quot; and &quot;Seek Forward By&quot; to your desired values.</li>
<li>By default, Undercast will attempt to bind your system's global media keys to it. If you would like to free up control of your system's media keys to another application, right click on the icon and deselect &quot;Bind Media Keys&quot;.</li>
</ul>
</li>
<li>Playback Indicator
<ul>
<li>By default, Undercast's icon changes from white to orange when a podcast is playing. This setting can be disabled in the right click menu.</li>
</ul>
</li>
</ul>
<h1 id="howundercastworks">How Undercast Works</h1>
<p>Undercast uses a technology called <a href="http://electron.atom.io">Electron</a>. It was developed by Github for use with their <a href="http://atom.io">Atom</a> text editor, but has become a generalized tool for creating cross platform applications using web technologies like HTML, CSS, and Javascript.</p>
<p>Using Electron, Undercast creates a sandboxed instance of <a href="https://overcast.fm/">Overcast.fm</a>'s website. As far as the website is concerned, it's just been opened in a standard browser instance. Undercast then injects a small javascript API into the Overcast instance to enable media key controls. The menubar aspects of Electron are handled by <a href="https://github.com/maxogden">maxogden</a>'s <a href="https://github.com/maxogden/menubar">menubar</a> package.</p>
<h1 id="gettingundercast">Getting Undercast</h1>
<p>Precompiled builds of Undercast are located in the <code>/releases</code> folder. Simply unzip them and launch the .app or .exe file.</p>
<p><a href="https://github.com/ImStuBTW/undercast/raw/master/releases/Undercast.app.zip">Current Mac Release</a></p>
<p><a href="https://github.com/ImStuBTW/undercast/raw/master/releases/undercast-win32-x64.zip">Current Windows Release</a></p>
<h1 id="buildingityourself">Building It Yourself</h1>
<p>Undercast is a standard straightforward Electron application. Run <code>npm install</code> to download the necessary Node packages, then run <code>./node_modules/.bin/electron .</code> to launch the application. Compiling the application for production can be done with the <code>electron-packager</code> npm module.</p>
<h1 id="disclaimer">Disclaimer</h1>
<p>Undercast is not an official Overcast application. It has no relation to Marco Arment, and offers no guarantee that it will continue working in the future. Undercast is a free open source project available under the MIT license.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Chrono, the Cron-to-English Cron Job Builder]]></title><description><![CDATA[Chrono is a small Cron-To-English web app to help you set reoccurring system tasks on Linux machines.]]></description><link>https://stuart-jones.com/blog/the-cron-to-english-cron-job-builder/</link><guid isPermaLink="false">5e913bf4017a114be0c9605b</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Wed, 16 Nov 2016 22:10:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2020-04-10-at-11.38.00-PM-1.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><iframe src="https://stuart-jones.com/chrono/" width="100%" height="500px" scrolling="yes" frameborder="0"></iframe>
<img src="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2020-04-10-at-11.38.00-PM-1.png" alt="Chrono, the Cron-to-English Cron Job Builder"><p>Cron is a popular reoccurring task scheduler for Unix-based computer systems. The concept is relatively simple: Create a .crontab file with your scheduled events, and an omnipresent system daemon executes the command at the correct time. Cron has been around since the 1970s, so the crontab's file format has seen plenty of use outside of Cron itself. Being such an old format, Cron can take a while to completely wrap your head around. Set your brackets and braces aside, Cron works completely off of whitespace and newline characters.</p>
<p>Here's an example of your typical .crontab file:</p>
<pre><code class="language-nohighlight">30 08 10 06 * /home/john/full-backup
00 11,16 * * * /home/john/bin/incremental-backup
00 09-18 * * * /home/jane/check-db-status
</code></pre>
<p>The first five entries designate the times the job should run, and the last entry is the command that should be run. Cron's manfile gives a brief rundown of what which number signifies:</p>
<pre><code class="language-nohighlight"># ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday;
# │ │ │ │ │                                       7 is also Sunday on some systems)
# │ │ │ │ │
# │ │ │ │ │
# * * * * *  command to execute
</code></pre>
<p>In addition to numbers, Cron also accepts * wildcards, and will also allow ranges of values (1-5) and comma separated lists of values (1,3,6,8).</p>
<p>Some cron jobs are easier to build than others. It's fairly easy to determine that <code>0 0 * * *</code> means midnight every night, but it's less evident that <code>0 0 * 1 0</code> means midnight on every Monday in January. To double-check their work, programmers often turn to Cron-to-English tools to ensure their task will run at the correct interval.</p>
<p>I've been learning <a href="https://facebook.github.io/react/">React</a> recently, and realized that a such a converter would be a nice way to test the React skills I had learned without needing to code an extensive backend. The end result was <a href="https://stuart-jones.com/chrono">Chrono</a>, a simple Cron-to-English Cron Job Builder.</p>
<p>Chrono takes a standard 5 field Cron job, validates the entries, and then converts it to English. It also provides a field-by-field explanation of valid values. It can be used in the embed above, or viewed directly using the link.</p>
<p>Building Chrono was relatively straightforward. It uses the latest version of <a href="https://facebook.github.io/react/">React</a> (15.3), and the ES2015 and JSX javascript standards. I used the <a href="http://gulpjs.com">Gulp task runner</a> to convert the ES2015 scripts to standard ES5 javascript using the <a href="http://babeljs.io">Babel polyfill</a>. For styling the page, I used the <a href="http://getbootstrap.com">Bootstrap</a> framework and the <a href="http://sass-lang.com">Sass</a> CSS Preprocessor. For converting the cron jobs to plain English, I used azza-bazoo's <a href="https://www.npmjs.com/package/prettycron">PrettyCron</a> NPM package. This package was converted for use with React by <a href="http://browserify.org">Browserify</a>. Finally, the validation of the various cron fields was done by this <a href="http://stackoverflow.com/a/21558119">lengthy RegEx</a>. The code for Chrono can be found on my <a href="https://github.com/ImStuBTW/chrono">Github repository</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Messaging UI Concept: Bruno]]></title><description><![CDATA[Bruno is a conversational UI concept that lives completely in your web browser. It loosely mimics the iOS Messages interface. It supports image attachments, gifs, emoji, and links. Rather than allowing full text entry, Bruno allows the user to choose from a fixed set of replies. ]]></description><link>https://stuart-jones.com/blog/messaging-ui-concept-bruno/</link><guid isPermaLink="false">5e913bf4017a114be0c96058</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Wed, 04 May 2016 22:15:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2020-04-21-at-9.11.01-AM.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><iframe src="https://stuart-jones.com/bruno" width="100%" height="800px" scrolling="yes" frameborder="0"></iframe>
<img src="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2020-04-21-at-9.11.01-AM.png" alt="Messaging UI Concept: Bruno"><p>Using computers to message each other isn't exactly a new concept. From the dawn of networked machines, we've been using a variety of tools to communicate with each other. Be it email, IRC, IM, SMS, or one of the relative newcomers like social networks, group messaging apps, and persistent team chats like Slack, we've gotten pretty good at letting each other know what we're up to.</p>
<p>One newer wrinkle in this paradigm is human-to-machine messaging. Rather than using a messaging app to talk to another human being, your messages are being sent to a fully or partially automated computer script. These services can vary in complexity, ranging from something as primitive as a phone tree, or scaling all the way up to natural language processing with something like <a href="http://www.ibm.com/smarterplanet/us/en/ibmwatson/">IBM's Watson</a>.</p>
<p>These new services have started to be colloquially referred to as &quot;Bots&quot;. Services like Siri and Slack have gotten people used to talking to machines, and so designers have begun to focus more attention on these new &quot;Conversational UIs&quot;. There have already been a few interesting projects to come out of this new focus, such as the <a href="https://itunes.apple.com/us/app/lifeline.../id982354972">Lifeline</a> mobile game, or <a href="http://qz.com/613700/its-here-quartzs-first-news-app-for-iphone/">Quart's new iOS news app</a>.</p>
<p>That's where Bruno comes in.</p>
<h2 id="bruno">Bruno</h2>
<p>Bruno is a conversational UI concept that lives completely in your web browser. It loosely mimics the iOS Messages interface. It supports image attachments, gifs, emoji, and links. Rather than allowing full text entry, Bruno allows the user to choose from a fixed set of replies.</p>
<p>This UI mockup was a chance for me to learn a variety of newer web technologies, it probably won't work if your browser doesn't suppose <a href="https://babeljs.io/docs/learn-es2015/">ES2015</a>. Bruno uses <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations">CSS3 Animations</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a> for flow control, <a href="http://emojione.com">Emoji One</a> for cross platform emoji support, and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Using_CSS_flexible_boxes">Flexboxes</a> to keep everything nice and centered. The messages are currently a fixed script of <a href="https://jquery.com">JQuery</a>-parsed .json files, but he was designed modularly so that a more complicated message source could be implemented later.</p>
<p>I just referred to my program as a 'he'. The future is weird.</p>
<p>If you'd like to try out Bruno, feel free to use the embed below, or view it full screen <a href="https://stuart-jones.com/bruno">here</a>. His- err, the page's source is also up on <a href="http://github.com/ImStuBTW/bruno">Github</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Some Old Projects]]></title><description><![CDATA[I've already highlighted a few of my favorite projects on this site, but I wanted to take a minute to post about some of my older college works. Enjoy.]]></description><link>https://stuart-jones.com/blog/some-old-projects/</link><guid isPermaLink="false">5e913bf4017a114be0c96057</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Wed, 04 May 2016 20:24:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/IMG_0129-2.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://stuart-jones.com/content/images/2020/04/IMG_0129-2.jpg" alt="Some Old Projects"><p>I've already highlighted a few of my favorite projects on this site, but I wanted to take a minute to post about some of my older college works. Enjoy.</p>
<h2 id="piot">PiOT</h2>
<!--kg-card-end: markdown--><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://stuart-jones.com/content/images/2020/04/PiOT-1.jpg" width="3264" height="2448" alt="Some Old Projects"></div><div class="kg-gallery-image"><img src="https://stuart-jones.com/content/images/2020/04/Pebble.jpg" width="2448" height="3264" alt="Some Old Projects"></div></div></div></figure><!--kg-card-begin: markdown--><p>It's an <em><strong>I</strong></em>nternet <em><strong>O</strong></em>f <em><strong>T</strong></em>hings sensor based off of a Raspberry <em>Pi</em>. Get it?</p>
<p>The PiOT is a data capture device for a do-it-yourself IoT setup. It's equipped with a pair of DS18B20 temperature sensors, a DHT22 combination  temperature/humity sensor, and an optional Raspberry Pi camera.</p>
<p>The sensors are read and logged on a five minute interval. This data is uploaded to Sparkfun's <a href="https://stuart-jones.com/blog/some-old-projects/data.sparkfun.com">data.sparkfun.com</a> data logging service, or a personal hosted instance of the underlying <a href="http://phant.io/">Phant</a> service. From there, data.sparkfun.com and Phant can serve up .json files to a variety of endpoints, including a web dashboard, mobile app, or even a smartwatch.</p>
<p>It was a fun project combining several of my favorite technologies. I got to solder a sensor together, write some Python scripts for the sensors, design a Chart.js and Bootstrap web dashboard, and even learn how to write <a href="http://pebble.com">Pebble</a> smartwatch faces.</p>
<h1 id="movieknight">Movie Knight</h1>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Movie-Knight.png" class="kg-image" alt="Some Old Projects"></figure><!--kg-card-begin: markdown--><p>Movie Knight was my senior project. Working with a group of classmates, we followed standard Agile and SCRUM methodologies to create an 2.3 Android-based movie recommendation app.</p>
<p>The app was a least-grief recommendation service for groups. You and your friends would enter your movie collections into the app, giving each film a 5-star (with half increment) rating. Then, you would specify which users were attempting to watch a movie together, and the app would recommend movies that best fit the entire group's tastes.</p>
<p>I handled the majority of the work writing the Android app. Another classmate handled the database logic, and finally our final group member wrote the recommendation engine. It was a fun project, and gave a good amount of insight into mobile app development.</p>
<h1 id="diceroller">Dice Roller</h1>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Dice-Roller.png" class="kg-image" alt="Some Old Projects"></figure><!--kg-card-begin: markdown--><p>This one might take a bit of explaining. My school had a Computer Human Interaction course. It was a fascinating class that taught Computer Scientists to use Humanities-style skills in order to study users more efficiently. As part of the class, we had to study a group of people in a culture we didn't belong to. These groups ranged from student athletes to bingo hall regulars. I had never played a tabletop RPG before, so I decided to study my friends during their weekly Dungeons and Dragons game.</p>
<p>I got to hang out with friends for homework, how cool is that?</p>
<p>The final project of the class was open ended. All we had to do was to create something that would benefit the group we had studied. I had recently been teaching myself the Arduino platform, so I decided to put those skills to good use.</p>
<p>I ended up creating an electronic dice roller. Tabletop games like Dungeons and Dragons often require users to roll all sorts of non-standard dice. The device I created could simulate any number of dice rolls, and automatically integrate a player's skill modifiers into the final result. It even had a motion sensor in it so that you could mimic the sensation of rolling dice.</p>
<p>My favorite part of the project was the enclosure. I used <a href="http://ponoko.com">Ponoko.com</a> to laser-cut a custom enclosure I had created in the 3D modeling software <a href="http://www.sketchup.com">Sketchup</a>. It definitely helped turn heads during the final presentation.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[The When and Whys of the Bootstrap Grid]]></title><description><![CDATA[Bootstrap 3's documentation is good at showing you how to use its grid system, but it's not that great at telling you when or why you might want to use some of the grid system's various features. This abridged guide is going to try and help fill in some of those gaps.]]></description><link>https://stuart-jones.com/blog/the-when-and-whys-of-the-bootstrap-grid/</link><guid isPermaLink="false">5e913bf4017a114be0c96056</guid><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Sat, 16 Apr 2016 17:25:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2020-04-10-at-11.31.59-PM-1.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2020-04-10-at-11.31.59-PM-1.png" alt="The When and Whys of the Bootstrap Grid"><p>Bootstrap 3's documentation is good at showing you <em>how</em> to use its grid system, but it's not that great at telling you when or why you might want to use some of the grid system's various features. This abridged guide is going to try and help fill in some of those gaps, and point out some beginner pitfalls of the Bootstrap grid.</p>
<h2 id="wrappers">Wrappers</h2>
<p>Before we can get started discussing the grid itself, we first need to briefly go over some of Bootstrap's wrapper elements. Bootstrap offers two primary page wrappers <code>.container</code> and <code>.container-fluid</code>. <code>.container</code> has a fixed width, while <code>.container-fluid</code> always take up the full width of the page. Bootstrap expects one of these two divs to be wrapped around your page's primary content. The grid system won't work without it.</p>
<p>One of the common pitfalls with Bootstrap's <code>.container</code> is not knowing when to break them up into separate elements. It's rare that you'll just have one container covering the entire page.</p>
<pre><code class="language-html">&lt;html&gt;
&lt;body&gt;
	&lt;div class=&quot;container&quot;&gt;
		// This rarely happens.
	&lt;/div&gt;
&lt;/body&gt;
&lt;html&gt;
</code></pre>
<pre><code class="language-html">&lt;html&gt;
&lt;body&gt;
	&lt;nav class=&quot;navbar&quot;&gt;
		&lt;div class=&quot;container&quot;&gt;
		&lt;/div&gt;
	&lt;/nav&gt;
	&lt;div id=&quot;main&quot;&gt;
		&lt;div class=&quot;container&quot;&gt;
			// Main content goes here.
			// The typical Bootstrap layout has a few containers.
		&lt;/div&gt;
	&lt;/div&gt;
	&lt;div id=&quot;footer&quot;&gt;
		&lt;div class=&quot;container&quot;&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>The second wrapper is a much shorter discussion. When working with the grid system, you need to place your grid within a <code>.row</code> div. The Bootstrap documentation dedicates all of 6 words to this vital point. <em>Place grid columns in any <code>.row</code>.</em></p>
<h2 id="thegrid">The Grid</h2>
<p>Alright, with wrappers out of the way, we can move onto the grid itself. The Bootstrap grid system has 12 column slots that are typically displayed side-by-side. It uses CSS media queries to stack these columns on top of each other when your browser window gets small enough to pass a certain breakpoint.</p>
<pre><code class="language-html">&lt;html&gt;
&lt;body&gt;
	&lt;div class=&quot;container&quot;&gt;
		&lt;div class=&quot;row&quot;&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Large Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-44-26-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Small Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-45-00-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>You can set which breakpoint you'd like the columns to start stacking at by changing the variable in the middle of the column. <code>.col-lg-1</code> is shown side by side with the other columns when the browser is 1170px or larger, <code>.col-md-1</code> shows side by side with other columns when the page is 970px or larger, <code>.col-sm-1</code> shows side by side with other columns when the page is 750px or larger, and <code>.col-xs-1</code> is <em>always</em> shown as a side-by-side column. Columns using <code>.col-xs-1</code> will never be stacked, no matter how small the browser window gets.</p>
<p>Of course, page layouts are almost never twelve individual columns of content. Most page layouts take advantage of about two or three columns. To create a layout with only a few columns, we'll need to adjust the size variable at the end of our column class. Instead of using 12 <code>.col-md-1</code> columns, we'll use just two columns: <code>.col-md-8</code> and <code>.col-md-4</code>. The number at the end represents how many columns wide a div is. For most layouts, you'll want all of your column sizes to add up to 12.</p>
<pre><code class="language-html">&lt;html&gt;
&lt;body&gt;
	&lt;div class=&quot;container&quot;&gt;
		&lt;div class=&quot;row&quot;&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-8&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-4&lt;/div&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Large Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-53-32-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Small Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-53-45-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Columns can have different sizes at different breakpoints. Take for instance the <code>.col-md-8</code>, <code>.col-md-4</code> layout shown above. If you wanted the <code>.col-md-4</code> sidebar to render narrower on a smaller screen, you should just add the sm class after the md class. <code>.col-md-8 .col-sm-9</code> and <code>.col-md-4 .col-sm-3</code> would feature a thick sidebar at lg and md breakpoints, a narrower sidebar at the sm breakpoint, and would stack at the xs level. Just remember that you still would generally want your columns to add up to 12 at whatever size they're at.</p>
<h2 id="columnwrapping">Column Wrapping</h2>
<p>There is one exception to the add-up-to-12 rule, which the &quot;Column Wrapping&quot; section of the Bootstrap docs tries to explain. If you've got a <code>.row</code> that is more than 12 columns wide, the overflow columns will be wrapped around to the next line. A trio of <code>.col-md-6</code> columns would render as two boxes side by side, with a third box underneath the first. This technique is useful for when you're rendering <code>n</code> items from a database, and don't know how many items are going to be retrieved. You'll frequently see it used in galleries and similar layouts. The overflowed columns will still stack on on small screens as before.</p>
<pre><code class="language-html">&lt;html&gt;
&lt;body&gt;
	&lt;div class=&quot;container&quot;&gt;
		&lt;div class=&quot;row&quot;&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-6&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-6&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-6&lt;/div&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Large Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-10-56-17-AM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Small Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-10-56-32-AM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<h2 id="offsetcolumns">Offset Columns</h2>
<p>Offset columns are fairly straightforward. They create a gap in your column layout. <code>.col-md-4</code> and <code>.col-md-4</code> would usually create two blocks that are four columns wide, with a four column wide space after the two blocks. If you instead have a <code>.col-md-4</code> div and a <code>.col-md-4 .col-md-offset-4</code> div, the second block will be pushed over 4 spaces. The page will render with a block 4 columns wide, a space 4 columns wide, and then a block 4 columns wide. The xs/sm/md/lg sizing in the col-*-offset-4 div refers to what screen size the offset should happen on. The divs <code>.col-sm-6</code> <code>.col-md-4</code> and <code>.col-sm-6 .col-md-4 .col-offset-4</code> will be stacked on xs screens, side by side on sm screens, and separated by a 4 column gap on screens md and above. I don't really use this feature much, but it can be useful for certain layouts.</p>
<p>Large Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-46-01-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Small Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-46-47-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Large Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-47-33-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Small Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-47-52-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<h2 id="nestingcolumns">Nesting Columns</h2>
<p>Nesting columns is one of the tricky mental leaps to make with grid systems. It's often not strictly necessary, but can help logically separate your page better. For instance, if your page layout has a main content area and a sidebar, you'd probably use a <code>.col-md-8</code> and <code>.col-md-4</code> div. If for whatever reason you needed to break your main content area up into another pair of equal divs, you generally <em>would not</em> want to change the page layout to be a <code>.col-md-4</code> <code>.col-md-4</code> <code>.col-md-4</code> grid. Instead, you would want to keep the <code>.col-md-8</code> <code>.col-md-4</code> layout, and nest a new grid inside of the main col-md-8 content area. The nested grid will need its own <code>.row</code> element, but it doesn't need another <code>.container</code> wrapper.</p>
<pre><code class="language-html">&lt;html&gt;
&lt;body&gt;
&lt;div class=&quot;container&quot;&gt;
	&lt;div class=&quot;row&quot;&gt;
		&lt;div class=&quot;col-md-8&quot;&gt;
			&lt;p&gt;.col-md-8&lt;/p&gt;
			&lt;div class=&quot;row&quot;&gt;
				&lt;div class=&quot;col-md-6&quot;&gt;
					.col-md-6
				&lt;/div&gt;
				&lt;div class=&quot;col-md-6&quot;&gt;
					.col-md-6
				&lt;/div&gt;
			&lt;/div&gt;
		&lt;/div&gt;
		&lt;div class=&quot;col-md-4&quot;&gt;
			.col-md-4
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Large Screen:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-50-31-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Small Screen:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-50-57-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>The new nested divs still needs to add up to 12, not the width of the parent column. After all, you're creating a whole new set of columns. So inside of your <code>.col-md-8</code> column's <code>.row</code> wrapper, you would want to put a set of <code>.col-md-6</code> <code>.col-md-6</code> divs. If you wanted to make the main content area temporarily have three columns, you'd instead use <code>.col-md-4</code> <code>.col-md-4</code> <code>.col-md-4</code>, etc.</p>
<h2 id="columnordering">Column Ordering</h2>
<p>Finally there's column ordering. When using Bootstrap's grid system, your columns will be displayed in the order they're declared. When unstacked, they'll be shown in order from left to right, and then they're stacked they'll be shown top to bottom. The only way to change the stacking order is to change the order in which your columns are declared in your HTML code.</p>
<p>In the unstacked, side-by-side view however, it is possible to change the order of the columns. Using the <code>.col-md-8</code> <code>.col-md-4</code> example from before, let's say that you wanted the <code>col-md-8</code> column to stack on top of the <code>.col-md-4</code> column, but you wanted the <code>.col-md-4</code> column to render on the left of the <code>.col-md-8</code> column when on larger pages.</p>
<p>To do this, you would need to use the <code>.col-md-pull-* </code> and <code>.col-md-push-* </code> helper classes. By changing your columns to <code>.col-md-8 .col-md-push-4</code> and <code>.col-md-4 .col-md-pull-8</code>, the larger column would be pushed out an extra 4 column lengths, and the smaller column would be pulled in 8 column lengths. The end result would be that the columns would switch places when viewed at md breakpoints and above.</p>
<pre><code class="language-html">&lt;div class=&quot;row&quot;&gt;
	&lt;div class=&quot;col-md-8 col-md-push-4&quot;&gt;.col-md-8 .col-md-push-4&lt;/div&gt;
	&lt;div class=&quot;col-md-4 col-md-pull-8&quot;&gt;.col-md-4 .col-md-pull-8&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>Large Screen:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-53-32-PM-1.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Small Screen:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-53-45-PM-1.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Large Screen:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-53-02-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Small Screen:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-52-49-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<h2 id="conclusion">Conclusion</h2>
<p>I hope these explanations make sense. It can take a while to get used to combining all of these different techniques together. Make sure to check out the Bootstrap documentation to see some of the more advanced options I didn't have a chance to go over here.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Tumblr Theme: Wallace, A Bootstrap Theme]]></title><description><![CDATA[A Bootstrap-based Tumblr theme template.]]></description><link>https://stuart-jones.com/blog/tumblr-theme-wallace-a-bootstrap-theme/</link><guid isPermaLink="false">5e913bf4017a114be0c96054</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Wed, 16 Dec 2015 06:12:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/ScreenShot-3-1.png" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Wallace.png" class="kg-image" alt="Tumblr Theme: Wallace, A Bootstrap Theme"></figure><!--kg-card-begin: markdown--><img src="https://stuart-jones.com/content/images/2020/04/ScreenShot-3-1.png" alt="Tumblr Theme: Wallace, A Bootstrap Theme"><p>I recently looked into what it takes to theme a Tumblr site. While there were a few existing Bootstrap-based Tumblr themes, none of them were that straightforward to use or customize. I decided to throw this theme together for anyone else who happens to be looking for a Bootstrap-based Tumblr theme.</p>
<h2 id="tldr">tl;dr</h2>
<p>Take the index.html file in the /dist folder and upload it to the &quot;Edit Theme&quot; section of Tumblr. That’s it. You’re done.</p>
<h2 id="thehardway">The Hard Way</h2>
<p>Wallace provides two methods for styling the theme. Option 1 is the simple method described above. You fetch the Bootstrap.min.css file from a CDN, and the theme’s header contains the few Tumblr specific stylings required to make the page render correctly.</p>
<p>Option 2 is a bit more tricky. Due to the popularity of Bootstrap, sites powered by the framework have all started to look the same. To combat this, Bootstrap offers several different methods of theming the framework. The most simplistic method uses <a href="http://getbootstrap.com/customize/">getbootstrap.com/customize/</a>, while the slightly more advanced methods rely on the LESS and Sass CSS preprocessors. CSS Preprocessors act as a superset of CSS, allowing you to take advantage of more advanced features such as variables, inheritance, and mixins.</p>
<p>Wallace is designed to take advantage of <a href="http://sass-lang.com">Sass</a>. It uses <a href="https://nodejs.org/">NodeJS’s</a> <a href="https://github.com/sass/libsass">LibSass</a> to compile Sass files, aided by the <a href="http://gruntjs.com">Grunt</a> task runner and the <a href="http://bower.io">Bower</a> package manager. If you’d like to theme Wallace, you’ll need a system with Node, Grunt, and Bower installed. Node’s <a href="https://nodejs.org">website</a> has everything you need to install the scripting language. Once that’s done, you’ll want to install Grunt and Bower globally on your development system.</p>
<pre><code>npm install grunt-cli -g
npm install bower -g
</code></pre>
<p>Now that you’v gotten that taken care of, you can let Wallace’s install script’s do it’s thing. Execute the NPM package.json file, then Bower’s bower.son file.</p>
<pre><code>npm install
bower install
</code></pre>
<p>Finally, all you need to do is run the Gruntfile.js. Grunt will take the ./sass/styles.scss file and compile it down into the ./css/styles.css file.</p>
<p><code>grunt</code></p>
<p>styles.<em>scss</em> already has a @import command for the _bootstrap.scss file that Bower fetched. styles.<em>css</em> is the only stylesheet your Tumblr theme will need. Go to the “Edit Appearance” section of Tumblr, then click “Edit Theme”. Click “Edit HTML”, then copy and paste Wallace’s Index.html file into the window. Click the gear in the top left corner and then click “Theme Assets”. Upload your compiled style.css file here. Find the section on line 164 that reads “[REPLACE WITH TUMBLR UPLOADED CSS FILE]”. Highlight this text, then slick the “Insert” link next to the styles.css file you uploaded. Click “Update Preview”, and then “Save” at the top of the coding pane. Wallace should now be successfully applied to your Tumblr blog.</p>
<h2 id="license">License</h2>
<p>Wallace is licensed under The MIT License (MIT). It is based in part on the excellent <a href="http://buildthemes.tumblr.com">Build Themes</a> ebook by Rohan Chandra.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Ghost Theme: Ninamori]]></title><description><![CDATA[A simple and clean theme for Ghost using Twitter Bootstrap 3, Font Awesome Icons, SASS, Bower, and Grunt.]]></description><link>https://stuart-jones.com/blog/ghost-theme-ninamori/</link><guid isPermaLink="false">5e913bf4017a114be0c96052</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Sat, 09 May 2015 22:44:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/Home-Page-2.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://stuart-jones.com/content/images/2020/04/Home-Page-2.png" alt="Ghost Theme: Ninamori"><p>In my <a href="https://stuart-jones.com/howdy-world/">introductory blog</a> post, I explained the reasoning behind me jumping ship to the Ghost blogging platform. I always strive to give my sites the personal touch, so went and made my own Ghost theme to go along with my Stuart-Jones.com blog. If you're taking a peek under the hood, feel free to go look over at my <a href="https://github.com/imstuartjones/ninamori">Github entry</a> for:</p>
<h1 id="ninamori">Ninamori</h1>
<p>A simple and clean theme for Ghost using Twitter Bootstrap 3, Font Awesome Icons, SASS, Bower, and Grunt.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Home-Page-3.png" class="kg-image" alt="Ghost Theme: Ninamori"></figure><!--kg-card-begin: markdown--><h2 id="features">Features</h2>
<p>Ninamori was designed and tested for version 0.6 of the Ghost blogging platform. It supports the standard theme features of Ghost up until that point, including:</p>
<ul>
<li>Blog Image</li>
<li>Blog Cover</li>
<li>Customizable Navbar</li>
<li>Tags</li>
</ul>
<p>Ninamori was designed as a single user portfolio site theme, so it does not show post authors.</p>
<p>In addition to these standard features, Ninamori also includes:</p>
<ul>
<li>Auto-hiding navbar with scroll-up reveal</li>
<li>Tag specific cover images</li>
<li>Link-Blog Support</li>
</ul>
<h2 id="demo">Demo</h2>
<p>Ninamori powers my site! Check it out at /</p>
<h2 id="gettingstarted">Getting Started</h2>
<p>Ninamori uses <a href="http://bower.io/">Bower</a> for dependency management, and <a href="http://gruntjs.com/">Grunt</a> as the task runner. Both of these tools require Node and npm, which should already be installed on a system running Ghost. Instructions on how to use Bower and Grunt can be found on their respective websites. To get started using the theme, clone it to your Ghost's content/themes/ folder.</p>
<pre><code>git clone https://github.com/ImStuartJones/ninamori.git
</code></pre>
<p>Use NPM to install the required development packages.</p>
<pre><code>npm install
</code></pre>
<p>Next, run Bower to download the necessary web frameworks. (Bootstrap, Jquery, etc.)</p>
<pre><code>bower install
</code></pre>
<p>Bower should run and install the necessary packages into assets/bower_components/. Next we need to run Grunt to compile Ninamori's .sass and .js files.</p>
<pre><code>grunt
</code></pre>
<p>The gruntfile is also configured to be run in watch mode with the following task.</p>
<pre><code>grunt watch
</code></pre>
<p>Finally, Ninamori's gruntfile also supports BrowserSync. BrowserSync is a live browser reload package which streamlines web development and device testing. More information can be found at the <a href="https://stuart-jones.com/blog/ghost-theme-ninamori/www.browsersync.io">BrowserSync</a> website.</p>
<pre><code>grunt browsersync
</code></pre>
<p>Your Ninamori instance should be ready for Ghost to use at this point. Login to your Ghost instance and change the theme on the main page of the Settings screen.</p>
<h2 id="changingthetagcoverimages">Changing the Tag Cover Images</h2>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Post-View.png" class="kg-image" alt="Ghost Theme: Ninamori"></figure><!--kg-card-begin: markdown--><p>By default, Ninamori comes with a variety of cover images for posts based on their tag. The image files can be found in assets/images/. New tags can be defined in the partials/post_header.hbs file. It uses a simple Handlebar has/else waterfall, so a post with multile tags will use the first one referenced.</p>
<pre><code class="language-html">{{! Post Header has custom images based off of tags.}}
{{#post}}
{{#has tag=&quot;tech&quot;}}
&lt;div class=&quot;image-header-post&quot; style=&quot;background-image: url('{{asset 'images/post-bg-tech.jpg'}}');&quot;&gt;
{{else}}
	{{#has tag=&quot;board-games&quot;}}
		&lt;div class=&quot;image-header-post&quot; style=&quot;background-image: url('{{asset 'images/post-bg-board.jpg'}}');&quot;&gt;
	{{else}}
		{{#has tag=&quot;video-games&quot;}}
			&lt;div class=&quot;image-header-post&quot; style=&quot;background-image: url('{{asset 'images/post-bg-video.jpg'}}');&quot;&gt;
		{{else}}
			{{#has tag=&quot;lgbt&quot;}}
				&lt;div class=&quot;image-header-post&quot; style=&quot;background-image: url('{{asset 'images/post-bg-lgbt.jpg'}}');&quot;&gt;
			{{else}}
				{{#has tag=&quot;news&quot;}}
					&lt;div class=&quot;image-header-post&quot; style=&quot;background-image: url('{{asset 'images/post-bg-news.jpg'}}');&quot;&gt;
				{{else}}
					&lt;div class=&quot;image-header-post&quot; style=&quot;background-image: url('{{asset 'images/post-bg.jpg'}}');&quot;&gt;
				{{/has}}
			{{/has}}
		{{/has}}
	{{/has}}
{{/has}}
{{/post}}
</code></pre>
<h2 id="licensing">Licensing</h2>
<p>Ninamori and its included images are covered under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International license.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>