Projects
  • sync-and-swim
    Jan 2025

    github.com/bgschiller/sync-and-swim

    A Tauri app to load music and audiobooks onto Shokz OpenSwim headphones. It offers three functions:

    • Load music from a directory onto the headphones. Copying via Finder or Explorer tends to screw up the order of files (because the headphones sort by file timestamp instead of alphabetically). This tool copies files in the correct order.
    • Slice long audio files, like podcasts or audiobooks, into smaller pieces. This makes it easier to skip forward or backward on a device with no screen and only three buttons.
    • Find your place in a directory of audio files. Rather than starting from the beginning of the book if you lose your spot (like if the headphones accidentally turn on in your bag), this app uses binary search to find the last file you listened to.
  • vitest-unsettled
    Apr 2024

    github.com/bgschiller/vitest-matchers/tree/main/packages/unsettled

    Custom Vitest matcher for checking if a Promise is settled (either resolved or rejected).

    If you expect a promise to be settled, you probably assert on its value: expect(p).resolves.toBe(value). But what if you want to assert that a promise is not settled? You can’t do that with the built-in matchers.

    let resolve = null;
    const p = new Promise((r) => {
      resolve = r;
    });
    await expect(p).toBeUnsettled(); // passes
    resolve(12);
    await expect(p).not.toBeUnsettled(); // passes
    
  • cpp-subprocess
    Nov 2021

    github.com/bgschiller/cpp-subprocess

    A C++ library for safely (no exceptions!) invoking external commands and pipelines. 5x better than popen.h or your money back.

    It’s not quite there yet, but the goal is to be able to write something like this:

    auto res = (
        Exec::shell("cat api/src/*.avdl") |
        Exec::cmd("grep").arg("-oP").arg(R"(@since\((\d+)\))") |
        Exec::cmd("tr").arg("-cd").arg("0-9\\n") |
        Exec::cmd("sort").arg("-rn") |
        Exec::cmd("head").arg("-n1")
    ).capture();
    

    The killer app here is that you can avoid shell injection attacks because arguments are passed as separate strings, not as a single string that gets parsed by the shell. This is a common problem with system and popen in C++.

    auto res = Redirection::Exec::cmd("jq").arg(jqFilter).stdin(body).capture();
    

    This is all a shameless rip-off of Rust’s subprocess crate.

  • backsplash
    May 2020

    github.com/bgschiller/backsplash

    Conversions between lat-lng, web mercator tiles, and pixel coordinates.

    Useful to figure out which tiles you need to grab from a Tile Map Service depending on the lat-lng bounding box you’re interested in and the zoom level.

  • superdelegate
    May 2019

    github.com/bgschiller/superdelegate

    Python port of Ruby ActiveSupport’s Model#delegate. Delegate methods and properties to child objects in a terse, explicit style.

  • alfred-clipbox
    Feb 2019

    github.com/bgschiller/alfred-clipbox

    An Alfred workflow to save a screenshot to Trello and put a shareable link on your clipboard. Also supports screen recordings, text, and any file.

  • inspect.macro
    Feb 2019

    github.com/bgschiller/inspect.macro

    Babel plugin macro for loggin an expression and the result of that expression. Transforms

    import inspect from "inspect.macro";
    
    inspect(complicatedExpression(involving.many(parts * and * values)));
    

    into

    console.log(
      "complicatedExpression(involving.many(parts * and * values)) →",
      (function () {
        try {
          return complicatedExpression(involving.many(parts * and * values));
        } catch (e) {
          return "an error occurred: " + e;
        }
      })(),
    );
    
  • vue-await
    Nov 2018

    github.com/bgschiller/vue-await

    Render blocks based on the status of a Promise. Demo at brianschiller.com/vue-await/.

    <template>
      <Await :p="prom">
        <p>Waiting...</p>
        <p slot="then" slot-scope="[result]">Success: {{ result }}</p>
        <p slot="catch" slot-scope="[error]">Error: {{ error }}</p>
        <p slot="none">(promise is null)</p>
      </Await>
    </template>
    
    <script>
    export default {
      data() {
        return {
          prom: fetch("http://thecatapi.com/api/images/get"),
        };
      },
    };
    </script>
    
  • citrus
    Jun 2018

    github.com/bgschiller/citrus

    A more convenient interface for doing Binary Linear Programming with PuLP. This package uses Python operator overloading to allow you to write

    model.addConstraint(x & y == 1, "Both x and y must be true")
    

    instead of

    x_and_y = pulp.LpVariable("x_and_y", cat=pulp.Binary)
    model.addConstraint(x_and_y == 1, "Both x and y must be true")
    model.addConstraint(x_and_y <= x, "supporting constraint 1")
    model.addConstraint(x_and_y <= y, "supporting constraint 2")
    

    And similar for |, negate, implies, maximum and minimum.

  • floorspace.js
    Feb 2018

    github.com/NREL/floorspace.js
  • winnow
    May 2017

    github.com/bgschiller/winnow
  • postgres-kernel
    Jul 2015

    github.com/bgschiller/postgres_kernel

    Interact with your PostgreSQL db via a jupyter notebook.

    A screenshot of a Jupyter notebook with a SQL query and the results displayed in a table.

  • latex-therefore
    Oct 2013

    github.com/bgschiller/latex-therefore

    a LaTeX package to avoid the tedium of coming up with synonyms for ‘Therefore’ in your mathematical writing.

    A section of a proof laid out using LaTeX. Let a, b be elements of the integers and gcd(a,b) = 1. Wherefore said He unto them, we can square both sides to get 2 = a^2 / b^2. Multiplying through by b^2, we see that a^2 = 2b^2. Therefore, a^2 must be an even number. If a were odd, its square would be odd. And veriy it goes that a must be even: a = 2k for k in the integers. (it continues but is cut off)