Project: Super Simple ChatUI

I’ve been playing around a lot with Ollama, an open source project that allows one to run LLMs locally on their machine. It’s been fun to mess around with. Some benefits: no rate-limits, private (e.g., trying to create a pseudo therapy bot, trying to simulate a foul mouthed smarmy sailor, or trying to generate ridiculous fake news articles about a Florida Man losing a fight to a wheel of cheese), and access to all sorts of models that get released.

I decided to try my hand at creating a simplified interface for interacting with it. The result: Super Simple ChatUI.

As if I need more side projects. So it goes!

TIL: List git branches by recent activity

In both my work and personal coding projects, I generally have a number of various branches going at once. Switching between various branches (or remembering past things I was working on) can somethings be a chore. Especially if I’m not diligent about deleting branches that have already been merged.

Usually, I do something like:

> git branch

Then, I get a ridiculously huge list of branches that I’ve forgotten to prune and spend all sorts of time trying to remember what I was most recently working on.

daves/XXXX-123/enable-clickstream
daves/XXXX-123/impression-events
daves/XXXX-123/tracking-fixes
daves/XXXX-123/broken-hdps
daves/XXXX-123/fix-contacts
daves/XXXX-123/listing-provider
daves/XXXX-123/revert-listing-wrapper-classname
daves/XXXX-123/typescript-models
daves/XXXX-123/inline-contact-form
daves/XXXX-123/clickstream_application_event
daves/XXXX-123/unused-file
daves/XXXX-123/convert-typescript
daves/XXXX-123/convert-typescript-v2
daves/XXXX-123/similar-impressions
daves/XXXX-123/update-node-version

At least 75% of those have already been merged and should have been pruned.

There has to be a better way, right?

Thanks to the power of the Google machine (and Stack Overflow), I found out, there is!

> git branch --sort=-committerdate

Hot diggity dog!

daves/XXXX-123/clickstream-filter-events
main
daves/XXXX-123/convert-typescript-v2
daves/XXXX-123/update-node-version
daves/XXXX-123/similar-impressions
daves/XXXX-123/convert-typescript
daves/XXXX-123/clickstream_application_event
daves/XXXX-123/unused-file
daves/XXXX-123/typescript-models
daves/XXXX-123/listing-provider
daves/XXXX-123/inline-contact-form
daves/XXXX-123/revert-listing-wrapper-classname

That list is now sorted by most recent activity on the branch.

Alright. Even though this is better, that’s still a lot of typing to remember. Fortunately, we can create an alias:

> git config --global alias.recent "branch --sort=-committerdate"

Now all I need to do is just type git recent and it works!

Nice.

TIL: How to change your default editor for git commits

A recent post on Hacker News highlighted the benefits of detailed commit messages in git.

Usually, my git commits look something like this:

> git commit -m "fix: component missing configuration file"

…which isn’t all that helpful. (Related: see XKCD on git commit messages)

I decided to try and utilize this newfound knowledge in my own git commits and I quickly ran into an obstacle. Simply using > git commit opens up vim. Which, I really don’t want to use. (I’m sorry!)

This is something I should already know how to do, but I had to do a Google search to learn more. It turns out, you can change the default editor in git. This makes it much more convenient! How do you do it?

git config --global core.editor "nano"

Replace “nano” with your preferred editor of choice. Now, running > git commit opens up your editor and you can make detailed commit messages to your heart’s content!

Upgrading Mr. RossBot’s image model and prompt template

My Mastodon landscape painting bot, Mr. RossBot keeps kicking along, generating some fun landscape art. It’s been powered by the AI Horde (the open source project behind ArtBot) and has tried to utilize whatever image models provided by the API to the best of its abilities.

For the most part, the code behind it is a bunch of spaghetti that looks like this:

An update to the AI Horde late last year added support for SDXL. However, the SDXL model on the Horde did not use a refiner. Because of this, images tended to come out a bit soft and lacked texture.

You can see examples of this in my announcement post about Mr. RossBot being back, here. See also:

More recently, the Horde added support for a new image model: AlbedoBaseXL. It’s an SDXL model that has a refiner baked in. Now images will come out a lot sharper looking.

Coincidentally, I was also playing around with various prompts and discovered I could get much better image results that look more painterly (rather than simple digital renderings) by utilizing the following prompt:

A beautiful oil painting of [LITERALLY_ANYTHING], with thick messy brush strokes.

And that is it! No more messy appending various junk to the end of the prompt to attempt to get what I want. The results speak for themselves and are pretty awesome, I think!

TIL: Local overrides in Chrome

I’ve been doing web development professionally for about 10 years now and just discovered something new. (I love it when this happens!)

Today, I learned about local overrides in Chrome. Local overrides are a powerful feature within Chrome’s Developer Tools that allow developers to make temporary changes to a web page’s files (CSS, JavaScript, images, etc.) directly within the browser.

These changes are saved to your local filesystem, allowing you to experiment with modifications without affecting the live website. This is especially useful for testing, debugging, and experimenting with different designs or functionalities.

Here’s how you can use local overrides in Chrome:

  1. Open Chrome Developer Tools:
    – Right-click on any webpage and select “Inspect” or press `Ctrl+Shift+I` (Windows/Linux) or `Cmd+Opt+I` (Mac).
  2. Enable Local Overrides:
    – Go to the “Sources” tab.
    – In the navigation pane, click on the “Overrides” tab (you may need to click on the “>>” to see it).
    – Click on “Select folder for overrides” and choose a folder on your local system. This is where your changes will be saved.
    – Allow Chrome to access the folder if prompted.
  3. Start Editing:
    – Find the file you want to edit in the page file navigator pane. You can navigate through the website’s file structure or find the file in the “Network” tab.
    – Right click on a file and select “Override content”
    – Once you open a file, you can modify it directly in the editor pane. Your changes will be reflected in real-time on the webpage.
  4. Save Changes:
    – After editing, press `Ctrl+S` or `Cmd+S` to save your changes. These changes are saved to the selected local folder and will override the network resource until you disable overrides or delete the local file.
  5. Disable Overrides:
    – To stop using local overrides, simply uncheck the “Enable Local Overrides” option in the Overrides tab.

Local overrides are a temporary way to experiment with web page modifications. They don’t affect the actual files on the web server, so other users won’t see these changes. This feature is highly useful for developers and designers to test changes without deploying them to a live server.

Interesting uses of a Steam Deck

My Steam Deck has to be one of my favorite gadgets in the last few years. Gaming aside, the fact that it’s running Linux opens up all sorts of interesting possibilities.

For example, let’s use it to add a new feature to ArtBot… while I’m on an airplane. The screen is tiny, but oh man, it actually worked.

ArtBot written up in PC World!

Hah! This is pretty awesome. My nifty side project, ArtBot, has been written up in PC World as part of a larger article about Stable Horde (the open source backend that powers my web app):

Stable Horde has a few front-end interfaces to use to create AI art, but my preferred choice is ArtBot, which taps into the Horde. (There’s also a separate client interface, with either a Web version or downloadable software.)

Interestingly enough, ArtBot just passed 2,000,000 images generated!

Creating an automated Twitter bot about gun violence

The school shooting in Uvalde last week was horrible. As a parent, I feel so powerless to protect my kids from something like that. Taking them to school the next day was extremely emotional.

It’s clear that we, as a country, are going to continue to do nothing about guns and gun violence. I channeled some of my emotion into building an automated bot for Twitter. I call it SABSStochastic Analysis for Ballistics Superfans (alternative title is “Second Amendment Bullshit”).

If you’re so technically inclined, you can download and run it yourself. Powered by Node and a fun little experiment into Twitter’s API.

It automatically replies to any congressional member who tweets.

Which of course includes unhinged Republicans.

Fixing rendering issues with React and IE11

Happy April Fools’ Day. This post is no laughing matter because it deals with IE11. 🙀

Awhile back, we had an issue where visitors to our site would hit our landing page and not see anything if they were using Internet Explorer 11. The skeleton layout would appear on the screen and that was it.

Debugging the issue using IE11 on a Windows box proved to be difficult. Normally, we open up the inspector window, look for an error stack and start working backwards to see what is causing the issue.

However, the moment I opened the inspector, the site would start working normally again. This lead me down a rabbit hole and I eventually found a relevant post on StackOverflow: “Why does my site behave differently when developer tools are open in IE11?”

The suggested solution was to implement a polyfill for console.log, like so:

if (!window.console || Object.keys(window.console).length === 0) {
  window.console = {
    log: function() {},
    info: function() {},
    error: function() {},
    warn: function() {}
  };
}

Interestingly, we didn’t have console.log statements anywhere in our production build, so I figured it must be from some third party library that we were importing. We added this line of code at the top of our web app’s entry point to try and catch any instances of this. For us, that was located at the following path: src/app/client/index.js

After rebuilding the app, things were still broken, so the investigation continued.

We eventually concluded that the issue had to do with how our app was built. Our web app is server side rendered and we use Babel and Webpack to transpile and bundle things up. It turns out, Babel wasn’t transpiling code that was included in third party libraries, so any library that was using console.log for one reason or another would cause our site to break.

(The fact that IE11 treats console.log statements differently when the inspector is open vs. not is an entirely separate issue and is frankly ridiculous.)

Knowing this, we were eventually able to come up with a fix. We added the polyfill I posted above directly into the HTML template we use to generate our app as one of the first things posted in the head block. The patches console.log so that it’s available in any subsequent scripts (both external or not) that use it.

<!doctype html>
  <html>
    <head lang="en">
      <meta content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0" name="viewport"/>
      <meta charSet="UTF-8"/>
      <script>
        if (!window.console || Object.keys(window.console).length === 0) {
          window.console = {
            error: function() {},
            log: function() {},
            warn: function() {},
            info: function() {},
            debug: function() {},
          };
        }
      </script>

After that, everything started working again in IE11.

#TheMoreYouKnow

Baseball Twitter: Fun with the jQuery and Twitter’s API

screenshot_redsoxThis past weekend, I played around with jQuery, JavaScript, and Twitter’s API to create a web app that displays realtime search results for various baseball teams. I figure this is particularly important since pitchers and catchers report in about 10 days! 🙂

Anyway, the goal of this project was to rapidly build out a web app that could search and parse publicly available data (e.g., tweets!) for mentions of particular baseball teams while using various jQuery and JavaScript libraries. I could see this app having potentially wider uses beyond baseball such as returning the latest tweets from an event or hashtag. It also gave me a chance to use various libraries and frameworks as well. I’m pretty happy with how it turned out.

You can see it in action right here and the source code is available on Github.

 

Making maps: Using Mapbox to display 5,000 Foursquare check ins

Earlier this year, I joined a group on Meetup dedicated to creating, analyzing, and talking about digital maps. It’s organized by the fine folks at #Maptime, who have an amazing enthusiasm for all things map related. It sounded like something that’s right up my alley, considering all the time I spend thinking about maps.

Last night was project night. All sorts of folks were there, working on complicated issues such as transit, land use, green space, climate effects. It’s kind of inspiring!

I went to focus on some less lofty goals — I just wanted to get some experience playing with a JavaScript mapping library created by a company called Mapbox. My goal was to get the library up and running and then use it to display some geo-encoded data from a service I happen to use a lot: Foursquare.

It’s no secret that I really like Foursquare — mainly for keeping track of our adventures far and wide. I found a helpful PHP library for converting Foursquare (and other social networking) data into GeoJSON, which is needed by the JavaScript mapping libraries. After a couple of hours of hacking around, I got something up and running!

Screenshot 2015-02-04 20.06.29Pretty neat stuff! I have some ideas on how to improve this and some additional data that I want to show in the future.

 

Adventures in Learning to Code: Sudoku Edition (Part II)

In part I, I explained how I’m attempting to write a webapp that can solve simple Sudoku puzzles. Since then, I’ve made some progress. This series will track what I’ve been working on, learning, and any difficulties I’m encountering.

With a goal in mind, it was time to just dive in and go.

Step 1: Oh God, what is this even…

At first, it was kind of overwhelming. Where do I even start? How the heck am I going to even tackle any of this?

After learning the basics of Javascript loops, functions, arrays, and objects, thanks to Codecademy, I was mostly ready to go. What was my real first step? How the heck do I even manipulate data on a webpage, let alone collect and analyze it?

After a few tries, I had a simple project featuring a table (with cells individually labeled) up and running!

Screenshot 2015-01-27 17.23.03

After some more tinkering, I was able to get the “Generate Numbers!” button to actually randomly generate the numbers 1 through 9 and fill in the cells without repeating any numbers. Sweet!

Screenshot 2015-01-27 17.25.58

Hey, that’s kind of cool. Now we’re getting somewhere! I think I was ready to take it to the next level.

Step 2: Okay… I guess we need to make a fake Sudoku grid.

There’s probably simpler ways to go about doing this, but I ended up spending a lot of time figuring out how to build a Sudoku grid in the first place. Hey, I wanted this project to look somewhat presentable when I showed it off and (hopefully) finished it.

HTML tables were driving me crazy, so I just straight up stole this helpful thing after Googling for an answer. It looks pretty good. Pretty, pretty, pretty good.

Screenshot 2015-01-27 17.35.02

Wow, sir. That looks so good that I’d like to take two!”

Alright, one mission accomplished. Now onto another mission. How do I completely fill out the entire grid? I started with this, because I wanted to understand how to try to isolate certain sections of the Sudoku grid and see if I could create a series of non-duplicated random numbers.

Why did I start with randomly generating numbers? I initially thought I would just try to brute force solve some simple beginner’s level puzzles, but I later realized how ridiculous this would be. There’s a ridiculous number of possible permutations to try.

For now though, I decided to start filling out random numbers by row. This is what my initial code looked like.

[javascript]
function fillTable(row) {
for (var i = 1; i <= maxColumns; i++) {
var selectCell = row + i;
//console.log(selectCell);
document.getElementById(selectCell).innerHTML = fillCell(selectCell);
if (i == maxColumns && row != maxRow) {
i = 0;
row = nextChar(row);
} else if (i == maxColumns && row == maxRow) {
break;
}
}
}
[/javascript]

Did it work? Yes! I ended up with grid of random numbers filled out! Sure, there’s really no rhyme or reason for where numbers ended up, but it certainly looks like a completed Sudoku puzzle, right?

Screenshot 2015-01-26 17.20.12

Step 3: Let’s put some real numbers in there

Alright, entering a bunch of random numbers into a grid is pretty fun for all of 5 minutes. (To be honest, it was pretty fun) But to take this to the next level, I really needed to have the beginning of a Sudoku puzzle in there.

A few quick Google searches later, and I found a suitable candidate! One thing I decided early on was to store all this data in an object — mostly so I could easily update it with different puzzles later on. It ended up looking like this.

[javascript]
var allCells = {
a1: "", a2: 1, a3: "", a4: 6, a5: "", a6: 4, a7: 3, a8: "", a9: 7,
b1: 3, b2: 5, b3: 6, b4: "", b5: "", b6: "", b7: "", b8: "", b9: "",
c1: "", c2: "", c3: "", c4: "", c5: 5, c6: 3, c7: 6, c8: 9, c9: "",
d1: "", d2: 8, d3: 3, d4: 2, d5: 6, d6: "", d7: 4, d8: "", d9: 9,
e1: "", e2: "", e3: "", e4: "", e5: "", e6: "", e7: "", e8: "", e9: "",
f1: 4, f2: "", f3: 5, f4: "", f5: 7, f6: 8, f7: 2, f8: 6, f9: "",
g1: "", g2: "", g3: "", g4: "", g5: "", g6: "", g7: "", g8: "", g9: "",
h1: "", h2: "", h3: "", h4: "", h5: "", h6: "", h7: 7, h8: 2, h9: 4,
i1: 7, i2: "", i3: 9, i4: 4, i5: "", i6: 2, i7: "", i8: 8, i9: ""
};
[/javascript]

On top of this, I wanted to easily differentiate the initial numbers that were created on a brand new board (so I could easily see what my script was generating, versus what was already in place on the board). I wrote a function to modify the DOM and change the font weight and background color of the starting cells.

[javascript]
function setupBoard(row) {
var row = row;
var cellValue = 0;
for (i = 0; i < maxColumns; i++) {
//console.log("ROW: " + row + (i+1));
cellValue = allCells[row+(i+1)];
document.getElementById(row + (i+1)).innerHTML = allCells[row+(i+1)];

// Just highlighting what cells we initially started with.
if (Number(cellValue) > 0) {
document.getElementById(row + (i+1)).style.fontWeight = "bold";
document.getElementById(row + (i+1)).style.backgroundColor = "#F2F2F2";
} else {
// Use this to count up total number of empty cells that we need to solve for
// The idea is that we can use this to detect if we’re stuck
emptyCells++;
}
}
}
[/javascript]

You’ll notice I have a lot of console.log() calls commented out. I liberally used these all over the place so I could make sure things were working correctly. Anyway, once all that was said and done, it ended up generating a board that looked like this.

Screenshot 2015-01-27 07.38.36

Wow, we’re starting to chug along pretty nicely! Next time, I’ll talk about the million different functions I created in trying to solve this.

Adventures in Learning to Code: Sudoku Edition (Part I)

Screenshot 2015-01-27 21.00.15

This series of posts will track what I’ve been working on, new things I’ve been learning, and any difficulties I’m encountering along the way. Buckle up!

One of the things I resolved to do this year was spend a bit more time learning to code. Sure, sure, I’ve dabbled in things like PHP and have used MySQL to build some pretty basic websites and tools.

Even with the little I’ve done, I’ve always enjoyed it and have found myself losing track of time while trying to solve some problem or just get this certain thing to work. It reminds me of a similar thing I’ve encountered with the addictive nature of Sid Meier’s Civilization games.

“Just one more turn…”

Late last year, I mentioned to a coworker my desire to spend a bit more time learning various techniques and theory. He suggested that if I’m serious, I should really just start trying to tackle some problems and we could go over it together.

The first thing he suggested? I should build a application to solve a Sudoku puzzle.

What??!

As tends to happen, the end of the year got a bit hectic and I put this project on the backburner. When I originally considered it, I was going to try and write it in PHP. However, I recently saw this goofy map of popular programming languages by state and thought, “I’ve never really done anything with Javascript. Maybe I should try that.”

So, that’s what I’ve been doing the last few weeks. Between taking some awesome online courses through Codecademy and Udemy, as well as reading up on various books, I’ve been trying to really get into it.

How am I doing? I’ll try to share my progress in future updates.